Strata tokens
Source of truth:
packages/ui/src/css/theme.css(raw values,:root+.dark) andpackages/ui/src/css/tailwind.css(token → Tailwind utility wiring, radius ramp, type scale, base document styles). Edit those files; every consumer picks the change up automatically. The workbench (apps/strata) renders the live palette in both themes.
Palette model
- Semantic tokens only. Components consume
--background,--foreground,--card,--muted,--accent,--border,--ring,--sidebar-*,--chart-1..5, etc. via the Tailwind utilities (bg-background,text-muted-foreground,border-border). Never raw palette utilities. - Dark mode is class-based
.dark, overrides live in the sametheme.css. To tweak a dark-mode color, edit the.darkblock — one edit propagates to web-v2, marketing, and the docs sites. The dark surface model is two-level stacking: base level (background/card/popover) over a deeper backdrop (secondaryis the darkest plane);secondarymust always sit darker thanbackgroundin dark mode. - Neutral grays carry a +2 blue channel (cool/slate tinge) in both themes. Keep that when adding grays.
--inkis the fixed brand near-black (rgb(0 0 2), same in both themes) for chrome like the nav island and footer.
Brand palette
Four fixed pastels, identical in light and dark, exposed as
text-/bg-brand-{purple,blue,green,peach}:
--brand-purplergb(198 172 226)— also aliased as--brand(text-brand/bg-brand), the primary accent. A subtree may override--brandlocally (section accent bands) and every--brand-backed utility andcolor-mix()follows.--brand-blue/--brand-green/--brand-peach— the carousel accents.--brand-green-successrgb(73 200 95)— saturated functional green for money/positive signals only.
JS mirror: components that need literal values (inline styles, SVG
attributes) read apps/marketing/src/lib/palette.ts. If you change a
--brand-* value in theme.css, update the mirror in the same commit.
Radius
Everything derives from the single --radius token (0.625rem) via the ramp
--radius-sm … --radius-4xl in tailwind.css. Never write a bare
var(--radius-*) that isn’t in the ramp, and never invent an arbitrary
radius value. Squircle corners (corner-shape: squircle) are authored ~2x
the visual target and MUST ship a half-value fallback behind an @supports
gate — never inline-style a squircle radius.
Type scale
The Layers scale is denser than Tailwind’s defaults — shared components are authored against it and render ~2px oversized without it:
| Utility | Size |
|---|---|
text-xxs | 10px |
text-xs | 11px |
text-sm | 12px |
text-base | 14px (body default) |
text-lg | 16px |
text-xl | 20px |
The one 16px input exception is marketing-only: the marketing hero
composer uses literal text-[16px] because mobile Safari auto-zooms any
focused input under 16px and prospective signups must not hit that; the hero
context also warrants the display break. In-app inputs stay on the scale.
Fonts: --font-sans = Inter Variable (via @fontsource-variable/inter);
--font-mono = Berkeley Mono (self-hosted per app); --font-heading = the
sans. Base document styles (14px body, antialiasing, the theme-aware thin
scrollbars) come from tailwind.css’s @layer base — don’t re-declare them
per app.
Misc tokens
--noise-url (hosted noise SVG), --logo-color (the logo lime,
rgb(181 255 107)), --success-background, --dot-grid (the dot-grid
backdrop fill; deliberately fainter in dark mode).
Rules
- Grep
packages/ui/src/css/theme.cssbefore writing anyvar(--x)— if the token doesn’t exist there (or in the consumer’s own app layer), don’t use it. - Never redefine a Strata token in an app stylesheet. App-specific tokens
(e.g. marketing’s
--container-marketing) live in the app’s own@theme inlineblock under a distinct name. - Never invent arbitrary CSS values (heights, offsets, spacing) — ask for the correct value if a design doesn’t specify one.