diff --git a/AGENTS.md b/AGENTS.md index 53e421dd..39586687 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -11,8 +11,28 @@ Weekly Dev Chat website — MkDocs Material static site. Read `mkdocs.yml` and r - Use `./create_post.sh` to scaffold a new post (calculates next Tuesday automatically). - **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. +## Sponsors + +- Sponsor data lives in `data/sponsors.yml` (loaded via the `mkdocs-macros` plugin). The file's header comment documents the schema and consent policy. +- Sponsor logos go in `docs/assets/sponsors/`. Reference them with just the filename in the `image:` field. +- Optimize logos with `python3 scripts/optimize_image.py ` before committing. +- The page itself is `docs/sponsors/index.md`; styles are in `docs/stylesheets/sponsors.css`. + ## Guardrails - Pushing to `main` triggers automatic deployment to production. Do not push without explicit approval. - Do not modify `.github/workflows/ci.yml` unless explicitly asked. -- Verify changes build cleanly with `mkdocs serve` before committing. +- Verify changes build cleanly with `docker compose run --rm app mkdocs serve` before committing. + +## Using Docker Compose + +All Python, mkdocs, and similar commands should run via Docker Compose to ensure consistent Python versions: + +```bash +docker compose run --rm app + +# Examples: +docker compose run --rm app mkdocs serve +docker compose run --rm app mkdocs build +docker compose run --rm app python -m pip list +``` \ No newline at end of file diff --git a/README.md b/README.md index 6ccd359b..36eb5fde 100644 --- a/README.md +++ b/README.md @@ -173,6 +173,10 @@ python scripts/find_tags_categories.py 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. +## Updating Sponsors + +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. + ## Project Structure ``` @@ -185,8 +189,11 @@ This script requires `pyyaml`, which is included in `requirements.txt`. It is al ├── requirements-dev.in # Dev-only dependency pins (e.g. Pillow) ├── requirements-dev.txt # Compiled dev dependencies ├── scripts/ -│ ├── find_tags_categories.py # List all existing tags and categories +│ |── find_tags_categories.py # List all existing tags and categories +│ |── find_tags_categories.py # List all existing tags and categories │ └── optimize_image.py # Optimize images for the web (PNG/JPEG → WebP) +├── data/ +│ └── sponsors.yml # Sponsor & donor data (consumed by macros plugin) ├── .github/ │ ├── dependabot.yml # Dependabot configuration │ └── workflows/ @@ -198,11 +205,13 @@ This script requires `pyyaml`, which is included in `requirements.txt`. It is al ├── tags.md # Tags index page (auto-populated by tags plugin) ├── hosts/ # Current hosts ├── past-hosts/ # Past hosts - ├── sponsors/ # Sponsors + ├── sponsors/ # Sponsors page (index.md) ├── posts/ # Blog posts (YYYY/MM/DD/) ├── assets/ # Images, logos + │ └── sponsors/ # Sponsor logos (referenced by data/sponsors.yml) └── stylesheets/ - └── extra.css # Custom CSS + ├── extra.css # Site-wide custom CSS + └── sponsors.css # Sponsors page styling ``` ## Common Commands diff --git a/data/sponsors.yml b/data/sponsors.yml new file mode 100644 index 00000000..b39988e1 --- /dev/null +++ b/data/sponsors.yml @@ -0,0 +1,73 @@ +# Sponsors & Donors data for Weekly Dev Chat +# +# Sponsor details are defined once under `sponsors:` and referenced by ID +# in each year under `years:`. This avoids duplicating a sponsor's record +# when they return across multiple years — update their info in one place. +# +# Sponsor fields: +# name (required) Display name. +# image (optional) Filename in docs/sponsors/. +# tier (optional) Short label shown on the card (e.g. "Primary Sponsor"). +# description (optional) Short thank-you or description line. +# links (optional) List of { label, url } pairs shown on the card back. +# link (optional, legacy) Single URL — used only if `links` is not set. +# link_label (optional, legacy) Label for the legacy single `link`. +# +# Consent policy: only list individual donors by name with their explicit +# opt-in consent. When in doubt, leave them out or use "Anonymous". +# +# Example (commented out — copy, uncomment, and edit when adding a sponsor): +# +# sponsors: +# example-org: +# name: Example Org +# image: example.png +# tier: Community Partner +# description: Thanks to Example Org for supporting the community. +# links: +# - label: Website +# url: https://example.com/ + +sponsors: + saturday-mp: + name: Saturday Morning Productions + image: saturday-morning-productions.jpeg + tier: Primary Sponsor + description: Main financial sponsor. + links: + - label: Website + url: https://saturdaymp.com/ + - label: GitHub Sponsors + url: https://github.com/sponsors/saturdaymp + + dev-edmonton: + name: Dev Edmonton Society + image: dev-edmonton.png + tier: Community Partner + description: Slack workspace and other promotional support. + links: + - label: Website + url: https://devedmonton.com/ + + edmonton-unlimited: + name: Edmonton Unlimited + image: edmonton-unlimited.jpeg + tier: Community Partner + description: Meetup group and other promotional support. + + links: + - label: Website + url: https://edmontonunlimited.com/ + - label: Meetup + url: https://www.meetup.com/Edmonton-Unlimited/ + +years: + 2026: + - saturday-mp + - dev-edmonton + - edmonton-unlimited + + 2025: + - saturday-mp + - dev-edmonton + - edmonton-unlimited diff --git a/docs/sponsors/devEd.webp b/docs/assets/sponsors/dev-edmonton.webp similarity index 100% rename from docs/sponsors/devEd.webp rename to docs/assets/sponsors/dev-edmonton.webp diff --git a/docs/sponsors/EdmontonUnlimited.webp b/docs/assets/sponsors/edmonton-unlimited.webp similarity index 100% rename from docs/sponsors/EdmontonUnlimited.webp rename to docs/assets/sponsors/edmonton-unlimited.webp diff --git a/docs/sponsors/smp.webp b/docs/assets/sponsors/saturday-morning-productions.webp similarity index 100% rename from docs/sponsors/smp.webp rename to docs/assets/sponsors/saturday-morning-productions.webp diff --git a/docs/posts/2026/05/05/github.png b/docs/posts/2026/05/05/github.png deleted file mode 100644 index c89138b8..00000000 Binary files a/docs/posts/2026/05/05/github.png and /dev/null differ diff --git a/docs/posts/2026/05/05/github.webp b/docs/posts/2026/05/05/github.webp new file mode 100644 index 00000000..0ceba7c9 Binary files /dev/null and b/docs/posts/2026/05/05/github.webp differ diff --git a/docs/posts/2026/05/05/index.md b/docs/posts/2026/05/05/index.md index 3940f3e8..cd0fe4f3 100644 --- a/docs/posts/2026/05/05/index.md +++ b/docs/posts/2026/05/05/index.md @@ -23,4 +23,4 @@ Others have commented on their [recent decline](https://dbushell.com/2026/04/29/ 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. -![alt text](github.png) +![alt text](github.webp) diff --git a/docs/sponsors/index.md b/docs/sponsors/index.md index cccb8fd8..59e5cc8b 100644 --- a/docs/sponsors/index.md +++ b/docs/sponsors/index.md @@ -2,33 +2,132 @@ hide: - toc --- -# Help and Sponsorship +# Sponsors -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. +
+

