Theme
packages/theme is a handful of small files. Import order matters and the layouts do it for you.
| File | Role |
|---|---|
fonts.css | Red Hat Text/Display/Mono, self-hosted variable woff2, full weight ranges (OFL 1.1) |
tokens.scss | Compiles 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.css | Generated (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.css | patternfly-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.css | Starlight’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.css | Editorial 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.css | The 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 |
Dark mode
Section titled “Dark mode”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.
Palettes (D13)
Section titled “Palettes (D13)”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’sstatus--info--defaultis 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 writessrc/palettes/<id>.css,src/palettes.generated.ts(chart values for@csdr/charts) andpalettes/report.json.pnpm check:palette(insidepnpm 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:
| Scope | How |
|---|---|
| Dark mode | Every 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 site | PUBLIC_CSDR_PALETTE=landscape pnpm build (read in site.config.ts, whose fallback is goap; the Starlight KB follows it too) |
| One report | palette: 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.
Licences
Section titled “Licences”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.