ZEROlobby
22/22 strict⌘Kvisitor
skip to main
ADR-026
skin
——:——:——zcovenant · audit
theme system\u00b7 chapter 32 \u00b7 4 providers \u00b7 4 cross-cutting contracts

four providers \u00b7 one cascade \u00b7 every primitive participates

substrate. motion. density. locale. four cross-cutting brand contracts that every primitive in @zero/ui reads from. before this pack the contracts lived as CSS conventions; now they live as React Context, with a display: contentswrapper that adds zero layout cost. consumers compose the cascade once at the app root; descendant primitives inherit the tier without any further wiring.

00 · livepick any two skins \u00b7 same primitives render side by side \u00b7 the diff strip lists every role that changes

A/B the brand

skin · A/B · live· compare any two of the six skins · same primitives · the diff strip lists every role that changedsignaturebloomberg4 role deltas
left
right
signature · brand-canonical · amber-led · the wordmark variant
default
paper · 24h
+1.42%
decisions
6,143
refused
5,879
acidambergreenbluereddefault
trade receipt#7b1b2e51
agent
sera
market
sol-perp
side
short
pnl
+$1,420 · +1.42%

sample · sparkline

acid · area

source · syntheticsnapshot

sample · depth bar

bids vs asks

99.9520.0bp99.8512.00099.808.00099.7514.00099.704.000100.0510.000100.107.000100.1511.000100.205.000
source · syntheticsnapshot
bloomberg · inheritance-canonical · amber-led · capital-markets parent body
paper · 24h
+1.42%
decisions
6,143
refused
5,879
acidambergreenbluereddefault
trade receipt#7b1b2e51
agent
sera
market
sol-perp
side
short
pnl
+$1,420 · +1.42%

sample · sparkline

acid · area

source · syntheticsnapshot

sample · depth bar

bids vs asks

99.9520.0bp99.8512.00099.808.00099.7514.00099.704.000100.0510.000100.107.000100.1511.000100.205.000
source · syntheticsnapshot
role diff · 4 of 8every role whose oklch value changes between the two skins
  • brand-signaloklch(0.752 0.122 81.2)oklch(0.78 0.135 75)
  • figureoklch(0.861 0.0349 84.58)oklch(0.8507 0.0668 73.42)
  • groundoklch(0.0706 0.0145 82.32)oklch(0.0422 0.0173 29.23)
  • warnoklch(0.78 0.135 75)oklch(0.78 0.135 38)
01 · four providersThemeProvider \u00b7 MotionProvider \u00b7 DensityProvider \u00b7 LocaleProvider

the four cross-cutting React-tree contracts

plate · A · provider matrixall SSR-clean
metric
theme-system
unit
providers
window
static
agg
identity
source
packages/ui/src/{Theme,Motion,Density,Locale}Provider.tsx
fresh
last build
#providercontext shapewrapper attributedefault
72ThemeProvider{ skin, substrate: 'dark' }data-skin \u00b7 data-substratesignature \u00b7 dark
73MotionProvider{ tier, reducedMotion }data-motionfull
74DensityProvider{ density }data-densitycozy
75LocaleProvider{ locale, direction }lang \u00b7 diren \u00b7 ltr
why providers, not CSS conventions alone
a CSS variable cascade gives descendants the value but no React-level access. consumers that want to read the tier in a hook (e.g., a chart that adapts its tick density to the density tier, or a date input that formats per locale) need a context. shipping the four together \u00b7 one wrapper, one hook \u00b7 means every consumer reaches them by typing.
02 · cascade compositioncanonical mounting order

the four-deep wrap pattern

plate · B · recommended app-root compositionopt-in
metric
theme-system
unit
providers
window
static
agg
identity
source
book/app/theme/page.tsx
fresh
last build
apps/<consumer>/app/layout.tsxtsx
<ThemeProvider skin="signature">  <MotionProvider tier="full">    <DensityProvider density="cozy">      <LocaleProvider locale="en" direction="ltr">        <App />      </LocaleProvider>    </DensityProvider>  </MotionProvider></ThemeProvider>
plate · C · reading the contract from a client componentfour hooks
metric
theme-system
unit
providers
window
static
agg
identity
source
packages/ui/src/{Theme,Motion,Density,Locale}Provider.tsx
fresh
last build
MyComponent.tsxtsx
'use client';import { useTheme, useMotion, useDensity, useLocale } from '@zero/ui';import { chapterMetadata } from '../../lib/meta'; function MyComponent() {  const { skin } = useTheme();  const { reducedMotion } = useMotion();  const { density } = useDensity();  const { locale, direction } = useLocale();  return <span data-skin={skin}>{locale}</span>;}
order matters only for the cascade
for React Context, each provider is independent \u00b7 the order you nest is irrelevant. for the CSS variable cascade, theme must exist before motion can pivot off it. the brand book's root layout mounts none of these by default. chapter 32 is the contract reference; consumer apps compose the stack at their root.
03 · density grammarcompact \u00b7 cozy \u00b7 comfortable

