Skip to content

Commit 04a3f57

Browse files
authored
Merge pull request #118 from weeklydevchat/feature/sponsor-page
Re-design the sponsors page
2 parents 2774db9 + 59d2370 commit 04a3f57

16 files changed

Lines changed: 624 additions & 35 deletions

AGENTS.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,28 @@ Weekly Dev Chat website — MkDocs Material static site. Read `mkdocs.yml` and r
1111
- Use `./create_post.sh` to scaffold a new post (calculates next Tuesday automatically).
1212
- **Multiple posts on the same date:** If a date folder already has an `index.md`, prefix the filename with a number and dash (e.g., `0-index.md`). The newest/latest post should use the lowest number so it appears first on the homepage. The original `index.md` keeps its name.
1313

14+
## Sponsors
15+
16+
- Sponsor data lives in `data/sponsors.yml` (loaded via the `mkdocs-macros` plugin). The file's header comment documents the schema and consent policy.
17+
- Sponsor logos go in `docs/assets/sponsors/`. Reference them with just the filename in the `image:` field.
18+
- Optimize logos with `python3 scripts/optimize_image.py <path>` before committing.
19+
- The page itself is `docs/sponsors/index.md`; styles are in `docs/stylesheets/sponsors.css`.
20+
1421
## Guardrails
1522

1623
- Pushing to `main` triggers automatic deployment to production. Do not push without explicit approval.
1724
- Do not modify `.github/workflows/ci.yml` unless explicitly asked.
18-
- Verify changes build cleanly with `mkdocs serve` before committing.
25+
- Verify changes build cleanly with `docker compose run --rm app mkdocs serve` before committing.
26+
27+
## Using Docker Compose
28+
29+
All Python, mkdocs, and similar commands should run via Docker Compose to ensure consistent Python versions:
30+
31+
```bash
32+
docker compose run --rm app <command>
33+
34+
# Examples:
35+
docker compose run --rm app mkdocs serve
36+
docker compose run --rm app mkdocs build
37+
docker compose run --rm app python -m pip list
38+
```

README.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,10 @@ python scripts/find_tags_categories.py
173173

174174
This script requires `pyyaml`, which is included in `requirements.txt`. It is also run automatically by the `create_post` scripts when scaffolding a new post.
175175

