- Date: 2026-06-07
- Status: Accepted
- Phase / Feature: Foundation v1 · Colors workspace
- Builds on: ADR 0036 §3 (token cascade), ADR 0037 (WCAG overrides), ADR 0038 (brand persistence), ADR 0041 (brand_icon), ADR 0045 (composite typography refs), ADR 0047 (Phosphor icons)
Context
The Library catalog now groups primitives into a Tokens tab and components/demos into a Components tab (the Tabs nav landed 2026-06-07). Brand-import flows (logo → palette, HCT seed, paste CSS, semantic rebind) shipped as flat demos under Components — wrong placement: they are color-customization tools, not component examples.
ARNO Foundation is a monochrome neutral UI that ships as a usable design system out of the box. A designer creating a project does not start from zero — they start with the Foundation brand fully loaded (all 47 semantic roles bound, both light and dark modes validated, every WCAG pair passing). Customization is override-from-default, not build-from-scratch.
This ADR fixes:
- Where brand-import flows live in the Colors workspace.
- The Constructor / Limiter / Mentor framing that governs what is editable, what is blocked, and how the system explains itself.
- The default surface a designer sees on first entry — full Foundation brand visible, not empty slots.
Decision
§1 — Tab renamed Color → Colors
Plural matches industry convention (Tailwind, Material 3, Radix). Single-noun Color reads as a singular token or property; Colors reads as a workspace.
§2 — Sub-navigation: Brand / Primitives / Semantic / Validation
Inside the Colors token tab, four sub-tabs in this order:
| Sub-tab | Role | Demo content |
|---|---|---|
| Brand | Customize the loaded Foundation brand (the designer's entry point — Constructor) | LogoPaletteDemo, HctRampDemo, CssPaletteDemo, SemanticRebindDemo |
| Primitives | Read-only ramps (neutral + state) — what semantic tokens reference | NEUTRAL + STATES swatches |
| Semantic | Editable semantic roles with realtime WCAG (Limiter at edit time) | TokenEditorDemo |
| Validation | Pre-publish batch + audit (Limiter at release time) | WcagBatchDemo |
Default sub-tab is Brand. Rationale: the designer's first question is "what brand do I have, and what can I change?" — not "show me the primitive ramps I probably won't touch first."
§3 — Triple-role contract (Constructor / Limiter / Mentor)
ARNO Colors operates on three roles in parallel; every UI affordance must satisfy at least one:
Constructor — what the designer can change:
- Brand seed (1 primary required; secondary + tertiary optional)
- Neutral temperature (warm / cool / true — choice from 3 presets, not a free hex)
- State color overrides (corporate red/yellow/green/blue if needed; Foundation defaults if not)
- Semantic rebinding (drag any role to any primitive on the existing ramp)
- Per-token override within WCAG-passing limits
- Per-instance fidelity flip (wireframe → hi-fi, ADR 0036 §6)
- Mode-pair override (light/dark tone-by-tone, with pair-recalc warning)
Limiter — what is blocked:
- Free hex on primary slot (only seed → HCT ramp accepted)
- Non-monotonic ramp (HCT validation rejects)
- Ramp shorter than 11 steps (industry minimum)
- Component → primitive direct binding (
var(--red-500)in Button — anti-pattern; only semantic) - Semantic token with literal value instead of
{primitive.ref}(loses cascade on rebrand — ADR 0045) - Publish with failed WCAG pair (block-on-publish, T6.3)
- Brand re-import without confirm (destructive)
- Multi-source ramp (no mixing HCT-generated + paste-CSS in one primary)
- Light/dark mode breaking pair without explicit override
Mentor — what the system explains, inline, at every narrow place:
- Not an onboarding tour. Explanation surfaces precisely where the designer hits a limit.
- Each blocked action emits a why it's blocked message + a what to do instead suggestion. Not "Wrong color" — "Semantic tokens should reference a primitive ramp step (
{red.500}) so a future brand swap cascades. Pick the closest passing step?" with a one-click apply. - Hover-to-explain on every token in Primitives / Semantic — "this is a Foundation default" vs "this is your override" badge with full provenance.
- WCAG suggestions: at edit time the nearest-valid-step is highlighted, not just the ratio number.
- Daltonism preview overlay (deuteranopia / protanopia / tritanopia) at brand seed selection.
- Industry reference snippets ("Material 3 uses 13 tone steps for this exact case") inline in the explanation box, not in separate docs.
§4 — Foundation as default brand (entry-state invariant)
On first entry to Colors:
- All four sub-tabs are populated. Primitives shows the full Foundation neutral + state ramps. Semantic shows all roles bound to Foundation defaults. Validation shows 47/47 passing WCAG AA.
- No empty states. No "create your brand" splash. The brand exists; the designer customizes it.
- Every Foundation-default value carries a visible badge so the designer can tell which tokens are still defaults vs which they have overridden.
§5 — Reset to Foundation (escape hatch)
Per-token revert affordance: every customized token shows a "Reset to Foundation" inline action that deletes the project-scoped token_override row, returning the role to the Foundation default. Required by the override contract (ADR 0038): an override that can't be undone is a one-way commitment, which violates the "Constructor" promise.
Big-red-button "reset all overrides" is parked until a Project Settings surface exists.
§6 — Brand-import flows belong in Brand, not Components
Industry pattern places brand-import in two surfaces:
- Setup wizard (one-shot at project init — Material You, Mantine
createTheme, Storybook addon-themes) - Theming editor inside the DS surface (Material Theme Builder, Adobe Color, Carbon's brand mapping panel)
ARNO collapses both into the Brand sub-tab inside Colors. Reasoning: Foundation is already loaded (no "init step" needed), and a continuous customization surface matches how designers iterate. Re-importing from a logo or HCT seed mid-project is a normal flow, not destructive — only re-import that drops existing semantic mappings without confirm is destructive (handled by §3 Limiter).
What does NOT belong in Brand:
- Custom font upload (Typography concern) — stays in catalog under font.
- Custom icon upload (Icon concern) — stays in catalog under icons.
- Variant editor (Component concern) — stays in Components panel.
Anti-patterns explicit
- Do not add a "build your DS from scratch" wizard. Foundation is the starting brand; building from zero defeats the whole reason ARNO ships a default.
- Do not surface primitive ramps as the entry point. Designer sees Brand first, Primitives second — they will edit semantics + brand, rarely primitives.
- Do not write Mentor as static onboarding. Help that fires before the designer hits a problem is noise; help that explains the specific block they just hit is the contract.
- Do not ship Reset to Foundation as a settings-page burst. Per-token inline. Big-red-button waits for Project Settings.
- Do not mix font + icon brand-import into the Colors
Brandsub-tab. Per-modality brand-import surfaces stay in their own modality catalog section.
Open questions / parking
- Big-red-button "reset all overrides" — parked until Project Settings surface exists. ADR doesn't decide; mark TODO in
_index.mdwhen Settings ships. - Setup wizard on /app/projects/new — separate ADR if it ever lands. Today's flow (New project → ARNO Foundation → Library opens with full brand) already satisfies the "Foundation as starting point" invariant.
- Mode-pair toggle in Colors workspace — accepted as next-batch work; spec lives in §3 Limiter (light/dark breaking pair without override is blocked).
- Anti-pattern lint surface — needs a runtime rule engine; the engine is out of scope here, the Mentor surface that consumes its findings is in scope.