one provider \u00b7 three table densities

plate · D · DataTable @ density='compact'--row-h: 28 px
metric
theme-system
unit
providers
window
static
agg
identity
source
packages/ui/src/DensityProvider.tsx
fresh
last build
paper trades \u00b7 compact
timeassetp/l
14:22sol-perp+82.30
14:24eth-perp-14.10
14:39btc-perp+41.40
plate · E · DataTable @ density='cozy' (default)--row-h: 32 px
metric
theme-system
unit
providers
window
static
agg
identity
source
packages/ui/src/DensityProvider.tsx
fresh
last build
paper trades \u00b7 cozy
timeassetp/l
14:22sol-perp+82.30
14:24eth-perp-14.10
14:39btc-perp+41.40
plate · F · DataTable @ density='comfortable'--row-h: 40 px
metric
theme-system
unit
providers
window
static
agg
identity
source
packages/ui/src/DensityProvider.tsx
fresh
last build
paper trades \u00b7 comfortable
timeassetp/l
14:22sol-perp+82.30
14:24eth-perp-14.10
14:39btc-perp+41.40
why three stops, not five
the tri-stop grammar covers every consumer surface in cockpit \u00b7 league \u00b7 coliseum \u00b7 studio. five stops (xs/sm/md/lg/xl) doubles the CSS variable surface without unblocking any concrete request. the local DataTable declarations from ADR-020 remain as fallbacks; the provider's pivots win via inheritance when both are mounted.
04 · motion tiersfull \u00b7 reduced \u00b7 off

one provider \u00b7 three motion tiers

plate · G · motion tier \u00b7 cascade pivots3 vars
metric
theme-system
unit
providers
window
static
agg
identity
source
packages/ui/src/MotionProvider.tsx
fresh
last build
tier--ease-step--breath--pulseuse case
fullcubic-bezier(0.32, 0.72, 0, 1)11default \u00b7 the four-clock organism runs
reducedlinear01respects prefers-reduced-motion
offlinear00print substrate \u00b7 accessibility-first embeds
plate · H · Skeleton @ tier='reduced' (breath frozen)--breath: 0
metric
theme-system
unit
providers
window
static
agg
identity
source
packages/ui/src/MotionProvider.tsx
fresh
last build
motion is not optional chrome
the four-clock organism (chapter 03) is how the brand breathes. shutting it off changes how a primitive feels, not just how it looks. reach for tier='reduced'when the consumer has set prefers-reduced-motion; reach for tier='off' only for print, embeds, and accessibility-first surfaces where motion has been explicitly opted out.
05 · locale contractlang \u00b7 dir \u00b7 useLocale()

one provider \u00b7 two locales \u00b7 one receipt

plate · I · Receipt @ locale='en' (LTR)lang=en \u00b7 dir=ltr
metric
theme-system
unit
providers
window
static
agg
identity
source
packages/ui/src/LocaleProvider.tsx
fresh
last build
╭── TRADE RECEIPT ──────╮
│ asset    · sol-perp
│ side     · long
│ size     · 12.000
│ price    · $248.40
│ ts       · 14:22:08Z
│ hash     · 0x7a4f
╰───────────────────────╯
plate · J · Receipt @ locale='es' (LTR)lang=es \u00b7 dir=ltr
metric
theme-system
unit
providers
window
static
agg
identity
source
packages/ui/src/LocaleProvider.tsx
fresh
last build
╭── TRADE RECEIPT ──────╮
│ asset    · sol-perp
│ side     · long
│ size     · 12.000
│ price    · $248.40
│ ts       · 14:22:08Z
│ hash     · 0x7a4f
╰───────────────────────╯
LocaleProvider does not translate
translating ZERO into a second locale is a copy-pipeline concern: edit the strings in brand/voice/lexicon-*and grade with rewriteIn from @zero/brand-runtime. the LocaleProvider just exposes which locale to format numbers, dates, and day-of-week against. rendering the sameReceipt at en versus es changeslang and dir on the wrapper; downstream format helpers key off those attributes.