Skip to content

Make the generated component-library styleguide leaner and fully offline (purge CSS + inline fonts) #135

@hb-agent

Description

@hb-agent

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):

  1. 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.
  2. 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.tsxnext/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.jsonstyleguide: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

  • With no dev server running, opening docs/component-library/index.html over file:// 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/*.
  • No url(...) in the generated file points at 127.0.0.1/localhost/:3000 (grep returns 0); font refs are data: URIs (or repo-local self-hosted paths shipped alongside the HTML).
  • Generated index.html is substantially smaller than the current ~608 KiB (purged utility CSS; suggest a soft target well under ~250 KiB, font payload aside).
  • Light/dark toggle and all gallery surfaces still render correctly; npm run styleguide:generate still succeeds against a running dev server.
  • The "GENERATED — do not edit by hand" header and single-source-of-truth flow are preserved.

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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions