The complete catalog of design tokens declared in src/styles/globals.css. Every color, radius, shadow, font, font size, spacing value, and z-index used by the admin / editor / UI primitives is here. CSS Modules in src/admin/, src/admin/pages/site/, and src/ui/ MUST reference these via var(--*) — hardcoded hex / rgb / hsl is gated by css-token-policy.test.ts, hardcoded font-size pixels are gated by admin-typography-token-policy.test.ts, and hardcoded margin / padding / gap pixels are gated by admin-spacing-token-policy.test.ts.
- One file:
src/styles/globals.css. - Grouped by role: fonts/fluid type, fluid spacing, core surfaces/text/borders, overlays/scrims, identity accents, semantic state, radii, shadows, canvas, syntax, and z-index.
- Two-layer color model: achromatic base + semantic / categorical color layer on top. See docs/design.md for the rationale.
- Add a new token by editing
globals.css. Reference it withvar(--your-token)in CSS Modules. - The
src/modules/directory is exempt from the no-hardcoded-color rule — module CSS ships to published pages where admin tokens are not guaranteed to exist.
--font-sans: "Inter Variable", system-ui, sans-serif;
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;Token names: --font-sans, --font-mono.
Inter Variable is loaded via a targeted @font-face declaration in globals.css — Latin subset only (unicode-range: U+0000-00FF, …), with font-display: swap. Non-Latin characters fall back to system-ui. The monospace stack is entirely system-native; no custom monospace font is loaded.
If the admin UI needs broader character coverage, add a second @font-face block in globals.css with the appropriate unicode-range pointing at the matching subset file under @fontsource-variable/inter/files/.
Admin UI font sizes use the Core Framework-style --text-* scale directly. These tokens are fluid across viewport width but intentionally narrow for dense admin chrome. Use the nearest semantic step instead of introducing one-off pixel sizes.
Settings → Preferences can remap the scale through data-editor-text-scale on the document and layout roots. The small, large, and extra-large options shift the same token names down or up the base scale, so component CSS continues to say font-size: var(--text-s) rather than branching per preference.
Each visible token has a stable base token used by those preference remaps: --text-3xs-base, --text-2xs-base, --text-xs-base, --text-s-base, --text-m-base, --text-l-base, --text-xl-base, --text-2xl-base, --text-3xl-base, --text-4xl-base, --text-5xl-base, --text-6xl-base, --text-7xl-base.
| Token | Fluid range | Typical use |
|---|---|---|
--text-3xs |
8px → 9px | Tiny badges, axis labels, micro metadata |
--text-2xs |
9px → 10px | Dense metadata, compact chips |
--text-xs |
10px → 11px | Panel labels, toolbar chrome, descriptions |
--text-s |
11px → 12px | Default compact body text, inputs, buttons |
--text-m |
12px → 13px | Card/body copy and readable secondary text |
--text-l |
13px → 14px | Section titles and prominent labels |
--text-xl |
14px → 16px | Dialog titles and compact page titles |
--text-2xl |
16px → 18px | Page headings |
--text-3xl |
18px → 20px | Large page headings |
--text-4xl |
20px → 24px | Hero numbers / large empty states |
--text-5xl |
24px → 28px | Display headings |
--text-6xl |
32px → 40px | KPI values |
--text-7xl |
40px → 56px | Largest admin display values |
These are admin tokens. The published-site Framework engine also emits short names such as --text-s; those belong to the generated site CSS, not admin chrome. Editor chrome injected into the canvas iframe maps admin sizes to --chrome-text-* before using them so it cannot override the site's Framework typography.
Admin UI spacing uses the Core Framework-style --space-* scale directly. Use these tokens for CSS-authored margin, padding, gap, row-gap, column-gap, and SVG width/height values in admin and shared UI CSS modules. Keep true hairlines on --space-px.
| Token | Fluid range | Typical use |
|---|---|---|
--space-px |
1px fixed | Hairline gaps and exact 1px spacing |
--space-4xs |
2px → 3px | Micro offsets, dense icon nudges |
--space-3xs |
3px → 4px | Very tight inline padding |
--space-2xs |
4px → 5px | Tight chip gaps |
--space-xs |
5px → 6px | Compact control gaps |
--space-s |
6px → 8px | Default dense row gaps |
--space-m |
8px → 10px | Default panel/control spacing |
--space-l |
10px → 12px | Section row padding |
--space-xl |
12px → 14px | Dialog/control interior padding |
--space-2xl |
14px → 16px | Larger panel gaps |
--space-3xl |
16px → 18px | Icon-size SVGs and roomy control gaps |
--space-4xl |
18px → 20px | Compact empty-state spacing |
--space-5xl |
20px → 24px | Card and modal spacing |
--space-6xl |
24px → 28px | Large controls / icon tiles |
--space-7xl |
28px → 32px | Page-section rhythm |
--space-8xl |
32px → 40px | Large page body spacing |
--space-9xl |
40px → 48px | Prominent empty-state spacing |
--space-10xl |
48px → 56px | Large dashboard/display spacing |
--space-11xl |
56px → 72px | Large SVG previews and major vertical gaps |
--space-12xl |
84px → 108px | Largest admin display spacing |
These are admin tokens. The published-site Framework engine also emits short names such as --space-s; those belong to the generated site CSS, not admin chrome. Editor chrome injected into the canvas iframe maps admin spacing to --chrome-space-* before using it so it cannot override the site's Framework spacing.
Six surface tones for depth. Lighter is higher in the stack.
The default values below are the dark theme. data-editor-theme="light" on the document or layout root overrides the same token names in globals.css, so component CSS stays theme-agnostic.
| Token | Hex | Use |
|---|---|---|
--bg-body |
#000000 |
Page bottom — root, behind everything |
--bg-surface |
#1b1b1b |
Darker parent of tile cards / sidebar fill |
--bg-surface-2 |
#282828 |
Tile cards themselves, panel bodies |
--bg-surface-3 |
#323232 |
Tile/card hover, nested structural surfaces |
--bg-surface-4 |
#4a4a4a |
Stronger nested structural surface |
--bg-surface-5 |
#605f5f |
Strongest nested structural surface |
Tile/card hovers change tone, not border. Compact interactive chrome — buttons, segmented controls, toolbar chips, selected rows, and pressed states — uses --overlay-5/10/20/30 for state fills so light and dark themes keep the same visible hierarchy.
| Token | Hex | Use |
|---|---|---|
--border |
#333333 |
Default 1px borders on input controls, dividers |
--border-muted |
#2e2e2e |
Slightly elevated borders (active states on bordered controls) |
--border-subtle |
#1f1f1f |
Quiet dividers and low-emphasis panel edges |
--border-strong |
#4a4a4a |
Stronger structural borders and high-emphasis separators |
Five tones, each a different meaning level.
| Token | Hex | Means |
|---|---|---|
--text-bright |
#f4f4f5 |
Titles, headings, KPIs |
--text |
#ededed |
Primary body text |
--text-muted |
#a1a1aa |
Labels, secondary UI |
--text-subtle |
#787878 |
Muted / placeholder |
--text-disabled |
#52525b |
Disabled / very subtle |
Add a new text tone only by adding a new token here.
Scrollbars are admin chrome, not identity. Keep them achromatic so the panel rail remains the dominant colored vertical affordance.
| Token | Value / source | Use |
|---|---|---|
--scrollbar-size |
6px |
WebKit/Blink scrollbar width and height |
--scrollbar-radius |
var(--radius-sm) |
Thumb radius |
--scrollbar-track |
transparent |
Track and corner background |
--scrollbar-thumb |
color-mix(in srgb, var(--text-subtle) 58%, transparent) |
Default thumb color, also Firefox thumb color |
--scrollbar-thumb-hover |
color-mix(in srgb, var(--text-muted) 72%, transparent) |
Hover thumb color |
globals.css applies these through scrollbar-color for Firefox and ::-webkit-scrollbar for WebKit/Blink. Scrollable panel layouts that place rail navigation beside content should use scrollbar-gutter: stable.
Foreground overlays are used for selected rows, pressed states, borders, compact control tracks, and subtle surface lifts. The dark theme uses white overlays; the light theme swaps the same token names to dark overlays. Black scrims are used for shadows and modal backdrops. Suffixes are alpha x 100.
Overlay tokens: --overlay, --overlay-5, --overlay-10, --overlay-20, --overlay-30, --overlay-40, --overlay-50, --overlay-60, --overlay-70, --overlay-80, --overlay-90.
Scrim tokens: --scrim, --scrim-10, --scrim-20, --scrim-30, --scrim-40, --scrim-50, --scrim-60, --scrim-70, --scrim-80, --scrim-90.
Token-backed identity colors for widget categories, panel rails, sidebar icons, tag pills, and storage breakdown segments. Panel rails assign these automatically from the full panel identity with repeat avoidance inside the visible rail group.
| Token | Hex | Conventional category |
|---|---|---|
--accent-1 |
#8ee6c8 |
"Saved / system / status" |
--accent-2 |
#c8b6ff |
"Pages / structure" |
--accent-3 |
#9bdcff |
"Storage / data / configuration" |
--accent-4 |
#ffc7a8 |
"Posts / media / activity" |
--accent-5 |
#ffb6cd |
Secondary warm identity tint |
--accent-6 |
#b8f28b |
Secondary green identity tint |
--accent-7 |
#f7df72 |
Secondary yellow identity tint |
--accent-8 |
#83e7ff |
Secondary blue identity tint |
--accent-9 |
#f0a6ff |
Secondary violet identity tint |
--accent-10 |
#ff9f9f |
Secondary red identity tint |
Each accent also has a standard 10% tint for soft backgrounds: --accent-1-10, --accent-2-10, --accent-3-10, --accent-4-10, --accent-5-10, --accent-6-10, --accent-7-10, --accent-8-10, --accent-9-10, --accent-10-10. Used by Widget (tint prop), the rail accent helper (src/ui/railAccent.ts), TagPill, and the storage breakdown chart. The light theme overrides the same accent token names with darker foreground-safe variants, so shared rail icons and badge text stay readable on white surfaces. Adding another identity color requires a new token — don't inline a color.
TagPill maps the first meaningful alphanumeric character of its label to a
stable numbered accent. This keeps selector punctuation from driving the color
while giving class names, HTML tags, and badges enough visual variety without
creating a second tag-specific tint scale.
| Token | Value | Use |
|---|---|---|
--danger |
#ef4444 |
Error fill (button, badge, border) |
--danger-light |
#f87171 |
Lighter danger for text |
--danger-lighter |
#fca5a5 |
Very light danger tint |
--danger-text |
#fecaca |
Danger badge text |
--danger-5 |
rgba(239, 68, 68, 0.05) |
Very quiet danger wash |
--danger-10 |
rgba(239, 68, 68, 0.1) |
Danger pill / message surface |
--danger-20 |
rgba(239, 68, 68, 0.2) |
Danger surface border |
--danger-30 |
rgba(239, 68, 68, 0.3) |
Stronger danger border / fill |
--danger-40 |
rgba(239, 68, 68, 0.4) |
Highest danger alpha fill |
| Token | Value | Use |
|---|---|---|
--warning |
#f59e0b |
Warning fill |
--warning-text |
#fde68a |
Warning badge text |
--warning-10 |
rgba(245, 158, 11, 0.1) |
Warning surface |
--warning-20 |
rgba(245, 158, 11, 0.2) |
Warning border / stronger wash |
--warning-30 |
rgba(245, 158, 11, 0.3) |
Warning surface border |
| Token | Value | Use |
|---|---|---|
--success |
#34d399 |
Saved / OK indicator |
--success-bright |
#4ade80 |
Agent tool success |
--success-text |
#d1fae5 |
Success badge text |
--success-text-muted |
#a7f3d0 |
Success hint text |
--success-10 |
rgba(52, 211, 153, 0.1) |
Success pill surface |
--success-20 |
rgba(52, 211, 153, 0.2) |
Success wash / match highlight |
--success-30 |
rgba(52, 211, 153, 0.3) |
Success pill / alert border |
--success-40 |
rgba(52, 211, 153, 0.4) |
Highest success alpha fill |
| Token | Value | Use |
|---|---|---|
--info-text |
#c4b5fd |
Violet-tinted info text |
| Token | Use |
|---|---|
--canvas-chrome-shadow |
Achromatic drop shadow for canvas chrome overlays |
--canvas-selection-ring |
Neon green inset ring for the selected node |
--canvas-hover-ring |
Neon pink inset ring for the hovered node |
--canvas-selector-ring |
Neon orange inset ring for selector-panel match sweeps |
--canvas-selection-ring-color |
Bare selection colour for outline / border / custom shadows |
--canvas-hover-ring-color |
Bare hover colour for outline / border / custom shadows |
--canvas-selector-ring-color |
Bare selector-affinity colour for custom shadows |
--tree-drop-line |
Drag-and-drop insertion line in tree views |
--tree-drop-line-shadow |
Glow behind the tree insertion line |
--tree-drop-inside-bg |
Valid drop-inside row background |
--tree-drop-inside-border |
Valid drop-inside row border |
--tree-drop-invalid-bg |
Invalid drop row background |
--tree-drop-invalid-border |
Invalid drop row border |
--canvas-placeholder-bg |
Shared diagonal-stripe empty-module placeholder background |
--canvas-live-scrim |
Live-preview overlay scrim, theme-adjusted |
Bare colour variants (*-ring-color) are for surfaces that need an outline / border-color / custom shadow geometry rather than the full inset-ring shorthand. Keep them in sync with the shorthands. The selector ring (#ff8800) is a third distinct identity so a "show me everything this selector touches" sweep reads differently from selection (green) and hover (pink).
--canvas-placeholder-bg:
repeating-linear-gradient(
-45deg,
color-mix(in srgb, var(--text-subtle) 6%, transparent) 0 6px,
transparent 6px 12px
),
color-mix(in srgb, var(--text-subtle) 4%, transparent);The shared diagonal-stripe pattern used by every module's "empty" placeholder (image, video, container, slot-outlet, VC ref placeholders). Strictly achromatic. Edit once, retune everywhere.
Used inside CodeMirror only. Don't reach for these in editor chrome.
| Token | Hex | Use |
|---|---|---|
--syntax-keyword |
#ff7b72 |
Keywords (if, return) |
--syntax-entity |
#d2a8ff |
Function names, types |
--syntax-property |
#7ee787 |
Object properties |
--syntax-variable |
#ffa657 |
Variables |
--syntax-string |
#a5d6ff |
String literals |
--syntax-constant |
#79c0ff |
Numbers, constants |
--syntax-comment |
#8b949e |
Comments |
--syntax-operator |
#c9d1d9 |
Operators, punctuation |
--syntax-invalid |
#ffa198 |
Invalid syntax |
| Token | Value | Use |
|---|---|---|
--radius-sm |
3px | Tight chips, micro-badges, segmented-control inner indicator |
--radius |
6px | Default editor controls, toolbar buttons, ghost menu items |
--panel-radius |
12px | Floating overlay panels |
--card-radius |
16px | Borderless tile cards (Widget, dashboard cells, module inserter tiles) |
--input-radius |
1em | Pill-shaped inputs, class / property chips |
--tooltip-radius |
6px | Tooltips |
| Token | Use |
|---|---|
--focus-ring |
0 0 0 1px var(--overlay-20) — 1px achromatic focus ring |
--panel-blur |
Backdrop blur amount for floating overlay panels |
--shadow-panel |
Composite for floating panels: bottom-inset shadow + drop shadow |
--shadow-panel-inset-bottom |
Sub-token: inset 0 -1px 0 var(--scrim-40) |
--shadow-panel-drop |
Sub-token: 0 12px 40px rgba(0, 0, 0, 0.65) |
--shadow-input-focus |
Inset composite achromatic glow for focused inputs |
--shadow-tooltip |
0 4px 16px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.04) |
Use --shadow-panel directly when you need a floating-panel feel. Don't recompose from the sub-tokens.
background: var(--bg-surface);
border: 1px solid var(--overlay-10);
border-radius: var(--panel-radius);
backdrop-filter: blur(var(--panel-blur));
box-shadow: var(--shadow-panel);Used by spotlight, popovers, modals — anything that floats above the editor with a blur backdrop.
background: var(--scrim-30);Used by the Code primitive for inline code chip backgrounds. Don't reuse for block-level code editors (that's CodeMirror, which uses the --syntax-* tokens).
A sculpted, top-lit dark mechanical keycap. The face is a vertical gradient; the box-shadow stack fakes a raised cap — top highlight, concave inner shadow, a solid bottom edge, and a soft drop shadow. All values are achromatic, sitting inside the editor surface ramp.
| Token | Value | Use |
|---|---|---|
--kbd-face-top |
#434343 |
Top of the face gradient (between surface-3 and -4) |
--kbd-face-bottom |
#2f2f2f |
Bottom of the face gradient (just below surface-3) |
--kbd-face-top-hover |
#4d4d4d |
Hover face gradient top (one step brighter) |
--kbd-face-bottom-hover |
#363636 |
Hover face gradient bottom |
--kbd-border |
var(--border) |
Keycap border — achromatic, same as editor controls |
--kbd-text |
var(--text-muted) |
Key label text — same register as surrounding labels |
--kbd-highlight |
rgba(255, 255, 255, 0.06) |
Top inner highlight (inset box-shadow) |
--kbd-inner-shadow |
rgba(0, 0, 0, 0.22) |
Soft concave bottom inner shadow |
--kbd-edge |
rgba(0, 0, 0, 0.30) |
1px bottom edge — the key's simulated thickness |
--kbd-drop |
rgba(0, 0, 0, 0.28) |
Small drop shadow beneath the cap |
These tokens are scoped to Kbd and ShortcutKeys. Don't reach for them in other components.
background: transparent;
border: 1px solid var(--overlay-20);
border-radius: var(--input-radius);
color: var(--text);.field:hover { border-color: var(--overlay-30); }
.field:focus { border-color: var(--overlay-50); box-shadow: var(--shadow-input-focus); }Cards are filled and borderless; inputs are unfilled and bordered. That's the load-bearing visual distinction between containers and controls.
Four global tokens cover the layered surfaces that float above the editor:
--z-dropdown: 20;
--spotlight-z-index: 9000;
--toast-z-index: 10000;
--tooltip-z-index: 10001;Token names: --z-dropdown, --spotlight-z-index, --toast-z-index, --tooltip-z-index.
--tooltip-z-index is deliberately the highest token so tooltips are never occluded by the surface their trigger lives on. --toast-z-index sits above modal layers and below tooltips. --spotlight-z-index is reused by several modal-level surfaces that need to sit above the editor chrome.
Global modal layer (all raw values in the shared admin stacking context):
| Value | What occupies it |
|---|---|
| 9000 | Spotlight backdrop (--spotlight-z-index); Settings modal backdrop; ModuleInserterDialog backdrop |
| 9001 | Settings dialog wrapper (--spotlight-z-index + 1) |
| 9050 | MediaPickerModal backdrop (calc(--spotlight-z-index + 50)) — sits above Settings because the picker can be opened from inside Settings (e.g. Settings → General → Favicon → Browse library…) |
| 10000 | BodySlashMenu — predates tokenisation; see inline comment in BodySlashMenu.module.css |
| 10001 | Tooltips (--tooltip-z-index); AdminContextMenuGuard |
The gap between 9001 (Settings dialog) and 9050 (MediaPickerModal) is intentional headroom for any future sub-dialogs inside Settings. The gap between 9050 and 10000 (BodySlashMenu) keeps the slash menu above all modal layers. Do not add new raw values into these ranges without updating this table.
The visual editor uses additional raw z-index values that are not tokenised. They fall into two independent stacking contexts:
Editor layout context (shared stacking context for the editor chrome):
| Value | What occupies it |
|---|---|
| 0 | CanvasRoot — an isolation root; all canvas-internal values are confined here |
| 30 | Main toolbar |
| 50 | Floating panels: PropertiesPanel, AgentPanel, DomPanel |
| 55 | LeftSidebar, RightSidebar, PanelRail |
| 70 | Media floating windows (FloatingWindow, MediaViewerWindow) |
| 80 | CodeEditorPanel |
| 201 | Toolbar popovers / dropdowns |
| 400–401 | PreviewOverlay |
Canvas-internal context (confined inside CanvasRoot's z-index: 0):
| Value | What occupies it |
|---|---|
| 50 | PluginCanvasOverlayLayer |
| 51 | Selection ring, hover ring, selection toolbar |
| 53 | CanvasModeToggle, CanvasNotch |
| 60 | CanvasContextSelector |
| 2147483647 | Drop-indicator layer inside iframe (must beat arbitrary module stacking contexts) |
CanvasRoot declares z-index: 0; position: relative to establish the isolation. Without it, the canvas-internal z-index 51 would escape into the layout context and paint over floating panels at z-index 50. See docs/editor.md → "Canvas stacking context isolation" for the full explanation.
Raw canvas-internal values are intentional exceptions — they cannot be tokens because they are relative to an isolated stacking context, not the global one. Do not add new raw z-index values outside this established ladder.
Tooltip chrome uses --bg-body, --text-bright, --overlay-5, --shadow-tooltip, --tooltip-radius, and --tooltip-z-index directly.
Spotlight has only layout/z-index tokens of its own. Its colors come from the same global overlay, scrim, text, and semantic state tokens as the rest of the admin UI.
| Token | Value |
|---|---|
--scrim-50 |
Backdrop fill |
--backdrop-blur |
8px |
--spotlight-z-index |
9000 |
--spotlight-width |
640px |
--overlay-10 |
Selected row and skeleton shimmer |
--success-20 |
Match highlight background |
--danger-light / --danger-10 |
Destructive command states |
--progress-shimmer |
Travelling highlight on progress bars |
These tokens are narrow, named exceptions for authored surfaces that need a specific translucent treatment and cannot be expressed as a reusable semantic state.
| Token | Use |
|---|---|
--plugins-hero-tint-a |
First plugins workspace hero background tint |
--plugins-hero-tint-b |
Second plugins workspace hero background tint |
--canvas-live-scrim |
Theme-adjusted live-preview overlay scrim |
Default chart tint, series colors, glow halos, empty-segment styling. Used by dashboard widgets and the framework scale UI.
| Token | Value | Use |
|---|---|---|
--chart-default-tint |
var(--accent-4) |
Fallback when no per-call tint |
--chart-series-min |
#38bdf8 (sky 400) |
"Mobile" series |
--chart-series-min-glow |
rgba(56, 189, 248, 0.40) |
Halo for active mobile segment |
--chart-series-max |
#4ade80 (green 400) |
"Desktop" series |
--chart-series-max-glow |
rgba(74, 222, 128, 0.40) |
Halo for active desktop segment |
--chart-segment-empty |
var(--overlay-5) |
Empty segment fill |
--chart-segment-empty-border |
var(--overlay-5) |
Empty segment border |
--chart-track-bg |
var(--overlay-5) |
Track background behind chart segments |
globals.css includes a non-negotiable reduced-motion override:
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}The two !important declarations here are the only legitimate !important usages in globals.css. Constraint #189.
- Pick the right group in
src/styles/globals.css(color → semantic group; radius → radius group; z-index → z-index group). - Add the custom property with a one-line comment if the meaning isn't obvious from the name.
- Reference it via
var(--*)in CSS Modules. - Add a row to the matching table in this doc.
- If it introduces a new concept (not a variation of an existing group), add a paragraph explaining what it's for in this doc and in docs/design.md.
| Pattern | Use instead |
|---|---|
color: #ededed; in an admin / ui CSS module |
color: var(--text); |
border: 1px solid #333; |
border: 1px solid var(--border); |
box-shadow: 0 4px 16px rgba(0,0,0,0.5); |
box-shadow: var(--shadow-panel-drop); |
| Inventing a one-off radius | Use the radius scale |
| Inventing a one-off z-index | Use the existing z-index tokens |
| Reaching for a one-off rail color | Use railAccent / assignRailAccents, or add a token |
| Hardcoding the canvas selection ring color | var(--canvas-selection-ring) |
- docs/design.md — design principles + surface systems + UI primitives
- docs/reference/ui-primitives.md — which primitive uses which tokens
- Source-of-truth file:
src/styles/globals.css - Gate tests:
src/__tests__/architecture/css-token-policy.test.tssrc/__tests__/architecture/css-token-vocabulary.test.tssrc/__tests__/architecture/admin-typography-token-policy.test.tssrc/__tests__/architecture/admin-spacing-token-policy.test.tssrc/__tests__/architecture/noTailwindUtilities.test.tssrc/__tests__/architecture/no-css-var-fallbacks.test.tssrc/__tests__/architecture/scrollbar-chrome.test.ts— scrollbar tokens declared; both Firefox and WebKit/Blink styled; properties panel usesscrollbar-gutter: stable