Skip to content

Theme

packages/theme is a handful of small files. Import order matters and the layouts do it for you.

FileRole
fonts.cssRed Hat Text/Display/Mono, self-hosted variable woff2, full weight ranges (OFL 1.1)
tokens.scssCompiles PatternFly’s token Sass mixins: palette + default + local + charts on :root, dark tokens on :root[data-theme=dark] and .pf-v6-theme-dark. Light+dark only: ~10 KB gz
palettes.cssGenerated (D13): one block per palette under :root[data-palette=<id>] — the PatternFly palette layer re-filled plus the brand/role tokens — so every semantic token, PatternFly component, the bridge, editorial components and dark mode follow. default emits nothing
base-min.csspatternfly-common.css + patternfly-svg-icons.css + ~15 lines: box-sizing, body font, controls inherit, focus ring, PatternFly’s margin/padding reset scoped to pf-v6-c-* elements. No global heading/list resets
starlight-bridge.cssStarlight’s --sl-* slots assigned to PatternFly tokens, light-first, unlayered (Starlight’s CSS is all in @layer starlight.*, so this wins by construction); KB type: Red Hat Display headings, 16 px body, the editorial heading ramp
editorial.cssEditorial tokens (--csdr-*), .csdr-editorial type-token overrides for long-form surfaces (17 px body, 1.6 leading, heading ramp), .csdr-prose for rendered Markdown, display type, containers, breakouts
print.cssThe PDF/print layer: @page geometry and running header/footer as margin boxes (Chromium ≥131) from tokens and the per-report --csdr-print-* strings ReportLayout emits; hides chrome/TOC/actions (data-print="hide"); fragmentation contract (figures, callouts, cards never split; table heads repeat; 3-line orphans/widows); print type ramp; static Red Hat instances (../fonts/print/) swapped in via the semantic font tokens; cover + contents page for type: report; status watermark

Starlight stamps data-theme on <html>; landing/design/tools pages run theme-script.js (shares Starlight’s localStorage key). Reports are light-onlyReportLayout sets data-theme="light" and never loads the script — because SSR chart SVGs and PDFs are light.

Colour is a swappable module. Three palettes ship — default (PatternFly, unchanged), landscape (Australian landscape hues) and goap (Global Ocean Accounts Partnership) — and each is one JSON file in packages/theme/palettes/. Nothing downstream is hand-typed:

  • pnpm palette:build (scripts/palette-build.mjs) generates ramps in OKLCH onto PatternFly’s step curve, checks the step contract (step 60 ≥ 4.5:1 on white in every family; brand-50 ≥ 4.5:1 on white and on gray-10; steps 30/40 readable on the dark surface), validates every chart set (band, chroma floor, CVD ΔE, normal-vision floor, contrast; sequential monotone; diverging symmetric) and role tokens, then writes src/palettes/<id>.css, src/palettes.generated.ts (chart values for @csdr/charts) and palettes/report.json.
  • pnpm check:palette (inside pnpm check) re-runs the gate and fails on drift, so a hex can never be edited in a generated file.
  • The palette pages — /design/palette/ and /design/palette/<id>/ — show ramps, roles, chart sets and every component rendered in that palette; /design/palette/<id>/report/ is the reference report in that palette.

Choosing a palette is a build-time decision, never a reader control:

ScopeHow
Whole sitePUBLIC_CSDR_PALETTE=goap pnpm build (read in site.config.ts; the Starlight KB follows it too)
One reportpalette: landscape in the report’s frontmatter
One page/tool<SiteLayout palette="goap">

The layout stamps data-palette on <html> (the CSS hook) and sets Astro.locals.palette, which <Chart> reads so build-time SVGs, the PDF and the HTML export use the same colours; islands follow data-palette at runtime.

Adding a palette: copy a JSON, give the nine PatternFly families a hue + chroma (pin exact brand hexes with pin), optionally add extensions (e.g. brown--csdr-color--brown--*), set roles (which family is brand, and any status remap such as info: blue), supply the chart sets (categorical light/dark — validated; sequential family; diverging warm/cool; status refs), run pnpm palette:build, then look at /design/palette/<id>/. Brand-specific typography and wordmark artwork are deliberately not part of a palette; the wordmark already follows --pf-t--global--color--brand--default.

PatternFly packages are MIT; the Red Hat fonts are SIL OFL 1.1 (see packages/theme/fonts/LICENSE-OFL.txt); react-icons is MIT and includes Font Awesome Free icons (CC BY 4.0). Neither grants trademark rights: the CSDR wordmark is ours.