ADRs
ADR 0036 — Foundation v1: default project starter + 50-component library + 100-icon set + WCAG-locked rule cascade
  • Date: 2026-06-04
  • Status: Accepted (paradigm direction); component-level specs frozen for v1
  • Phase / Feature: Foundation v1 — implements ADR 0035 Decision §2 (default-described components) and first concrete slice of §3 (rule-locked cascade) for greenfield
  • Builds on: ADR 0035 (Onion-layered paradigm), ADR 0025 (Tokens publish), ADR 0027 (Composition zones)
  • Reframes (does not replace): ADR 0034 — Storybook-first capture demoted from primary onboarding to brownfield-mode equal-rank option

Context

ADR 0035 named four paradigm principles. §2 (default-described components) and §3 (rule-locked DS cascade) require a concrete substrate. ADR 0034 chose Storybook-first capture as primary onboarding — solving brownfield. The greenfield case (designer opens ARNO for a new product without an existing DS) had no defined onboarding artifact.

This ADR fixes that gap. The guiding principle for every concrete choice below is "improve, do not invent" — established industry patterns are adopted by default. Deviations from industry-standard are limited to the few places where ARNO's substrate (cloud-native + code-linked + rule-locked) lets us solve problems the industry has not solved.

This ADR defines:

  • A pre-loaded canonical token system (Foundation v1, 3-tier, ~514 tokens) shipping with every new project
  • A 50-component default library aligned to shadcn/Radix industry baseline
  • A 100-icon set, custom-designed, with Solid/Line variants, RTL-aware
  • Component fidelity as a per-instance flag (low/high), transitioning implicitly on first edit
  • Continuous WCAG 2.1 AA validation as the first concrete rule running on top of ADR 0035 §3 cascade
  • RTL/i18n support from v1 for EMEA/SEA markets
  • A brand-import ritual (Material HCT palette extraction + WCAG-validated ramp generation)
  • Three lifecycle modes (greenfield / brownfield / hybrid) reconciling with ADR 0034

The ADR went through two critical review passes: Pass 1 (dark corners — 27 issues, 27 resolved) and Pass 2 (industry alignment — 18 issues, 12 optimizations applied, 6 acknowledged as legitimate ARNO innovations).

Decision

§1 — New project modal: "ARNO Foundation (default)" as default starter

  1. ARNO Foundation (default) — opens with Foundation v1 tokens + components pre-loaded
  2. Import from Storybook — brownfield path (ADR 0034)
  3. Capture existing site — extension DOM-walk (ADR 0030)
  4. GitHub repo — Phase 13 sync target

User-defined presets / customized defaults — not v1. v1 modal: take-it-or-leave-it. Preset switching deferred to Phase 18+.

§2 — Foundation v1 token system

  • Storage: DTCG JSON, $schema v1.0.0 pinned per file
  • Architecture: 3-tier (primitive → semantic → component), Atlassian-style hierarchical naming <category>.<usage>.<modifier>.<state>. com.arno extension reserved for future rule-lock metadata
  • Versioning: Semver (v1.0.0 → v2.0.0). Project pins. Upgrade = computed diff with side-by-side preview
  • Theme runtime: CSS custom properties on :root. Theme switch = class toggle on <html>. <16ms swap
  • Tooling: Style Dictionary 4.x build pipeline
  • Total tokens: ~514

§2.1 Colors — Layer 1 primitives

Neutral scale — 13 steps, Tailwind-aligned with 0/1000 anchors:

neutral.0    #FFFFFF      neutral.500   #71717A
neutral.50   #FAFAFA      neutral.600   #52525B
neutral.100  #F4F4F5      neutral.700   #3F3F46
neutral.200  #E4E4E7      neutral.800   #27272A
neutral.300  #D4D4D8      neutral.900   #18181B
neutral.400  #A1A1AA      neutral.950   #09090B
                          neutral.1000  #000000

State colors — 4 × 11 steps (Tailwind-aligned full ramps): red.50..950, yellow.50..950, green.50..950, blue.50..950. Total: 44 state primitives.

No brand colors in primitives — added in §5.

§2.2 Colors — Layer 2 semantic (~50 tokens)

Categories: text.*, background.*, border.*, icon.*, interactive.*. Light theme refs:

text.primary           {neutral.900}
text.secondary         {neutral.600}
text.subtle            {neutral.500}
text.disabled          {neutral.400}
text.inverse           {neutral.0}
text.link              {blue.600}
text.link.hovered      {blue.800}
text.danger            {red.600}
text.warning           {yellow.700}
text.success           {green.700}

