Skip to content

Commit 35833f9

Browse files
committed
docs: record SEO/asset-pipeline ADR and update README
- ADR 0005: head-level SEO, JSON-LD by section, and the committed purged-CSS / self-hosted-font pipeline, with the constraints a future maintainer must keep - README: document `npm run build:css` and the updated dependency list
1 parent 9783b44 commit 35833f9

2 files changed

Lines changed: 85 additions & 2 deletions

File tree

README.md

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,22 @@ The author taxonomy automatically:
173173
Edit `baseof.html` CSS to customize.
174174

175175
### Tailwind CSS
176-
All styling uses Tailwind CSS utility classes. Update `baseof.html` or create component-specific classes.
176+
Styling uses Tailwind CSS utility classes. The site ships a **purged, self-hosted**
177+
build (`assets/css/tailwind.css`, ~35 KB) instead of the full ~2.9 MB CDN file —
178+
only the utility classes actually used anywhere in the built output are retained.
179+
180+
If you add a **new** Tailwind class in a template or content file, regenerate the
181+
purged stylesheet and commit it:
182+
183+
```bash
184+
npm install # one-time, for the purgecss devDependency
185+
npm run build:css # rebuilds assets/css/tailwind.css (see scripts/build-tailwind.mjs)
186+
```
187+
188+
You will notice a missing class immediately in `npm run dev` (dev serves the same
189+
purged file). Classes injected only at build time (e.g. activity-dot colors from
190+
`data/community_stats.json`) are pinned via the safelist in `purgecss.config.cjs`.
191+
The CI deploy builds run bare `hugo`, so the committed file is what ships.
177192

178193
### Layouts
179194
- Modify layouts in `themes/powershell-community/layouts/`
@@ -235,7 +250,8 @@ Check `data/community_stats.json` format and `last_updated` timestamp.
235250

236251
- **Hugo**: v0.128+ with Goldmark renderer
237252
- **Node.js**: For npm scripts
238-
- **Tailwind CSS**: v2.2.19 (via CDN)
253+
- **Tailwind CSS**: v2.2.19 (purged + self-hosted via `npm run build:css`)
254+
- **Inter**: self-hosted from `assets/fonts/` (`@font-face`, no Google Fonts)
239255
- **FontAwesome**: v6.0.0 (via CDN)
240256
- **Alpine.js**: v3.10.2 (via CDN)
241257

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# SEO metadata lives in the head; CSS and fonts are purged and self-hosted
2+
3+
The page `<head>` (`themes/powershell-community/layouts/_default/baseof.html`)
4+
emits the full technical-SEO surface: a `rel=canonical`, Open Graph + Twitter
5+
cards, `article:*` tags for posts, and JSON-LD via two partials
6+
(`partials/schema-site.html``Organization` + `WebSite` on the home page,
7+
`partials/schema-article.html``Article` + `BreadcrumbList` on `articles` and
8+
`podcast` pages). `robots.txt` (`layouts/robots.txt`, enabled by
9+
`enableRobotsTXT`) advertises the sitemap. Render-blocking third-party CSS is
10+
replaced by a purged, self-hosted Tailwind build and self-hosted Inter.
11+
12+
Two load-bearing constraints shaped the asset half of this decision.
13+
14+
**The deploy builds run bare `hugo`.** Both `netlify.toml` and
15+
`.github/workflows/deploy.yml` build with `hugo --minify`, not `npm run build`.
16+
So a CSS step wired into npm would never run in CI. The purged stylesheet is
17+
therefore a **committed artifact** (`assets/css/tailwind.css`, ~35 KB), not a
18+
build-time output. It is regenerated by `npm run build:css`
19+
(`scripts/build-tailwind.mjs`): download the pinned Tailwind 2.2.19 source, build
20+
the whole site to a throwaway dir, and purge the source against that output
21+
(`purgecss.config.cjs`). Hugo then `minify | fingerprint`s the committed file at
22+
build time so the immutable `Cache-Control` in `netlify.toml` is safe.
23+
24+
**Purging against built HTML misses classes that aren't in the committed output.**
25+
The extractor keeps Tailwind tokens (`:` `/` `.` survive), and runtime-toggled
26+
classes (Alpine `:class`, JS `classList`) are caught because they appear as
27+
literals in the HTML. The gap is classes injected from data that only exists at
28+
deploy time — the activity-dot colors written into `data/community_stats.json` by
29+
`.github/scripts/fetch-discourse-activity.js` (and the `deploy.yml` fallback).
30+
Those are pinned in the `purgecss.config.cjs` safelist.
31+
32+
## Considered options
33+
34+
- **Keep the CDN links** (full Tailwind ~2.9 MB + FontAwesome + Prism, all
35+
render-blocking; Inter via a `@import` in an inline `<style>`) — rejected. The
36+
unpurged Tailwind download alone dominates first paint, and an `@import` is the
37+
worst case for blocking when Inter ships in `assets/fonts/` already.
38+
- **PostCSS/Tailwind compiled in the Hugo build** (`css.PostCSS`) — rejected. It
39+
needs committed `node_modules` (tailwindcss + postcss) and a JIT migration off
40+
v2; the deploy builds don't run npm, so it would not execute in CI anyway.
41+
- **Regenerate via Tailwind v3/v4** — rejected. v3's palette and resets differ
42+
from the v2 classes the markup was authored against, risking silent visual
43+
drift. Purging the *exact* v2 file keeps retained rules byte-identical.
44+
- **Purge the committed v2 file against the built site; self-host Inter**
45+
chosen. No visual change for used classes, ~99% smaller CSS, one third-party
46+
render-blocking origin removed, and the Google Fonts round-trip eliminated.
47+
48+
## Consequences
49+
50+
- **Adding a new Tailwind class requires `npm run build:css`** and committing the
51+
result. The failure mode is visible, not silent: `npm run dev` serves the same
52+
purged file, so a missing class shows up locally before it ships. FontAwesome
53+
and Prism stay on the CDN (FontAwesome's CSS has relative `../webfonts/` font
54+
refs that would break if naively rehosted), so a preconnect is kept for them.
55+
- **A new build-time data class must be added to the safelist.** Anything driven
56+
by `community_stats.json` (or future data) that isn't present in committed
57+
markup will be purged unless pinned. The status-color palette is already pinned;
58+
`bg-orange-500` in the `deploy.yml` fallback is a no-op because orange is not in
59+
the stock Tailwind v2 palette — it never rendered, on the CDN or after.
60+
- **Inter ships only weights 400 and 700** (the two files in `assets/fonts/`).
61+
Heavier display weights used by `.gotham-black` (900) synthesize from 700; this
62+
was visually verified on the hero as acceptable. Adding a weight means adding
63+
the font file and a matching `@font-face`.
64+
- **JSON-LD is gated by section.** `Article`/`BreadcrumbList` render for `articles`
65+
and `podcast`; a new content section that should carry article schema must be
66+
added to the guard in `baseof.html` and the breadcrumb branch in
67+
`schema-article.html`.

0 commit comments

Comments
 (0)