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 + the element-level rules PatternFly components assume: box-sizing, body font, controls inherit, focus ring, media sizing, pointer cursors, scrollbar colour — and, scoped to pf-v6-c-* elements so authored prose is untouched, margin/padding, list-style: none, table/cell borders and alignment, the mono face and PatternFly’s link decoration. Headings and full-height html/body are deliberately omitted. packages/theme/base-reset.manifest.json records the decision for every declaration in PatternFly’s base and pnpm check:base fails when an upgrade adds one that is not accounted for
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>; every other surface — landing, design, tools and reports — runs theme-script.js, which shares Starlight’s localStorage key, and carries the masthead theme control (@csdr/chrome/ThemeToggle.astro: auto → light → dark). Reports were light-only until 2026-08-19, when charts gained a dark render; their PDF, HTML export and deck are still always light.

Colour is a swappable module. Three palettes ship — goap (Global Ocean Accounts Partnership — the site default, set in site.config.ts), landscape (Australian landscape hues) and default (PatternFly, unchanged) — 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 — and info-50, since PatternFly’s status--info--default is step 50 with white text on it — ≥ 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
Dark modeEvery surface follows the reader: the masthead control (auto → light → dark) writes Starlight’s starlight-theme key, so a choice made on a report and one made in the knowledge base are the same choice. Reports included — <Chart mode="auto"> (the default) renders a second SSR SVG in the palette’s dark set and CSS shows one per data-theme. The PDF, the HTML export and the deck are always light: they strip the dark render and print with colorScheme: 'light'
Whole sitePUBLIC_CSDR_PALETTE=landscape pnpm build (read in site.config.ts, whose fallback is goap; 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.