176+
## Updating Sponsors
177+
178+
Sponsor and donor entries live in [`data/sponsors.yml`](data/sponsors.yml), which is loaded into the sponsors page via the [`mkdocs-macros`](https://mkdocs-macros-plugin.readthedocs.io/) plugin. The file's header comment documents the schema, consent policy, and how to reference one sponsor across multiple years. Place sponsor logos in `docs/assets/sponsors/` and reference the filename via the `image:` field (e.g. `image: example.png``docs/assets/sponsors/example.png`). Optimize logos with `python3 scripts/optimize_image.py` before committing.
179+
176180
## Project Structure
177181

178182
```
@@ -187,6 +191,8 @@ This script requires `pyyaml`, which is included in `requirements.txt`. It is al
187191
├── scripts/
188192
│ ├── find_tags_categories.py # List all existing tags and categories
189193
│ └── optimize_image.py # Optimize images for the web (PNG/JPEG → WebP)
194+
├── data/
195+
│ └── sponsors.yml # Sponsor & donor data (consumed by macros plugin)
190196
├── .github/
191197
│ ├── dependabot.yml # Dependabot configuration
192198
│ └── workflows/
@@ -198,11 +204,13 @@ This script requires `pyyaml`, which is included in `requirements.txt`. It is al
198204
├── tags.md # Tags index page (auto-populated by tags plugin)
199205
├── hosts/ # Current hosts
200206
├── past-hosts/ # Past hosts
201-
├── sponsors/ # Sponsors
207+
├── sponsors/ # Sponsors page (index.md)
202208
├── posts/ # Blog posts (YYYY/MM/DD/)
203209
├── assets/ # Images, logos
210+
│ └── sponsors/ # Sponsor logos (referenced by data/sponsors.yml)
204211
└── stylesheets/
205-
└── extra.css # Custom CSS
212+
├── extra.css # Site-wide custom CSS
213+
└── sponsors.css # Sponsors page styling
206214
```
207215

208216
## Common Commands

data/sponsors.yml

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# Sponsors & Donors data for Weekly Dev Chat
2+
#
3+
# Sponsor details are defined once under `sponsors:` and referenced by ID
4+
# in each year under `years:`. This avoids duplicating a sponsor's record
5+
# when they return across multiple years — update their info in one place.
6+
#
7+
# Sponsor fields:
8+
# name (required) Display name.
9+
# image (optional) Filename in docs/assets/sponsors/.
10+
# tier (optional) Short label shown on the card (e.g. "Primary Sponsor").
11+
# description (optional) Short thank-you or description line.
12+
# links (optional) List of { label, url } pairs shown on the card back.
13+
# link (optional, legacy) Single URL — used only if `links` is not set.
14+
# link_label (optional, legacy) Label for the legacy single `link`.
15+
#
16+
# Consent policy: only list individual donors by name with their explicit
17+
# opt-in consent. When in doubt, leave them out or use "Anonymous".
18+
#
19+
# Example (commented out — copy, uncomment, and edit when adding a sponsor):
20+
#
21+
# sponsors:
22+
# example-org:
23+
# name: Example Org
24+
# image: example.png
25+
# tier: Community Partner
26+
# description: Thanks to Example Org for supporting the community.
27+
# links:
28+
# - label: Website
29+
# url: https://example.com/
30+
31+
sponsors:
32+
saturday-mp:
33+
name: Saturday Morning Productions
34+
image: saturday-morning-productions.webp
35+
tier: Primary Sponsor
36+
description: Main financial sponsor that pays the bills and keeps the lights on.
37+
links:
38+
- label: Website
39+
url: https://saturdaymp.com/
40+
- label: GitHub Sponsors
41+
url: https://github.com/sponsors/saturdaymp
42+
43+
dev-edmonton:
44+
name: Dev Edmonton Society
45+
image: dev-edmonton.webp
46+
tier: Community Partner
47+
description: Provides a Slack workspace and other promotional support.
48+
links:
49+
- label: Website
50+
url: https://devedmonton.com/
51+
52+
edmonton-unlimited:
53+
name: Edmonton Unlimited
54+
image: edmonton-unlimited.webp
55+
tier: Community Partner
56+
description: Provide the Meetup Group and other promotional support.
57+
58+
links:
59+
- label: Website
60+
url: https://edmontonunlimited.com/
61+
- label: Meetup
62+
url: https://www.meetup.com/edmontonunlimited/
63+
64+
years:
65+
2026:
66+
- saturday-mp
67+
- dev-edmonton
68+
- edmonton-unlimited
69+
70+
2025:
71+
- saturday-mp
72+
- dev-edmonton
73+
- edmonton-unlimited
File renamed without changes.

docs/sponsors/smp.webp renamed to docs/assets/sponsors/saturday-morning-productions.webp

File renamed without changes.

docs/posts/2026/05/05/github.png

-326 KB
Binary file not shown.

docs/posts/2026/05/05/github.webp

38.4 KB
Loading

docs/posts/2026/05/05/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,4 @@ Others have commented on their [recent decline](https://dbushell.com/2026/04/29/
2323

2424
Everyone and anyone are welcome to [join](https://weeklydevchat.com/join/) as long as you are kind, supportive, and respectful of others. Zoom link will be posted at 12pm MDT.
2525

26-
![alt text](github.png)
26+
![alt text](github.webp)

docs/sponsors/index.md

Lines changed: 120 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,132 @@
22
hide:
33
- toc
44
---
5-
# Help and Sponsorship
5+
# Sponsors
66

7-
The best way you can help the Weekly Dev Chat is to attend the events as the kind, supportive, and respectful person you are. A close second is to share the Weekly Dev Chat with others who might benefit.
7+
<div class="wdc-sponsors-page" markdown>
8+
<p class="lead">The Weekly Dev Chat is powered by community members like you!</p>
9+
<section class="support">
10+
<div class="support-card">
11+
<h3>Show Up</h3>
12+
<p>Attending events is the best way to support the Weekly Dev Chat. Bring your curiosity and be willing to share your knowledge and learn from others.</p>
13+
</div>
14+
<div class="support-card">
15+
<h3>Spread the Word</h3>
16+
<p>Invite others who follow our values to an event. Everyone and anyone is welcome as long as they are kind, supportive, and respectful of others.</p>
17+
</div>
18+
<div class="support-card">
19+
<h3>Sponsor</h3>
20+
<p>Help keep the lights on and get things done with contributions of money, time, skills, or other resources. Every little bit helps.</p>
21+
</div>
22+
</section>
823

9-
We also need volunteers to help with a variety of tasks from helping with events, admin work, website maintenance, social media, etc. If you are interested in volunteering, please reach out to Chris via email at <chris.cumming@saturdaymp.com>.
24+
<section class="become-sponsor">
25+
<div class="eyebrow"><span class="dot"></span> Want on this page?</div>
26+
<div class="become-inner">
27+
<div>
28+
<h3>Thank you to our Current and Past Sponsors!</h3>
29+
<p>Email us if you have any questions or would like to make non-financial contributions.</p>
30+
</div>
31+
<div class="become-ctas">
32+
<a class="btn primary" href="https://buy.stripe.com/dRmaEY4HJ2xUgcG8PdfIs01">Sponsor</a>
33+
<a class="btn primary" href="mailto:chris@weeklydevchat.com">Email</a>
34+
</div>
35+
</div>
36+
</section>
1037

11-
The final way you can help is by sponsoring SaturdayMP, the main Weekly Dev Chat sponsor, via GitHub [sponsors](https://github.com/sponsors/saturdaymp). Saturday MP pays for Zoom, hosting, food for in real life (IRL) events, and other expenses.
38+
{% for year in sponsors.years.keys() | sort(reverse=true) %}
39+
{% set valid_ids = (sponsors.years[year] or []) | select('in', sponsors.sponsors) | list %}
40+
<section class="year-group" data-year="{{ year }}">
41+
<div class="year-header">
42+
<h2 class="year-num" id="year-{{ year }}">{{ year }}</h2>
43+
<span class="year-meta">{{ valid_ids|length }} sponsor{{ '' if valid_ids|length == 1 else 's' }} (click/tap for info)</span>
44+
<div class="year-rule"></div>
45+
</div>
46+
<div class="sponsor-grid">
47+
{% for id in valid_ids %}
48+
{% set s = sponsors.sponsors[id] %}
49+
{% set card_links = s.links if s.links else ([{'label': s.link_label or 'Website', 'url': s.link}] if s.link else []) %}
50+
<div class="sponsor-card" role="button" tabindex="0">
51+
<div class="card-face card-front">
52+
<div class="card-logo">
53+
{% if s.image %}<img src="../assets/sponsors/{{ s.image }}" alt="{{ s.name }}">{% else %}<div class="card-logo-placeholder">{{ (s.name or 'Anonymous')[0] }}</div>{% endif %}
54+
</div>
55+
<div class="card-footer">
56+
<span class="card-tier">{{ s.name or 'Sponsor' }}</span>
57+
</div>
58+
</div>
59+
<div class="card-face card-back">
60+
<div class="card-back-inner">
61+
{% if s.tier %}<div class="card-back-tier">{{ s.tier }}</div>{% endif %}
62+
<h4 class="card-back-name">{{ s.name }}</h4>
63+
{% if s.description %}<p class="card-back-desc">{{ s.description }}</p>{% endif %}
64+
{% if card_links %}
65+
<div class="card-back-links">
66+
{% for l in card_links %}
67+
<a href="{{ l.url }}" target="_blank" rel="noopener">{{ l.label }} <span class="arrow">↗</span></a>
68+
{% endfor %}
69+
</div>
70+
{% endif %}
71+
</div>
72+
</div>
73+
</div>
74+
{% endfor %}
75+
</div>
76+
</section>
77+
{% endfor %}
1278

13-
If you have any other ideas for helping Weekly Dev Chat please give [Chris](mailto:chris.cumming@saturdaymp.com) a shout. Thank you for your help and support, it is much appreciated.
79+
<dialog id="wdc-sponsor-modal">
80+
<div class="wdc-sponsor-modal-inner">
81+
<button class="wdc-sponsor-modal-close" aria-label="Close">&#x2715;</button>
82+
<div class="wdc-sponsor-modal-tier"></div>
83+
<h3 class="wdc-sponsor-modal-name"></h3>
84+
<p class="wdc-sponsor-modal-desc"></p>
85+
<div class="wdc-sponsor-modal-links"></div>
86+
</div>
87+
</dialog>
1488

15-
Sponsors
16-
--------
89+
</div>
1790

18-
![](smp.webp){: style="width:150px;float: left;padding-right: 10px;"}
91+
<script>
92+
(function () {
93+
var modal = document.getElementById('wdc-sponsor-modal');
94+
if (!modal) return;
95+
var mTier = modal.querySelector('.wdc-sponsor-modal-tier');
96+
var mName = modal.querySelector('.wdc-sponsor-modal-name');
97+
var mDesc = modal.querySelector('.wdc-sponsor-modal-desc');
98+
var mLinks = modal.querySelector('.wdc-sponsor-modal-links');
99+
var mClose = modal.querySelector('.wdc-sponsor-modal-close');
19100

20-
**Saturday Morning Productions**
101+
function openModal(card) {
102+
var tier = card.querySelector('.card-back-tier');
103+
var name = card.querySelector('.card-back-name');
104+
var desc = card.querySelector('.card-back-desc');
105+
var links = card.querySelector('.card-back-links');
106+
mTier.textContent = tier ? tier.textContent.trim() : '';
107+
mTier.hidden = !tier;
108+
mName.textContent = name ? name.textContent.trim() : '';
109+
mDesc.textContent = desc ? desc.textContent.trim() : '';
110+
mDesc.hidden = !desc;
111+
mLinks.innerHTML = links ? links.innerHTML : '';
112+
mLinks.hidden = !links;
113+
modal.showModal();
114+
}
21115

22-
Thanks to [Saturday MP](https://saturdaymp.com/) for providing hosting, Zoom, and more.
116+
document.querySelectorAll('.wdc-sponsors-page .sponsor-card').forEach(function (card) {
117+
card.addEventListener('click', function () {
118+
card.blur();
119+
openModal(card);
120+
});
121+
card.addEventListener('keydown', function (e) {
122+
if (e.key === 'Enter' || e.key === ' ') {
123+
e.preventDefault();
124+
card.blur();
125+
openModal(card);
126+
}
127+
});
128+
});
23129

24-
![](devEd.webp){: style="width:150px;float: left;padding-right: 10px;"}
25-
26-
**Dev Edmonton Society**
27-
28-
Thanks to [DES](https://devedmonton.com/) for providing a Slack channel.
29-
30-
![](EdmontonUnlimited.webp){: style="width:150px;float: left;padding-right: 10px;"}
31-
32-
**Edmonton Unlimited**
33-
34-
Thanks to [Edmonton Unlimited](https://edmontonunlimited.com/) for providing a [Meetup Link](https://edmontonunlimited.com/).
130+
mClose.addEventListener('click', function () { modal.close(); });
131+
modal.addEventListener('click', function (e) { if (e.target === modal) modal.close(); });
132+
})();
133+
</script>

0 commit comments

Comments
 (0)