background.surface              {neutral.0}
background.surface.raised       {neutral.0}
background.surface.sunken       {neutral.100}
background.neutral.subtle       {neutral.100}
background.neutral.subtle.hovered {neutral.200}
background.neutral.bold         {neutral.900}
background.danger.subtle        {red.50}
background.danger.bold          {red.600}
background.warning.subtle       {yellow.50}
background.success.subtle       {green.50}
background.information.subtle   {blue.50}

border.default       {neutral.300}
border.subtle        {neutral.200}
border.bold          {neutral.700}
border.focused       {blue.500}
border.danger        {red.500}

icon.primary         {neutral.700}
icon.secondary       {neutral.500}
icon.disabled        {neutral.300}
icon.inverse         {neutral.0}
icon.danger          {red.600}

interactive.primary             {neutral.900}
interactive.primary.hovered     {neutral.800}
interactive.primary.pressed     {neutral.700}
interactive.secondary           {neutral.100}
interactive.secondary.hovered   {neutral.200}
interactive.danger              {red.600}
interactive.danger.hovered      {red.700}

Dark theme = same names, different primitive refs.

§2.3 Colors — Layer 3 component (~300 tokens)

Average 5-8 component tokens × 50 components ≈ 300. Examples:

component.button.primary.background           {interactive.primary}
component.button.primary.background.hovered   {interactive.primary.hovered}
component.button.primary.text                 {text.inverse}
component.input.background                    {background.surface}
component.input.border                        {border.default}
component.input.border.focused                {border.focused}
component.card.background                     {background.surface.raised}
component.modal.backdrop                      rgba({neutral.1000}, 0.5)
component.tooltip.background                  {background.neutral.bold}

Component tokens are enforcement scope for future rule-editor (ADR 0035 Q2).

§2.4 Typography

Default font: Inter — shipped self-hosted woff2, font-display: swap. System stack fallback.

font.sans   "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif
font.mono   ui-monospace, SFMono-Regular, "SF Mono", Consolas, "Liberation Mono", Menlo, monospace

Sizes — 10 steps, ratio ≈ 1.20:

font.size.075 12px   font.size.500 24px
font.size.100 14px   font.size.600 30px
font.size.200 16px   font.size.700 36px
font.size.300 18px   font.size.800 48px
font.size.400 20px   font.size.900 60px

Weights (4): regular 400, medium 500, semibold 600, bold 700 Line heights: tight 1.1, snug 1.25, normal 1.5, relaxed 1.75 Letter spacing: tight -0.02em, normal 0, wide 0.05em Composite (12): heading.h1..h6, body.large/base/small, caption, label, code.base Total: ~33 tokens.

§2.5 Spacing — 8pt grid, 16 steps

space.0     0px       space.300  24px
space.025   2px       space.400  32px
space.050   4px       space.500  40px
space.075   6px       space.600  48px
space.100   8px       space.700  56px
space.150   12px      space.800  64px
space.200   16px      space.1000 80px
space.250   20px      space.1200 96px

Used for both component padding/margins and layout gaps. No separate layout.gap.* namespace.

§2.6 Max-width (4)

maxwidth.narrow 640px, maxwidth.normal 1024px, maxwidth.wide 1280px, maxwidth.full 100%

§2.7 Breakpoints — Tailwind-aligned (5)

sm 640, md 768, lg 1024, xl 1280, 2xl 1536

§2.8 Radius (7)

radius.0 0px, .050 2px, .100 4px, .200 8px, .300 12px, .400 16px, .full 9999px

§2.9 Elevation — composite shadows (6 × 2 themes = 12)

elevation.0    none
elevation.100  0 1px 2px rgba(0,0,0,0.05)
elevation.200  0 1px 3px rgba(0,0,0,0.10), 0 1px 2px rgba(0,0,0,0.06)
elevation.300  0 4px 6px rgba(0,0,0,0.07), 0 2px 4px rgba(0,0,0,0.06)
elevation.400  0 10px 15px rgba(0,0,0,0.10), 0 4px 6px rgba(0,0,0,0.05)
elevation.500  0 25px 50px rgba(0,0,0,0.25)

Dark theme uses elevated alpha.

§2.10 Border widths (4)

border.0 0, .100 1px, .200 2px (focus), .300 4px (selected)

§2.11 Motion (11)

Durations: 0/100/200/300/500/700ms Easings: standard, decelerate, accelerate, emphasize, linear (cubic-beziers)

§2.12 Z-index (9), Opacity (6)

z: base 0, dropdown 1000, sticky 1100, banner 1200, modal.backdrop 1300, modal 1310, popover 1400, toast 1500, tooltip 1600 opacity: 0, 0.25, 0.4, 0.6, 0.8, 1

§2.13 Token count

