Merge branch 'fix/sponsorship-funnel'

This commit is contained in:
Vinta Chen
2026-10-21 09:09:32 +08:00
10 changed files with 168 additions and 51 deletions
+1 -1
View File
@@ -162,7 +162,7 @@ The component vocabulary is small and table-led. Source of truth: `website/stati
- **Hero**. Magazine-cover headline, dark earthy ground, kicker and proof microcopy, primary CTA button using `--hero-btn-start` / `--hero-btn-end`. Subtle grid plus slow sheen. Respects `prefers-reduced-motion`.
- **Jump links**. One per use case in a category hero, plus one for the guide. Underlined like the intro's links, with a trailing ↓. Never tag-styled: tags filter the table or open another page, jump links only scroll.
- **Guide band**. Everything in a category intro after its "How to choose" list. It sits below the table on the hero's dark gradient, with its H2 above the text.
- **Sponsor band**. Sits below the hero on the home page, on `--bg-paper-strong`. Editorial layout, not a logo wall. Sponsor links share the global accent treatment.
- **Sponsor band**. Sits below the hero on the home page and category pages, on `--bg-paper-strong`. Editorial layout, not a logo wall. Sponsor links share the global accent treatment.
- **CTA**. Warm `--cta-bg`, full-bleed within shell. The button itself uses accent tokens.
- **Footer**. Dark warm charcoal, part of the same system. Footer links share the global hover and focus treatment.
- **Search**. Pill input with `--search-inset` interior and `--search-focus-ring` focus ring. Focus shadow uses `--search-focus-shadow`.
+2 -2
View File
@@ -6,9 +6,9 @@ An opinionated guide to the best Python frameworks, libraries, and tools.
## **Sponsors**
- [iPulse AI](https://ipulseai.com) - Open Agentic Investment Research Platform. Inspect deep multi-agent market forecasts and stock picks.
- [iPulse AI](https://ipulseai.com?utm_source=awesome-python&utm_medium=sponsorship&utm_campaign=sponsor&utm_content=readme) - Open Agentic Investment Research Platform. Inspect deep multi-agent market forecasts and stock picks.
> The **#10 most-starred repo on GitHub**. Put your product in front of Python developers. [Become a sponsor](SPONSORSHIP.md).
> **Sponsor slots open**, from $99/month. The **#10 most-starred repo on GitHub**. Put your product in front of Python developers. [Become a sponsor](https://awesome-python.com/sponsorship/).
## Categories
+10 -8
View File
@@ -21,17 +21,17 @@ Your sponsorship puts your product in front of developers at the exact moment th
## Sponsorship Tiers
### Headline Sponsor - $500/month
### Headline Sponsor - $500/month or $4,500/year
- Large logo and one-line description (max 120 characters) pinned at the very top of the README, above all project entries
- Logo link in the sponsor section of [awesome-python.com](https://awesome-python.com/)
### Featured Sponsor - $200/month
### Featured Sponsor - $200/month or $1,800/year
- Text entry (`[Name](URL) - Description.`, max 120 characters) pinned at the top of the README, directly below Headline sponsors
- Text link in the sponsor section of [awesome-python.com](https://awesome-python.com/)
- Small logo and text entry (`[Name](URL) - Description.`, max 120 characters) pinned at the top of the README, directly below Headline sponsors
- Logo and text link in the sponsor section of [awesome-python.com](https://awesome-python.com/)
### Indie Dev Sponsor - $99/month or $249/quarter
### Indie Dev Sponsor - $99/month, $249/quarter, or $891/year
- Lower-cost tier for indie developers, solo founders, and one-person companies only
- Text entry (`[Name](URL) - Description.`, max 120 characters) in the README sponsor section, directly below Featured sponsors
@@ -43,13 +43,15 @@ Indie Dev Sponsor is available only to founder-led (one-person company) products
Email [sponsorship@awesome-python.com](mailto:sponsorship@awesome-python.com?subject=Awesome%20Python%20Sponsorship) with:
- **Tier:** Headline Sponsor ($500/mo), Featured Sponsor ($200/mo), or Indie Dev Sponsor ($99/mo or $249/quarter)
- **Content:** Product name, URL, logo, and description (Headline tier) or `[Name](URL) - Description.` entry (Featured or Indie Dev tier)
- **Duration:** 1, 3, 6 months, or longer
- **Tier:** Headline Sponsor ($500/mo or $4,500/year), Featured Sponsor ($200/mo or $1,800/year), or Indie Dev Sponsor ($99/mo, $249/quarter, or $891/year)
- **Content:** Product name, URL, logo, and description (Headline tier), logo and `[Name](URL) - Description.` entry (Featured tier), or `[Name](URL) - Description.` entry (Indie Dev tier)
- **Duration:** 1, 3, or 6 months, or 1 year (3 months free)
- **Payment method:** card via [Open Collective](https://opencollective.com/awesome-python), US bank transfer (ACH/wire), or PayPal
One upfront payment per term. Setup takes less than 24 hours.
Sponsor links carry UTM tags, so clicks show up in your own analytics as `awesome-python / sponsorship`.
## Previously Sponsored By
- [Warp](https://www.warp.dev/) - The terminal for modern developers.
+14
View File
@@ -11,6 +11,7 @@ from collections.abc import Sequence
from datetime import UTC, datetime
from pathlib import Path
from typing import TypedDict
from urllib.parse import parse_qsl, urlencode, urlsplit, urlunsplit
from jinja2 import Environment, FileSystemLoader
from markdown_it import MarkdownIt
@@ -130,6 +131,16 @@ def extract_github_repo(url: str) -> str | None:
return m.group(1) if m else None
def sponsor_website_url(url: str) -> str:
"""Set utm_content=website so sponsors can tell website clicks from README clicks."""
parts = urlsplit(url)
pairs = parse_qsl(parts.query, keep_blank_values=True)
if not any(k == "utm_content" for k, _ in pairs):
return url
query = [(k, "website" if k == "utm_content" else v) for k, v in pairs]
return urlunsplit(parts._replace(query=urlencode(query)))
def load_stars(path: Path) -> dict[str, dict]:
"""Load star data from JSON. Returns empty dict if file doesn't exist or is corrupt."""
if path.exists():
@@ -749,6 +760,8 @@ def build(repo_root: Path) -> None:
parsed_groups = parse_readme(readme_text)
sponsors = parse_sponsors(readme_text)
for sponsor in sponsors:
sponsor["url"] = sponsor_website_url(sponsor["url"])
categories = [cat for g in parsed_groups for cat in g["categories"]]
cat_slugs = [cat["slug"] for cat in categories]
@@ -881,6 +894,7 @@ def build(repo_root: Path) -> None:
parent_category=parent_category,
group_categories=group_categories,
category_json_ld=category_json_ld,
sponsors=sponsors,
),
encoding="utf-8",
)
+7
View File
@@ -399,6 +399,13 @@ document.addEventListener("click", function (e) {
}
});
// Mailto clicks leave no page view, so the sponsor funnel needs its own event
document.addEventListener("click", function (e) {
const link = e.target.closest('a[href^="mailto:sponsorship@"]');
if (!link || typeof gtag !== "function") return;
gtag("event", "sponsor_contact", { tier: link.dataset.tier });
});
if (location.hash) {
const linkedRow = document.getElementById(decodeURIComponent(location.hash.slice(1)));
if (linkedRow && linkedRow.matches("tr.row")) openEntryRow(linkedRow);
+25
View File
@@ -0,0 +1,25 @@
{% if sponsors %}
<section class="sponsor-band" aria-labelledby="sponsor-heading" data-reveal>
<div class="section-shell sponsor-shell">
<header class="sponsor-meta">
<p class="section-label" id="sponsor-heading">Sponsors</p>
<a class="sponsor-become" href="/sponsorship/"> Become a sponsor </a>
</header>
<ul class="sponsor-list">
{% for sponsor in sponsors %}
<li class="sponsor">
<a
class="sponsor-link"
href="{{ sponsor.url }}"
target="_blank"
rel="noopener"
>
<span class="sponsor-name">{{ sponsor.name }}</span>
</a>
<p class="sponsor-desc">{{ sponsor.description | safe }}</p>
</li>
{% endfor %}
</ul>
</div>
</section>
{% endif %}
+2
View File
@@ -208,6 +208,8 @@
<td></td>
</tr>
{% endmacro %}
{% include "_sponsor_band.html" %}
<section class="results-section" id="library-index">
<div class="results-intro section-shell" data-reveal>
{% if entry_groups %}
+1 -25
View File
@@ -70,31 +70,7 @@
</header>
{% endblock %}
{% block content %}
{% if sponsors %}
<section class="sponsor-band" aria-labelledby="sponsor-heading" data-reveal>
<div class="section-shell sponsor-shell">
<header class="sponsor-meta">
<p class="section-label" id="sponsor-heading">Sponsors</p>
<a class="sponsor-become" href="/sponsorship/"> Become a sponsor </a>
</header>
<ul class="sponsor-list">
{% for sponsor in sponsors %}
<li class="sponsor">
<a
class="sponsor-link"
href="{{ sponsor.url }}"
target="_blank"
rel="noopener"
>
<span class="sponsor-name">{{ sponsor.name }}</span>
</a>
<p class="sponsor-desc">{{ sponsor.description | safe }}</p>
</li>
{% endfor %}
</ul>
</div>
</section>
{% endif %}
{% include "_sponsor_band.html" %}
<section class="results-section" id="library-index">
<div class="results-intro section-shell" data-reveal>
+26 -15
View File
@@ -32,7 +32,8 @@
<p class="category-subtitle">
The #10 most-starred repository on GitHub, and the list Python
developers check when choosing what to use. Your sponsorship puts your
product in front of them at the moment of decision.
product in front of them at the moment of decision. Headline $500,
Featured $200, Indie Dev from $99 a month.
</p>
{% if hero_stats %}
@@ -45,6 +46,7 @@
<div class="hero-actions">
<a
href="mailto:sponsorship@awesome-python.com?subject=Awesome%20Python%20Sponsorship"
data-tier="general"
class="hero-action hero-action-primary"
>Email sponsorship@awesome-python.com</a
>
@@ -114,7 +116,7 @@
<p class="tier-eyebrow">Headline Sponsor</p>
<p class="tier-price">
<span class="tier-amount">$500</span>
<span class="tier-cadence">/ month</span>
<span class="tier-cadence">/ month or $4,500 / year</span>
</p>
<p class="tier-summary">
Logo pinned at the top of the README. Logo on the website.
@@ -128,6 +130,7 @@
</ul>
<a
href="mailto:sponsorship@awesome-python.com?subject=Awesome%20Python%20Sponsorship%20-%20Headline%20Tier"
data-tier="headline"
class="tier-cta"
>Email about Headline tier</a
>
@@ -136,21 +139,25 @@
<p class="tier-eyebrow">Featured Sponsor</p>
<p class="tier-price">
<span class="tier-amount">$200</span>
<span class="tier-cadence">/ month</span>
<span class="tier-cadence">/ month or $1,800 / year</span>
</p>
<p class="tier-summary">
Text link pinned at the top of the README. Text link on the website.
Small logo and text link pinned at the top of the README. Logo and
text link on the website.
</p>
<ul class="tier-includes">
<li>
Text entry (<code>[Name](URL) - Description.</code>, max 120
characters) pinned at the top of the README, directly below
Headline sponsors.
Small logo and text entry
(<code>[Name](URL) - Description.</code>, max 120 characters)
pinned at the top of the README, directly below Headline sponsors.
</li>
<li>
Logo and text link in the sponsor section of awesome-python.com.
</li>
<li>Text link in the sponsor section of awesome-python.com.</li>
</ul>
<a
href="mailto:sponsorship@awesome-python.com?subject=Awesome%20Python%20Sponsorship%20-%20Featured%20Tier"
data-tier="featured"
class="tier-cta"
>Email about Featured tier</a
>
@@ -159,7 +166,7 @@
<p class="tier-eyebrow">Indie Dev Sponsor</p>
<p class="tier-price">
<span class="tier-amount">$99</span>
<span class="tier-cadence">/ month or $249 / quarter</span>
<span class="tier-cadence">/ month, $249 / quarter, or $891 / year</span>
</p>
<p class="tier-summary">
Lower-cost text link for eligible indie developers and solo
@@ -180,6 +187,7 @@
</ul>
<a
href="mailto:sponsorship@awesome-python.com?subject=Awesome%20Python%20Sponsorship%20-%20Indie%20Dev%20Tier"
data-tier="indie"
class="tier-cta"
>Email about Indie Dev tier</a
>
@@ -225,6 +233,7 @@
Email
<a
href="mailto:sponsorship@awesome-python.com?subject=Awesome%20Python%20Sponsorship"
data-tier="general"
>sponsorship@awesome-python.com</a
>
with the four items below.
@@ -233,21 +242,22 @@
<div>
<dt>Tier</dt>
<dd>
Headline Sponsor ($500/mo), Featured Sponsor ($200/mo), or Indie Dev
Sponsor ($99/mo or $249/quarter).
Headline Sponsor ($500/mo or $4,500/year), Featured Sponsor
($200/mo or $1,800/year), or Indie Dev Sponsor ($99/mo, $249/quarter,
or $891/year).
</dd>
</div>
<div>
<dt>Content</dt>
<dd>
Product name, URL, logo, and description (Headline tier), or
<code>[Name](URL) - Description.</code> entry (Featured or Indie Dev
tier).
Product name, URL, logo, and description (Headline tier), logo and
<code>[Name](URL) - Description.</code> entry (Featured tier), or
<code>[Name](URL) - Description.</code> entry (Indie Dev tier).
</dd>
</div>
<div>
<dt>Duration</dt>
<dd>1, 3, 6 months, or longer.</dd>
<dd>1, 3, or 6 months, or 1 year (3 months free).</dd>
</div>
<div>
<dt>Payment method</dt>
@@ -261,6 +271,7 @@
<div class="sponsorship-cta-row">
<a
href="mailto:sponsorship@awesome-python.com?subject=Awesome%20Python%20Sponsorship"
data-tier="general"
class="hero-action hero-action-primary"
>Email sponsorship@awesome-python.com</a
>
+80
View File
@@ -26,6 +26,7 @@ from build import (
load_pypi_badges,
load_stars,
sort_entries,
sponsor_website_url,
subcategory_path,
)
from readme_parser import parse_readme, slugify
@@ -458,6 +459,68 @@ class TestBuild:
assert llms_txt != readme
assert "# Contributing" not in llms_txt
def test_homepage_sponsor_link_tags_website_clicks(self, tmp_path):
readme = textwrap.dedent("""\
# Awesome Python
Intro.
## **Sponsors**
- [Sponsor](https://sponsor.example.com/?ref=x&utm_source=awesome-python&utm_content=readme) - Sponsored tool.
## Projects
**Tools**
## Widgets
- [w1](https://example.com) - A widget.
# Contributing
Help!
""")
(tmp_path / "README.md").write_text(readme, encoding="utf-8")
self._copy_real_templates(tmp_path)
build(tmp_path)
index_html = (tmp_path / "website" / "output" / "index.html").read_text(encoding="utf-8")
assert 'href="https://sponsor.example.com/?ref=x&amp;utm_source=awesome-python&amp;utm_content=website"' in index_html
assert "utm_content=readme" not in index_html
def test_category_page_shows_sponsor_band(self, tmp_path):
readme = textwrap.dedent("""\
# Awesome Python
Intro.
## **Sponsors**
- [Sponsor](https://sponsor.example.com) - Sponsored tool.
## Projects
**Tools**
## Widgets
- [w1](https://example.com) - A widget.
# Contributing
Help!
""")
(tmp_path / "README.md").write_text(readme, encoding="utf-8")
self._copy_real_templates(tmp_path)
build(tmp_path)
category_html = (tmp_path / "website" / "output" / "categories" / "widgets" / "index.html").read_text(encoding="utf-8")
assert 'href="https://sponsor.example.com"' in category_html
assert '<span class="sponsor-name">Sponsor</span>' in category_html
def test_build_cleans_stale_output(self, tmp_path):
readme = textwrap.dedent("""\
# T
@@ -1278,6 +1341,23 @@ class TestExtractGithubRepo:
assert extract_github_repo("https://github.com/org") is None
# ---------------------------------------------------------------------------
# sponsor_website_url
# ---------------------------------------------------------------------------
class TestSponsorWebsiteUrl:
def test_replaces_readme_utm_content(self):
url = "https://example.com?utm_source=awesome-python&utm_medium=sponsorship&utm_campaign=sponsor&utm_content=readme"
assert sponsor_website_url(url) == "https://example.com?utm_source=awesome-python&utm_medium=sponsorship&utm_campaign=sponsor&utm_content=website"
def test_keeps_path_and_other_params(self):
assert sponsor_website_url("https://example.com/landing?ref=x&utm_content=readme") == "https://example.com/landing?ref=x&utm_content=website"
def test_leaves_untagged_url_unchanged(self):
assert sponsor_website_url("https://example.com") == "https://example.com"
# ---------------------------------------------------------------------------
# load_stars
# ---------------------------------------------------------------------------