Installation
Add the package, import the global stylesheet and the fonts once, and wrap the app in ClientProvider.
Install
The package ships source components built on Base UI, which is a peer dependency alongside React.
pnpm add @fairgarden-private/design @base-ui/react
The rest comes with the package: the fonts (Fontsource), Recharts for Chart, and the Material Symbols Rounded icons, generated as per-icon SVG paths, so there is no icon font to load.
Maps
Map's vector base draws OpenStreetMap data from a Protomaps PMTiles archive with MapLibre GL JS. Its three libraries are optional peer dependencies: install them only in apps that pass pmtiles to a Map.
pnpm add maplibre-gl pmtiles @protomaps/basemaps
They load with a dynamic import when a PMTiles map mounts, so pages without one never fetch them, and MapLibre's stylesheet is not needed. Under a bundler, copy maplibre-gl-worker.mjs and maplibre-gl-shared.mjs from maplibre-gl/dist to one public directory and pass its URL as pmtiles.workerUrl. The only credit is "© OpenStreetMap", which the map draws below its frame. A drawn map (drawing) needs none of this.
Global CSS and fonts
Import both once, in the app shell. Components import their own CSS Modules; only these two are manual.
import '@fairgarden-private/design/utils/global.css'
import '@fairgarden-private/design/utils/fonts'global.cssdeclares the cascade layer order, then the reset, the tokens, the 31 Radix scales with their light and dark values, the mode blocks, the role maps and the print remap.fontsself-hosts the four families (Fraunces, Source Serif 4, Figtree and IBM Plex Mono) from Fontsource under the SIL OFL, with no font CDN. See Typography.
ClientProvider
ClientProvider gives the design system and Base UI the locale and its writing direction. Place it once, inside <html lang>. useLocale() reads it back.
import { ClientProvider } from '@fairgarden-private/design/utils/ClientProvider'
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<ClientProvider locale="en-US">{children}</ClientProvider>
</body>
</html>
)
}The page root is a paper band. Wrap regions in a Ground to change it.
Light and dark mode
The system follows the operating system by default. Mode is chosen in global CSS, never in a component (§1.11.9).
On <html> | Result |
|---|---|
No data-theme | Follows the OS: under prefers-color-scheme: dark the dark block applies to :root:not([data-theme="light"]) (screen only) |
data-theme="light" | Forces light |
data-theme="dark" | Forces dark |
Setting the attribute, and setting it before first paint, is the app shell's job. A page with no stored choice sets nothing and needs no script. Print always renders light.
Components never see the mode
There is no mode prop, and no component module contains a data-theme or prefers-color-scheme selector. Each color is one --{scale}{step} variable, such as --olive12, that the mode blocks redefine, so a component reads the same --primary12 in both modes and gets the right value.
The eight page grounds follow the mode: paper and white turn dark, and each pastel paints its step 3, very light in light mode and near-black in dark mode. The night band and the fields write their own mode through Ground: night, forest, royal and brick stay dark, and leaf, amber, clay and pink stay light, whatever the page does.
useColorScheme()reportslightordarkfor non-CSS consumers only, such as the Map's MapLibre canvas (which rebuilds its style from the role variables when the mode changes), embeds and thetheme-colormeta. Charts are SVG in role variables and need no script. No component uses it to choose colors.
Full specification: DESIGN-SYSTEM.md §1.11.9 (light and dark mode), §1.5.5 (fonts), §8.7 (charts) and §8.10 (maps); companion guide §A and §D.