CategoryCount
Neutral primitives13
State primitives44
Color semantic~50
Color component~300
Typography33
Spacing16
Max-width4
Breakpoints5
Radius7
Elevation12
Border4
Motion11
Z-index9
Opacity6
Total~514

§3 — 50-component library (shadcn-aligned)

All components:

  • Width-adaptive (stretch to container)
  • Token-bound (no raw hex/px)
  • Default-described (ADR 0035 §2)
  • RTL-aware (CSS logical properties, auto-flip via dir="rtl")
  • Height behavior documented per-component (fixed via S/M/L for form-like; content-driven for surface-like)

Form (10): Input, Textarea, Select, Combobox, Checkbox, Radio, Switch, Slider, DatePicker, Label Action (5): Button, IconButton, Link, DropdownMenu, ToggleButton Display (8): Avatar, Badge, Card, Tag, Tooltip, Toast, Alert, Popover Navigation (6): Tab, Breadcrumb, Pagination, Sidebar, Topbar, Stepper Layout (5): Stack, Grid, Divider, Spacer, Container Feedback (5): Modal, Drawer, Snackbar, Progress, Skeleton Data (7): Table, List, Tree, Accordion, EmptyState, Calendar, ScrollArea Util (4): CommandPalette, Banner, FileUpload, Collapsible

Total: 50. Aligned to shadcn/ui industry baseline. Scope locked for v1.

§4 — 100-icon set, custom-designed, Solid + Line, RTL-aware

Custom — not derived from Phosphor/Lucide/Heroicons. ARNO icon language: 24×24 viewBox, 1.5px stroke for Line, geometric simplification, optical balance. ~3-4 weeks single designer.

Naming: Solid (filled) / Line (outlined).

Delivery — dual format:

  • Primary: React components per icon, tree-shakeable (<UserIcon variant="solid" />). Lucide/Heroicons pattern
  • Secondary: SVG sprite for ARNO's renderer

Single-color via currentColor, inherits icon.* semantic.

RTL: ~14 directional icons auto-swap. Total: 200 base + 14 RTL = 214 SVGs.

100 across 14 categories: Navigation (10), Position (5), Actions (15), Status (6), Files (8), Communication (6), Media (6), User (6), Time (4), Tools (10), Shapes (4), Tech (8), Money (4), Location (4), Misc (4).

Known limit: 100 below industry minimum (Heroicons 300, Lucide 1500). V2 expansion to 300+ — Q7.

§5 — Brand-import ritual

  1. Logo/screenshot drop — k-means clustering for 5-8 dominant colors. 11-step ramps via Material 3 HCT algorithm (perceptually-uniform).
  2. Capture-by-URL — reuses ADR 0030 pipeline focused on tokens.
  3. Manual hex paste + HCT ramp auto-gen.

After population, designer rebinds semantic tokens to brand primitives via drag-drop. ~15-25 rebinds = 80% branding.

Custom fonts — drag .woff2/.otf, registered as brand.font.*. License attestation per ADR 0030. System stack fallback.

WCAG validation throughout — see §7.

§6 — Component fidelity as per-instance flag

Component is wireframe until user changes any variable. First edit transitions instance from low-fidelity to high-fidelity.

  • fidelity: 'low' | 'high' flag per instance, default 'low'
  • Low-fidelity: Foundation defaults (neutrals, Inter Regular, default sizes) → wireframe look
  • First edit → fidelity: 'high' → user values + brand cascade → hi-fi look
  • Per-instance, not per-definition. Reversible.

Dissolves ADR 0035 §1 wireframe-vs-hi-fi separation into a simpler primitive: fidelity is what user does, not which library.

ARNO innovation beyond industry — Figma uses file separation, we use per-instance flag.

§7 — WCAG 2.1 AA continuous validation

  • Normal text < 18pt: 4.5:1 minimum
  • Large text ≥ 18pt or 14pt bold: 3:1 minimum
  • UI components / borders / icons: 3:1 minimum

Validation points: token editor, brand import, component editor, theme switch.

Performance (required v1):

  • Debounced 300ms on edit
  • Incremental — only affected pairs
  • Web Worker compute
  • Caching per (color-A, color-B) pair

Failure UX: red icon + tooltip with ratio + suggested adjusted color. Does NOT block edit. DOES block publish/export unless explicit override with text reason.

First concrete rule of ADR 0035 §3 cascade. Hardcoded WCAG demonstrates the model in production without waiting for rule-editor sub-product.

Real-time WCAG in core editor is ARNO innovation — Stark addon, Atlassian CI; none in core editor's main loop.

§8 — Component name auto-prefix on collision

  • No collision → created in user namespace
  • Collision → auto-prefix with project prefix (acme-Button). Toast: "Name 'Button' is reserved by Foundation. Saved as 'acme-Button'."
  • Prefix-already-used collision → error

