ADRs
ADR 0048 — Colors raison d'être: Foundation as default brand, mentor at every narrow place
  • 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:

  1. Where brand-import flows live in the Colors workspace.
  2. The Constructor / Limiter / Mentor framing that governs what is editable, what is blocked, and how the system explains itself.
  3. The default surface a designer sees on first entry — full Foundation brand visible, not empty slots.

Decision

§1 — Tab renamed ColorColors

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-tabRoleDemo content
BrandCustomize the loaded Foundation brand (the designer's entry point — Constructor)LogoPaletteDemo, HctRampDemo, CssPaletteDemo, SemanticRebindDemo
PrimitivesRead-only ramps (neutral + state) — what semantic tokens referenceNEUTRAL + STATES swatches
SemanticEditable semantic roles with realtime WCAG (Limiter at edit time)TokenEditorDemo
ValidationPre-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 Brand sub-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.md when 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.