Theming
Tokens are dark-first. The default :root carries the dark palette;
[data-theme="light"] is the opt-in override. Setting the attribute on
<html> flips every CSS variable — every component goes with it.
// Toggle from anywhere in your app:
import { useTheme } from '@ship-it-ui/ui';
const { theme, toggle } = useTheme();Accent#
A single --accent-h knob drives the accent in both themes. Override it on
<html> (or any ancestor) to recolor without touching components:
:root {
--accent-h: 220; /* default cyan-ish */
}
[data-brand='warm'] {
--accent-h: 30;
}ship-it.config.ts — full customization#
For everything beyond hue rotation — re-skinning specific roles, swapping the
font family, overriding light-theme accents — drop a ship-it.config.ts at
your repo root and run the shipit build-tokens CLI. The CLI emits a sparse
override stylesheet you import after globals.css.
// ship-it.config.ts
import { defineConfig } from '@ship-it-ui/tokens/config';
export default defineConfig({
accentH: 280,
color: {
dark: { panel: '#0d0f14', ok: 'oklch(0.8 0.18 145)' },
light: { accent: 'oklch(0.42 0.14 280)' },
},
typography: {
fontFamily: { sans: '"Söhne", system-ui, sans-serif' },
fontSize: { body: '14px', h1: '36px' },
},
});Run the CLI (wire it into prebuild / predev for convenience):
npx shipit build-tokens # one-shot
npx shipit build-tokens --watch # re-emit on config changes during devThen add one extra import at your app entry, AFTER globals.css:
import '@ship-it-ui/ui/styles/globals.css'; // DS defaults
import './.ship-it/tokens.css'; // your overrides (sparse)CSS variables on :root cascade by source order, so the override import
wins. Commit both ship-it.config.ts and the generated .ship-it/tokens.css
— the generated file is small (~10–30 lines) and means a fresh git clone
works without needing to run the CLI first.
The full closed key set, foot-guns (on-color foregrounds, font loading), and
output-path options are documented in
docs/customizing-tokens.md.
Per-component one-off colors#
Some components accept a color prop for situations where you legitimately
need a one-off color (a brand tier badge, a per-user avatar accent) without
adding it to global tokens:
<Badge color="#7c3aed">Premium</Badge>
<StatusDot color="oklch(0.7 0.2 280)" />
<Avatar name="Alex" color={user.brandColor} />Supported: Badge, Tag, Chip, StatusDot, Rating, Avatar. When both
color and the semantic variant (variant on Badge, state on StatusDot)
are set, color wins at runtime. Invalid colors fall back to the default
variant; dev builds log a console.warn naming the offending value.
The prop is intentionally a distinct name so it's greppable in PR review and
easy to lint against with no-restricted-syntax if your team wants to
restrict the escape hatch.