Pattern borrowed from Tailwind prefix config. More flexible than hard error.

§9 — TypeScript codegen via Style Dictionary

DTCG JSON → Style Dictionary 4.x → packages/foundation/tokens.generated.ts:

export type ColorToken = 'text.primary' | 'text.secondary' | ... ;
export type SpacingToken = 'space.0' | 'space.025' | ... ;

Component props accept literal unions. Compile-time enforcement. Regenerated on token change (watch in dev, commit in CI).

Style Dictionary over custom — industry-standard (Salesforce/Adobe/Amazon), supports com.arno transformers, multiple output formats.

§10 — Lifecycle modes

ModeActivated whenTokens loadedComponents loadedRules
GreenfieldFoundation default selectedFoundation v1 (~514)Foundation 50 + 100 iconsFoundation + WCAG
BrownfieldStorybook URL / DTCG uploadImported onlyImported onlyImported + WCAG
HybridImported + Foundation gap-fillImported primary + Foundation gapsImported onlyImported + Foundation + WCAG

Greenfield default. WCAG runs in all modes.

§11 — Inter font shipping

  • License OFL (free, no attribution required)
  • Self-hosted woff2 in packages/foundation/fonts/, not Google Fonts CDN
  • Privacy: GDPR — Google CDN sends IPs, self-hosting eliminates
  • 4 weights: 400/500/600/700. Italic deferred to v2.
  • font-display: swap — fallback shown immediately. No FOIT.
  • Subset: Latin + Latin Extended + Cyrillic + Cyrillic Extended (covers EMEA inc. Russian). Vietnamese for SEA — Q9.
  • Bundle: ~80KB × 4 = ~320KB woff2.

§12 — Performance budgets

  • Foundation tokens (TS + CSS vars): < 50KB gzipped
  • Foundation components (typical 10 used): < 80KB gzipped
  • Foundation icons (typical 30 used): < 30KB gzipped
  • Inter font: ~320KB woff2
  • Total Foundation first-load: < 480KB

CI gate Phase 17 — fails build if exceeded.

Anti-patterns

  • Do not build "choose your preset" UI in v1
  • Do not add brand colors to Foundation primitives
  • Do not extend 50-component list in v1
  • Do not skip shipping Inter (system-stack-only rejected on industry alignment)
  • Do not allow pixel-level component edits — all sizing token-bound
  • Do not derive icons from Phosphor/Lucide/Heroicons — custom pass
  • Do not ship icon font — SVG sprite + React components
  • Do not introduce fidelity as mode/layer toggle — per-instance state
  • Do not allow color pairs below WCAG AA to publish without override
  • Do not hard-error on name collision — auto-prefix
  • Do not write custom codegen for tokens — Style Dictionary
  • Do not load Google Fonts CDN — self-host
  • Do not use date-based versioning — semver

Sequencing — parallel tracks, ~8 weeks wall-clock

Track 1 — Tokens (1 dev, ~2-3 weeks): DTCG JSON (~514 tokens), Style Dictionary 4.x pipeline, light + dark themes, WCAG utility, TS codegen.

Track 2 — Components (2 devs, ~6-8 weeks, critical path): 50 components React + token-bound styles, S/M/L variants, RTL via logical CSS, Storybook stories, A11y pass, fidelity flag mechanic.

Track 3 — Icons (1 designer, ~3-4 weeks): Custom 100×2 = 200 SVGs, ARNO icon language design pass, 14 RTL counterparts, sprite + React components.

Track 4 — Modal + WCAG UI (1 dev, ~2 weeks): Modal reorder, mode detection, WCAG UI with performance, auto-prefix collision.

Track 5 — Brand-import (1 dev + 1 designer, ~3-4 weeks): Logo drop + k-means, Material HCT ramp + WCAG check, capture-by-URL, manual hex, rebind drag-drop, font upload.

Wall-clock ~8 weeks (Track 2 critical path). Brand-import staged release.

Open questions

  • Q1 — Foundation upgrade path (semver-pinned, diff preview). Implementation TBD.
  • Q2 — Component variant API (dropdown/slider/picker). Defer.
  • Q3 — User-added custom icon mechanism. Defer to v2.
  • Q4 — Composite typography tokens live refs (default) vs frozen snapshots.
  • Q5 — RTL-aware icon opt-out (logos shouldn't flip). Per-icon mechanism TBD.
  • Q6 — WCAG override mechanism UI + storage format.
  • Q7 — Icon set v2 to 300+. Post-v1 6-8 week design pass to match Heroicons floor.
  • Q8 — SSR/SSG export theme inlining. Defer to export ADR.
  • Q9 — Vietnamese/SEA Inter subset (~100KB per language). Re-eval per market.

References