Context
scripts/generate-styleguide.mjs snapshots the live /dev/gallery into the static docs/component-library/index.html by inlining every stylesheet the dev page links. Two problems result (introduced/surfaced in PR #130, which made the script the single source of truth for the styleguide):
- It's huge. The current
docs/component-library/index.html is ~608 KiB (621,200 bytes). The bulk is Tailwind's unpurged dev stylesheet — dev serves the full utility set, none of which is tree-shaken to what the gallery actually uses.
- It's not offline.
next/font/google (Inter + Noto_Serif, see src/app/layout.tsx) emits @font-face blocks pointing at /_next/static/media/*.woff2. The script's absolutizeCssUrls() rewrites those root-relative paths to http://127.0.0.1:3000/_next/... (the dev server). The generated file currently has 73 @font-face blocks / 23 distinct woff2 files / 75 url(...) refs all pointing at 127.0.0.1:3000. Open the file over file:// with the dev server down and the fonts 404 — the page silently falls back to system-ui / serif. The script's own comment acknowledges this ("when [the server] isn't [up], the CSS already declares system-ui / serif fallbacks").
So the "self-contained, opens over file:// with no network" promise in the script header is only half-true today: structure + utility CSS render, but the design system's actual fonts only load while npm run dev is running.
Files involved
scripts/generate-styleguide.mjs
absolutizeCssUrls() — currently rewrites font url(...) to the dev server; this is the offline-break.
main() — the stylesheet-collection loop (for (const href of sheetHrefs)) that inlines each linked sheet verbatim.
buildDocument() — assembles ${inlinedCss} into <style>.
src/app/layout.tsx — next/font/google Inter + Noto_Serif source.
tailwind.config.ts / postcss.config.mjs — Tailwind 3.4 config; a purged/production build keys off these content globs.
- Output:
docs/component-library/index.html.
package.json — styleguide:generate script (and any new build step).
Proposed approach
Two independent wins; either can land alone, but do both for the acceptance criteria.
A. Self-host / inline the fonts (offline + faithful). In absolutizeCssUrls() (or a follow-up pass over inlinedCss), instead of pointing _next/static/media/*.woff2 at the dev server, fetch each woff2 over the running dev server at generate time and rewrite the url(...) to a data:font/woff2;base64,... URI. There are only ~23 distinct files; dedupe and embed. The fonts then travel inside the HTML and load over file:// with nothing running.
B. Shrink the CSS (leaner). Replace the verbatim inlining of the full dev Tailwind sheet with CSS scoped to what the gallery uses. Options, easiest first:
- Run a production/purged Tailwind build (Tailwind 3.4 already purges by
content globs) and inline that output instead of the dev sheet; or
- Post-filter the captured CSS to selectors actually present in the captured
bodyHtml (e.g. a purgecss/@fullhuman pass keyed on the snapshot DOM); or
- Have the script capture
document.styleSheets rules and drop unmatched selectors.
Keep the existing globals/token CSS and the .sg-gen-* chrome untouched — only the Tailwind utility layer needs purging.
Note the data-URI fonts (A) will add weight; the net size win comes from purging (B). Both are needed: B makes it small, A makes it offline.
Acceptance criteria
Auto-filed follow-up from the component-library completion (Draft PR #130). The library itself is gate-green; these are tracked enhancements/cleanups, not blockers.
Context
scripts/generate-styleguide.mjssnapshots the live/dev/galleryinto the staticdocs/component-library/index.htmlby inlining every stylesheet the dev page links. Two problems result (introduced/surfaced in PR #130, which made the script the single source of truth for the styleguide):docs/component-library/index.htmlis ~608 KiB (621,200 bytes). The bulk is Tailwind's unpurged dev stylesheet — dev serves the full utility set, none of which is tree-shaken to what the gallery actually uses.next/font/google(Inter + Noto_Serif, seesrc/app/layout.tsx) emits@font-faceblocks pointing at/_next/static/media/*.woff2. The script'sabsolutizeCssUrls()rewrites those root-relative paths tohttp://127.0.0.1:3000/_next/...(the dev server). The generated file currently has 73@font-faceblocks / 23 distinct woff2 files / 75url(...)refs all pointing at127.0.0.1:3000. Open the file overfile://with the dev server down and the fonts 404 — the page silently falls back to system-ui / serif. The script's own comment acknowledges this ("when [the server] isn't [up], the CSS already declares system-ui / serif fallbacks").So the "self-contained, opens over file:// with no network" promise in the script header is only half-true today: structure + utility CSS render, but the design system's actual fonts only load while
npm run devis running.Files involved
scripts/generate-styleguide.mjsabsolutizeCssUrls()— currently rewrites fonturl(...)to the dev server; this is the offline-break.main()— the stylesheet-collection loop (for (const href of sheetHrefs)) that inlines each linked sheet verbatim.buildDocument()— assembles${inlinedCss}into<style>.src/app/layout.tsx—next/font/googleInter + Noto_Serif source.tailwind.config.ts/postcss.config.mjs— Tailwind 3.4 config; a purged/production build keys off thesecontentglobs.docs/component-library/index.html.package.json—styleguide:generatescript (and any new build step).Proposed approach
Two independent wins; either can land alone, but do both for the acceptance criteria.
A. Self-host / inline the fonts (offline + faithful). In
absolutizeCssUrls()(or a follow-up pass overinlinedCss), instead of pointing_next/static/media/*.woff2at the dev server, fetch each woff2 over the running dev server at generate time and rewrite theurl(...)to adata:font/woff2;base64,...URI. There are only ~23 distinct files; dedupe and embed. The fonts then travel inside the HTML and load overfile://with nothing running.B. Shrink the CSS (leaner). Replace the verbatim inlining of the full dev Tailwind sheet with CSS scoped to what the gallery uses. Options, easiest first:
contentglobs) and inline that output instead of the dev sheet; orbodyHtml(e.g. a purgecss/@fullhumanpass keyed on the snapshot DOM); ordocument.styleSheetsrules and drop unmatched selectors.Keep the existing globals/token CSS and the
.sg-gen-*chrome untouched — only the Tailwind utility layer needs purging.Note the data-URI fonts (A) will add weight; the net size win comes from purging (B). Both are needed: B makes it small, A makes it offline.
Acceptance criteria
docs/component-library/index.htmloverfile://renders with the correct fonts (Inter headings/body + Noto Serif where used) and the full design system styling intact — no system-ui/serif fallback, no console 404s for_next/static/media/*.url(...)in the generated file points at127.0.0.1/localhost/:3000(grep returns 0); font refs aredata:URIs (or repo-local self-hosted paths shipped alongside the HTML).index.htmlis substantially smaller than the current ~608 KiB (purged utility CSS; suggest a soft target well under ~250 KiB, font payload aside).npm run styleguide:generatestill succeeds against a running dev server.Auto-filed follow-up from the component-library completion (Draft PR #130). The library itself is gate-green; these are tracked enhancements/cleanups, not blockers.