The Weekly Dev Chat is powered by community members like you!

+
+
+

Show Up

+

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.

+
+
+

Spread the Word

+

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.

+
+
+

Sponsor

+

Help keep the lights on and get things done with contributions of money, time, skills, or other resources. Every little bit helps.

+
+
-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 . +
+
Want on this page?
+
+
+

Thank you to our Current and Past Sponsors!

+

Email us if you have any questions or would like to make non-financial contributions.

+
+
+ Sponsor + Email +
+
+
-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. + {% for year in sponsors.years.keys() | sort(reverse=true) %} + {% set valid_ids = (sponsors.years[year] or []) | select('in', sponsors.sponsors) | list %} +
+
+

{{ year }}

+ {{ valid_ids|length }} sponsor{{ '' if valid_ids|length == 1 else 's' }} (click/tap for info) +
+
+ +
+ {% endfor %} -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. + +
+ +
+

+

+ +
+
-Sponsors --------- +
-![](smp.webp){: style="width:150px;float: left;padding-right: 10px;"} + diff --git a/docs/sponsors/logo_ZM_wordmark_bloom.webp b/docs/sponsors/logo_ZM_wordmark_bloom.webp deleted file mode 100644 index 52ae584c..00000000 Binary files a/docs/sponsors/logo_ZM_wordmark_bloom.webp and /dev/null differ diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css index d7eb6a11..f2af64b0 100644 --- a/docs/stylesheets/extra.css +++ b/docs/stylesheets/extra.css @@ -1,3 +1,13 @@ +.md-typeset h1 { + font-weight: 300; + font-size: clamp(32px, 4.5vw, 48px); + letter-spacing: -0.02em; + line-height: 1.1; + color: var(--md-primary-fg-color, #009688); + border-bottom: none; + padding-bottom: 0; +} + .logo-article-img { width: 300px; margin-left: 20px; @@ -8,4 +18,4 @@ float: right; margin-left: 10px; } -} \ No newline at end of file +} diff --git a/docs/stylesheets/sponsors.css b/docs/stylesheets/sponsors.css new file mode 100644 index 00000000..8cc02b4a --- /dev/null +++ b/docs/stylesheets/sponsors.css @@ -0,0 +1,350 @@ +/* Sponsors page — scoped to avoid leaking onto host/past-host pages. */ +.wdc-sponsors-page { + --paper: #fafafa; + --line: #e0e0e0; + --ink: #1a1a1a; + --ink-2: #5a5a5a; + --ink-3: #8a8a8a; + --teal: var(--md-primary-fg-color, #009688); + --teal-dark: #00766c; + --blue: var(--md-accent-fg-color, #448aff); + --radius: 14px; + --mono: ui-monospace, "SF Mono", Menlo, monospace; +} + +.wdc-sponsors-page .lead { + font-size: clamp(22px, 2.5vw, 28px); + font-weight: 300; + line-height: 1.5; + color: var(--ink); + margin: 4px 0 12px; + max-width: 60ch; +} + +/* Support cards */ +.wdc-sponsors-page .support { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 16px; + padding: 20px 0; + margin: 16px 0 20px; + border-bottom: 1px solid var(--line); +} +.wdc-sponsors-page .support-card { + padding: 22px; + border-radius: var(--radius); + background: var(--paper); + border: 1px solid var(--line); + transition: transform 0.2s, box-shadow 0.2s; +} +.wdc-sponsors-page .support-card:hover { + transform: translateY(-2px); + box-shadow: 0 1px 0 rgba(0, 0, 0, 0.04), 0 2px 6px rgba(0, 0, 0, 0.05); +} +.wdc-sponsors-page .support-card h3 { + font-weight: 400; + font-size: 22px; + margin: 10px 0 8px; +} +.wdc-sponsors-page .support-card p { + margin: 0; + color: var(--ink-2); + font-size: 15px; +} + +/* Year header */ +.wdc-sponsors-page .year-group { + margin-bottom: 72px; + scroll-margin-top: 80px; +} +.wdc-sponsors-page .year-header { + display: flex; + align-items: baseline; + gap: 16px; + margin-bottom: 20px; +} +.wdc-sponsors-page .year-num { + font-weight: 300; + font-size: clamp(24px, 3vw, 32px); + margin: 0; + color: var(--teal); + border-bottom: none; + padding-bottom: 0; +} +.wdc-sponsors-page .year-meta { + font-family: var(--mono); + font-size: 12px; + letter-spacing: 0.06em; + color: var(--ink-3); + text-transform: uppercase; +} +.wdc-sponsors-page .year-rule { + flex: 1; + height: 1px; + background: var(--line); +} + +/* Sponsor cards */ +.wdc-sponsors-page .sponsor-grid { + display: grid; + grid-template-columns: repeat(auto-fill, 160px); + gap: 14px; + justify-content: start; +} +.wdc-sponsors-page .sponsor-card { + position: relative; + aspect-ratio: 1 / 1; + border-radius: var(--radius); + background: var(--paper); + border: 1px solid var(--line); + overflow: hidden; + cursor: pointer; + outline: none; + transition: transform 0.25s, box-shadow 0.25s; +} +.wdc-sponsors-page .sponsor-card:hover { + transform: translateY(-3px); + box-shadow: 0 10px 40px -12px rgba(0, 0, 0, 0.18); +} +.wdc-sponsors-page .sponsor-card:focus-visible { + outline: 3px solid var(--blue); + outline-offset: 2px; +} + +.wdc-sponsors-page .card-face { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + padding: 18px; + transition: opacity 0.22s; +} +.wdc-sponsors-page .card-logo { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + padding: 2px; + min-height: 0; +} +.wdc-sponsors-page .card-logo img { + max-width: 85%; + max-height: 85%; + object-fit: contain; +} +.wdc-sponsors-page .card-logo-placeholder { + width: 60%; + aspect-ratio: 1 / 1; + border-radius: 50%; + background: #e0f2f1; + color: var(--teal-dark); + display: flex; + align-items: center; + justify-content: center; + font-size: 36px; + font-weight: 300; +} +.wdc-sponsors-page .card-footer { + display: flex; + justify-content: space-between; + padding-top: 10px; + border-top: 1px dashed var(--line); + font-family: var(--mono); + font-size: 10px; + letter-spacing: 0.06em; + text-transform: uppercase; +} +.wdc-sponsors-page .card-tier { + color: var(--ink-2); + font-weight: 600; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + min-width: 0; +} + +.wdc-sponsors-page .card-back { + background: var(--teal-dark); + color: #fff; + opacity: 0; + pointer-events: none; +} + +/* Become a sponsor CTA */ +.wdc-sponsors-page .become-sponsor { + margin: 20px 0; + padding: 40px; + border-radius: var(--radius); + background: linear-gradient(135deg, var(--teal-dark), var(--teal)); + color: #fff; + position: relative; + overflow: hidden; +} +.wdc-sponsors-page .become-sponsor::before { + content: ""; + position: absolute; + inset: 0; + background: radial-gradient(circle at 80% 20%, rgba(68, 138, 255, 0.3), transparent 50%); + pointer-events: none; +} +.wdc-sponsors-page .become-inner { + display: grid; + grid-template-columns: 1.4fr auto; + gap: 32px; + align-items: center; + position: relative; +} +.wdc-sponsors-page .eyebrow { + display: inline-flex; + align-items: center; + gap: 8px; + font-family: var(--mono); + font-size: 12px; + letter-spacing: 0.06em; + text-transform: uppercase; + padding: 6px 10px; + background: rgba(255, 255, 255, 0.15); + border-radius: 999px; +} +.wdc-sponsors-page .dot { + width: 7px; + height: 7px; + border-radius: 50%; + background: var(--blue); + border: 1px solid #fff; + box-shadow: 0 0 0 3px rgba(68, 138, 255, 0.3); +} +.wdc-sponsors-page .become-sponsor h3 { + font-weight: 300; + font-size: 36px; + margin: 14px 0 10px; + line-height: 1.15; + color: #fff; + border-bottom: none; +} +.wdc-sponsors-page .become-sponsor p { + margin: 0; + color: rgba(255, 255, 255, 0.9); + max-width: 48ch; +} +.wdc-sponsors-page .become-ctas { + display: flex; + flex-direction: column; + gap: 10px; +} +.wdc-sponsors-page .btn { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 12px 48px; + border-radius: 12px; + border: 2px solid rgba(255, 255, 255, 0.9); + font-weight: 500; + font-size: 17px; + text-decoration: none; + white-space: nowrap; + transition: background 0.15s, border-color 0.15s; +} +.wdc-sponsors-page .btn.primary { background: var(--blue); color: #fff; } +.wdc-sponsors-page .btn.primary:hover { background: #2979ff; border-color: #fff; } +.wdc-sponsors-page .btn.ghost { background: rgba(255, 255, 255, 0.25); color: #fff; } +.wdc-sponsors-page .btn.ghost:hover { background: rgba(255, 255, 255, 0.35); border-color: #fff; } + +@media (max-width: 900px) { + .wdc-sponsors-page .support { grid-template-columns: 1fr; } + .wdc-sponsors-page .sponsor-grid { + grid-template-columns: repeat(auto-fill, 130px); + gap: 10px; + justify-content: center; + } + .wdc-sponsors-page .become-inner { grid-template-columns: 1fr; } + .wdc-sponsors-page .become-ctas { flex-direction: row; } + .wdc-sponsors-page .become-sponsor { padding: 28px; } + .wdc-sponsors-page .become-sponsor h3 { font-size: 28px; } + /* Reduce padding and hide footer so logo fills the smaller cards */ + .wdc-sponsors-page .card-face { padding: 10px; } + .wdc-sponsors-page .card-logo { padding: 6px; } + .wdc-sponsors-page .card-footer { display: none; } +} + +/* Sponsor info modal (replaces flip card on small screens) */ +#wdc-sponsor-modal { + border: none; + border-radius: var(--radius); + padding: 0; + width: min(92vw, 400px); + background: var(--teal-dark); + color: #fff; + box-shadow: 0 24px 80px rgba(0, 0, 0, 0.35); + overflow: hidden; +} +#wdc-sponsor-modal::backdrop { + background: rgba(0, 0, 0, 0.45); + backdrop-filter: blur(3px); +} +.wdc-sponsor-modal-inner { + padding: 28px; + position: relative; + overflow-y: auto; + max-height: 80vh; +} +.wdc-sponsor-modal-close { + position: absolute; + top: 14px; + right: 14px; + width: 30px; + height: 30px; + border-radius: 50%; + background: rgba(255, 255, 255, 0.15); + border: none; + color: #fff; + font-size: 18px; + line-height: 1; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + padding: 0; +} +.wdc-sponsor-modal-close:hover { background: rgba(255, 255, 255, 0.28); } +.wdc-sponsor-modal-tier { + font-family: var(--mono); + font-size: 10px; + letter-spacing: 0.08em; + text-transform: uppercase; + color: #b2dfdb; + margin-bottom: 6px; +} +.wdc-sponsor-modal-name { + font-weight: 400; + font-size: 22px; + margin: 0 0 14px; + line-height: 1.2; + color: #fff; + border-bottom: none; + padding-bottom: 0; +} +.wdc-sponsor-modal-desc { + font-size: 14px; + line-height: 1.6; + color: rgba(255, 255, 255, 0.85); + margin: 0 0 16px; +} +.wdc-sponsor-modal-links { + display: flex; + flex-wrap: wrap; + gap: 6px; + padding-top: 14px; + border-top: 1px solid rgba(255, 255, 255, 0.15); +} +.wdc-sponsor-modal-links a { + font-family: var(--mono); + font-size: 11px; + padding: 5px 10px; + background: rgba(255, 255, 255, 0.12); + color: #fff; + border-radius: 4px; + text-decoration: none; + transition: background 0.15s; +} +.wdc-sponsor-modal-links a:hover { background: var(--blue); } diff --git a/mkdocs.yml b/mkdocs.yml index bbaa4d29..b4f22f1e 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -28,6 +28,9 @@ plugins: # more blog parameters here: # https://squidfunk.github.io/mkdocs-material/plugins/blog/ - tags + - macros: + include_yaml: + - sponsors: data/sponsors.yml nav: - Home: index.md @@ -40,9 +43,14 @@ nav: exclude_docs: | past_topics.md + +watch: + - data + markdown_extensions: - attr_list # This lets us specify image dimensions, etc. -extra_css: +extra_css: - stylesheets/extra.css + - stylesheets/sponsors.css diff --git a/requirements.in b/requirements.in index ff213999..89c38884 100644 --- a/requirements.in +++ b/requirements.in @@ -1,6 +1,7 @@ # uv pip compile requirements.in --output-file requirements.txt mkdocs-material>=9.7.0 +mkdocs-macros-plugin # GitHub's dependabot detected an error in previous versions urllib3>=2.7.0 diff --git a/requirements.txt b/requirements.txt index 1e0087db..8f0cdeab 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,11 +14,16 @@ colorama==0.4.6 # via mkdocs-material ghp-import==2.1.0 # via mkdocs +hjson==3.1.0 + # via + # mkdocs-macros-plugin + # super-collections idna==3.11 # via requests jinja2==3.1.6 # via # mkdocs + # mkdocs-macros-plugin # mkdocs-material markdown==3.10 # via @@ -34,21 +39,27 @@ mergedeep==1.3.4 # mkdocs # mkdocs-get-deps mkdocs==1.6.1 - # via mkdocs-material + # via + # mkdocs-macros-plugin + # mkdocs-material mkdocs-get-deps==0.2.0 # via mkdocs +mkdocs-macros-plugin==1.5.0 + # via -r requirements.in mkdocs-material==9.7.6 # via -r requirements.in mkdocs-material-extensions==1.3.1 # via mkdocs-material packaging==25.0 - # via mkdocs + # via + # mkdocs + # mkdocs-macros-plugin paginate==0.5.7 # via mkdocs-material pathspec==0.12.1 - # via mkdocs -pillow==12.2.0 - # via -r requirements.in + # via + # mkdocs + # mkdocs-macros-plugin platformdirs==4.5.1 # via mkdocs-get-deps pygments==2.20.0 @@ -58,20 +69,29 @@ pygments==2.20.0 pymdown-extensions==10.18 # via mkdocs-material python-dateutil==2.9.0.post0 - # via ghp-import + # via + # ghp-import + # mkdocs-macros-plugin pyyaml==6.0.3 # via # mkdocs # mkdocs-get-deps + # mkdocs-macros-plugin # pymdown-extensions # pyyaml-env-tag pyyaml-env-tag==1.1 # via mkdocs requests==2.33.0 - # via mkdocs-material + # via + # mkdocs-macros-plugin + # mkdocs-material six==1.17.0 # via python-dateutil -urllib3==2.7.0 +super-collections==0.6.2 + # via mkdocs-macros-plugin +termcolor==3.3.0 + # via mkdocs-macros-plugin +urllib3==2.6.3 # via # -r requirements.in # requests