- 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
- ARNO Foundation (default) — opens with Foundation v1 tokens + components pre-loaded
- Import from Storybook — brownfield path (ADR 0034)
- Capture existing site — extension DOM-walk (ADR 0030)
- 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,
$schemav1.0.0 pinned per file - Architecture: 3-tier (primitive → semantic → component), Atlassian-style hierarchical naming
<category>.<usage>.<modifier>.<state>.com.arnoextension 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 #000000State 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, monospaceSizes — 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 60pxWeights (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 96pxUsed 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
| Category | Count |
|---|---|
| Neutral primitives | 13 |
| State primitives | 44 |
| Color semantic | ~50 |
| Color component | ~300 |
| Typography | 33 |
| Spacing | 16 |
| Max-width | 4 |
| Breakpoints | 5 |
| Radius | 7 |
| Elevation | 12 |
| Border | 4 |
| Motion | 11 |
| Z-index | 9 |
| Opacity | 6 |
| 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
- Logo/screenshot drop — k-means clustering for 5-8 dominant colors. 11-step ramps via Material 3 HCT algorithm (perceptually-uniform).
- Capture-by-URL — reuses ADR 0030 pipeline focused on tokens.
- 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
| Mode | Activated when | Tokens loaded | Components loaded | Rules |
|---|---|---|---|---|
| Greenfield | Foundation default selected | Foundation v1 (~514) | Foundation 50 + 100 icons | Foundation + WCAG |
| Brownfield | Storybook URL / DTCG upload | Imported only | Imported only | Imported + WCAG |
| Hybrid | Imported + Foundation gap-fill | Imported primary + Foundation gaps | Imported only | Imported + 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
- ADR 0035 — Onion-layered paradigm
- ADR 0034 — Design-system-first capture (brownfield primary)
- ADR 0030 — Page reconstruction
- ADR 0027 — Composition zones
- ADR 0025 — Tokens publish
- W3C Design Tokens Community Group v1.0.0 — https://design-tokens.github.io/community-group/format/ (opens in a new tab)
- Style Dictionary 4.x — https://styledictionary.com/ (opens in a new tab)
- Atlassian Design Tokens
- Material 3 HCT — https://material.io/blog/science-of-color-design (opens in a new tab)
- shadcn/ui — component list reference
- Lucide/Heroicons — icon delivery pattern
- Inter — https://rsms.me/inter/ (opens in a new tab) — OFL self-hosted
- WCAG 2.1 AA — https://www.w3.org/TR/WCAG21/ (opens in a new tab)