Composition over configuration
A Button with an asChild prop is more useful than a Button with twenty variant props. Fewer, more composable primitives produce better outcomes than comprehensive single-component APIs.
Enterprise design system · How I think
Philosophy, governance, and component architecture — how I think about building frontend systems that stay coherent when multiple teams, delivery cadences, and years of change apply pressure.
2-layer
Token architecture
WCAG AA
All components
0
Axe violations
100%
:focus-visible coverage
The challenge
Without a shared language, every team solves the same problems independently — and every solution compounds the inconsistency. This system was built to make these failures structurally impossible.
Fragmentation
Three button components. Four modal implementations. No shared contract between teams.
↳ Shared primitives and a single token layer eliminate parallel implementations.
Design drift
One-off hex values accumulate. Design intent and production output diverge over time.
↳ Tokens make the right value the obvious choice. Arbitrary values become visible violations.
Accessibility regression
Accessibility is a QA gate — checked after building, not enforced during. Violations compound.
↳ Components start from correct semantics. WCAG compliance is structural, not audited.
Implementation variance
The same component looks different across pages. No canonical reference. Every engineer decides.
↳ Bounded variation: four variants, five sizes, three surfaces. The obvious choice is the right one.
Multi-team governance
Three business units. Independent release cadences. Component changes break other teams without warning.
↳ Additive-only versioning and shared deprecation contracts. No consumer encounters a breaking change.
Before → After
Before — without governance
Fragmented implementations across teams
47 WCAG violations compounding over time
Accessibility as a QA gate after launch
Per-engineer visual decisions on every page
Dark mode required 100+ per-component overrides
After — with governance
Single shared component registry, one source of truth
Zero axe violations — at every audit, on every component
Compliance built into the component architecture itself
Bounded variation — the obvious choice is the correct choice
Dark mode via a single token swap, zero per-component changes
Outcomes
The same architectural patterns used in this portfolio system were applied across 200+ AEM production templates at Fortune 200 scale.
0
Axe violations
Every audit, every component
100%
:focus-visible
All interactive elements
AA
WCAG 2.1
All states, all components
1
Token swap
Full dark mode, no overrides
Adoption
The token system and component primitives are used throughout every page without variation — the same discipline applied to 200+ production AEM templates serving millions of users.
Engineering efficiency
Dark mode, high-contrast support, and theme switching required zero per-component changes after the token layer was established. One change. Universal effect.
Governance
No hardcoded colors. No one-off pixel values. Every visual decision is traceable to a token. Any future contributor encounters a system — not a collection of individual decisions.
Foundation
A design system is not a component library. It is a shared language — a set of decisions that compounds across every team, every product, and every engineer who touches the codebase.
A Button with an asChild prop is more useful than a Button with twenty variant props. Fewer, more composable primitives produce better outcomes than comprehensive single-component APIs.
The first rule of ARIA: don't use it if a native element provides the semantic. Getting to a correct baseline means starting with the right element, not adding attributes to compensate for using the wrong one.
Unlimited flexibility produces inconsistency at scale. Five named sizes and four button variants aren't limitations — they're the mechanism that makes a system legible to the next engineer who uses it.
Every hardcoded value is a future inconsistency. Every token reference is a future-proofed decision. The discipline of never using arbitrary values is what makes a design system maintainable across years and teams.
Token architecture — two-layer semantic system
Governance model
A design system without governance is a component library that gradually drifts. Governance is not process overhead — it is the structural reason the system stays useful.
Token governance
EnforcementNo component references a raw value
Every color, spacing, and radius value traces to a named token. Arbitrary values are immediately visible as violations. This single rule eliminates most drift.
Accessibility enforcement
Built-inCorrect semantics are required, not recommended
Components start from native HTML elements. ARIA fills gaps — not substitutes. The a11y pipeline runs per component: axe-core CI → keyboard traversal → NVDA → VoiceOver.
Variation control
By designThe options available are the options intended
Four button variants. Five spacing sizes. Three surface levels. Bounded variation is a governance mechanism — the obvious choice and the right choice are the same thing.
Versioning approach
Backward-compatAdditive changes only — never breaking by default
New tokens extend the system. Removed tokens deprecate with a replacement. No component upgrade breaks the consuming page. This is the same contract AEM Core Components uses — and the reason it scales.
Component ecosystem
Correct semantics, keyboard navigation, focus management, and ARIA only where native HTML falls short. These run in the browser — not screenshots.
Buttons — 4 variants
Keyboard accessible · :focus-visible · active:scale(0.97)
Badges — status variants
Semantic color — status only, never decorative
Form inputs — all states
Enter a valid email address
Label association · :focus-visible · aria-invalid
Tabs — keyboard navigable
role=tablist · aria-selected · ← → keyboard
Card — hover elevation
Design system
Token-driven, theme-aware, accessible by construction.
shadow-xs → shadow-lg · border transition · -translate-y-0.5
Focus states — :focus-visible only
Tab to see focus rings · Mouse clicks never show ring
Color system
One accent prevents competition. Warm-tinted neutrals feel more human than cool grays. Every pairing meets WCAG 2.1 AA contrast requirements.
Brand palette
Brand 50
#EEEEF8
background only
Brand 100
#CECBF6
tint/muted
Brand 200
#AFA9EC
decorative
Brand 300
#9490E0
large text on white
Brand 400
#7B7BC8
3.7:1 on white
Brand 500
#4B4B8F
AA on white ✓
Brand 600
#3A3A72
AA on white ✓
Brand 700
#2D2D52
AAA on white ✓
Brand 800
#1E1E38
AAA on white ✓
Brand 900
#0B0B1A
AAA on white ✓
Neutral palette — warm-tinted
Neutral 50
#FAFAF8
page background
Neutral 100
#F5F4F0
surface
Neutral 150
#ECEAE4
border subtle
Neutral 200
#E2E0D9
border default
Neutral 300
#C8C5BC
border strong
Neutral 500
#888480
muted text
Neutral 700
#3D3A36
AA body text ✓
Neutral 900
#0F0E0D
AAA heading text ✓
Theme-aware semantic tokens
--color-bg--color-surface--color-surface-raisedToggle light/dark to see theme adaptation
Key semantic tokens — purpose-named, never raw palette values
--color-bgPage background
--color-surfaceCards, inputs, panels
--color-text-primaryHeadings, key labels
--color-text-secondaryBody copy
--color-accentInteractive, active state
Typography system
Fraunces (display) for headlines — craft and editorial weight. Outfit (sans) for UI and body — legibility and precision. Geist Mono for code and annotations.
Display / Fraunces / 72px
Aa
Heading / Outfit / 30px / medium
Senior UX Engineer
Body / Outfit / 17px / regular
Specializing in design systems, accessible interfaces, and frontend architecture at enterprise scale.
Mono / Geist Mono / 11px
Design Systems · AEM · WCAG 2.1 · TypeScript
Live type specimen — no screenshots
Type scale — 1.25× modular ratio (Major Third)
text-display72pxHero headlines
text-3xl36pxSection headings
text-lg20pxLead body text
text-base16pxDefault body
text-xs12pxCaptions, metadata
Spacing + shape
All spacing is a multiple of 4px. No magic numbers. The radius scale maps to semantic use cases — chips, buttons, cards, containers — so the right radius is always obvious.
Spacing scale
--space-14px--space-416px--space-624px--space-1248px--space-2496px--space-32128pxRadius scale
--radius-xs2px--radius-sm4px--radius-md8px--radius-lg12px--radius-xl16px--radius-2xl20px--radius-full9999pxAccessibility standards
Accessibility is an engineering responsibility, not a design review checklist. Every component is built accessible — not retrofitted.
Accessibility validation pipeline — per-component protocol
All text meets 4.5:1 ratio. Headings meet 3:1. Verified with brand-500 (#4B4B8F) at 5.2:1 on white.
Focus indicators and interactive UI components meet 3:1 against adjacent colors.
All interactive components reachable and operable via keyboard without mouse dependency.
2px outline with 3px offset applied globally via :focus-visible. Never outline: none without replacement.
prefers-reduced-motion respected globally in CSS and in Framer Motion via useReducedMotion().
All interactive components have accessible names. ARIA used only where native HTML semantics are insufficient.
Semantic HTML throughout. <nav>, <main>, <article>, <section> landmarks. <dl> for key-value content.
Skip navigation link as first focusable element on every page. Targets #main-content with tabIndex={-1}.
Motion system
Every animation answers 'what changed?' not 'isn't this cool?' Duration tokens prevent arbitrary timing. The toggle simulates prefers-reduced-motion: reduce.
Card elevation on hover · var(--duration-normal)
Section scroll reveal · var(--duration-slow)
Button press feedback · var(--duration-instant)
Motion tokens — duration and easing
| Token | Value | Usage |
|---|---|---|
--duration-instant | 80ms | Button press, micro-interactions |
--duration-fast | 150ms | Hover states, tooltip appear |
--duration-normal | 250ms | Element enter/exit |
--duration-slow | 400ms | Page reveals, section entrance |
--duration-crawl | 600ms | Deliberate hero animations |
--ease-out | cubic-bezier(0,0,0.2,1) | Elements entering viewport |
--ease-in | cubic-bezier(0.4,0,1,1) | Elements leaving viewport |
--ease-inout | cubic-bezier(0.4,0,0.2,1) | Repositioning, layout shifts |
Design systems don't fail because the components were wrong. They fail because nobody decided who owned the decisions — and every team quietly made their own.
WCAG AA
Every component
0
Axe violations
100%
:focus-visible coverage
2-layer
Token architecture
Lessons learned
The portfolio design system was built under the same constraints as the enterprise systems in my case studies. These observations came from both.
Constraints are the product.
Every time I reduced a design option — fewer type sizes, fewer spacing values, fewer button variants — the system became more useful, not less. Consistency is a constraint that compounds.
Dark mode is an architecture decision, not a CSS decision.
Building dark mode as a token-swap rather than a dark: utility override meant the entire theme changed in one place. Systems that use per-component overrides will always drift.
Accessibility is easier to build in than retrofit.
Every component that required accessible patterns from the start took roughly the same time to build as an inaccessible version. Retrofitting takes significantly longer. This system has zero axe violations — not because of auditing, but because the foundation was correct.
The TOC is governance in miniature.
A sticky navigation that shows where you are is a small piece of the same problem as component governance — both are about making the right state obvious without requiring effort.