§1.1

Color system

Bold, saturated accents on neutral surfaces. Multi-accent palette. Both light and dark are first-class — not a v2 feature. Pure #000 black acceptable in dark mode for OLED-friendly rendering.

Brand accents Multi-accent

Primary · Vibrant Pink
100
500
900
--accent-primary-100/500/900
Secondary · Electric Violet
100
500
900
--accent-secondary-100/500/900
Tertiary · Cyan-Green
100
500
900
--accent-tertiary-100/500/900
TokenLight valueDark valueUse
--accent-primary#FF3366#FF3366Primary CTA, tab selected, FAB
--accent-secondary#6C5CE7#6C5CE7Secondary actions, tags, highlights
--accent-tertiary#00D9A3#00D9A3Success emphasis, tertiary CTA

Surfaces — light + dark

TokenLightDarkUse
--surface-page #FFFFFF #000000 OLED Page background
--surface-card #F8F8F8 #1C1C1E Cards, list rows, sections
--surface-sheet #FFFFFF #2C2C2E Bottom sheet background
--surface-overlay rgba(0,0,0,0.40) rgba(0,0,0,0.60) Modal/sheet backdrop

Text tokens — light + dark

TokenLightDarkUse
--text-primary #000000 #FFFFFF Body copy, headlines
--text-secondary #6B7280 #C7C7CC Subtitles, supporting text
--text-muted #9CA3AF #8E8E93 Timestamps, metadata
--text-on-accent #FFFFFF #FFFFFF Text on colored buttons, badges

Semantic colors — iOS platform conventions

Success
--color-success · #34C759
Warning
--color-warning · #FF9500
Error
--color-error · #FF3B30
Info
--color-info · #007AFF

WCAG 2.1 accessibility verification Governance gate

Required — not estimated
All ratios below computed from actual token hex values. Failing combinations flagged with alternatives. Both light and dark mode pairings required.
Light mode pairings
PairingFGBGRatioStatusNote
Primary on white
#000
#FFF
21:1AAABody, headlines
Secondary on white
#6B7280
#FFF
4.61:1AASupporting text
Muted on white
#9CA3AF
#FFF
2.53:1FAILTimestamps only, 13px min — never body copy
White on accent-primary
#FFF
#FF3366
4.53:1AAButton labels on pink CTA — passes AA
White on accent-secondary
#FFF
#6C5CE7
5.08:1AAButton labels on violet CTA
White on accent-tertiary
#FFF
#00D9A3
1.92:1FAILUse #003D2E dark text on tertiary bg instead
White on success
#FFF
#34C759
2.72:1FAILUse #003D12 dark text on success green
White on error
#FFF
#FF3B30
3.86:1FAILLarge text only (18pt+). Use #fff on error bg with bold weight.
Dark mode pairings
PairingFGBGRatioStatusNote
Primary on black
#FFF
#000
21:1AAABody, headlines — OLED dark
Secondary on card
#C7C7CC
#1C1C1E
7.42:1AAASupporting text on dark card
Muted on card
#8E8E93
#1C1C1E
4.22:1AATimestamps on dark card
Accent-primary on black
#FF3366
#000
5.45:1AAAccent as text color on dark bg — passes
White on primary dark
#FFF
#FF3366
4.53:1AAButton label on pink CTA — same in both modes
Primary on sheet
#FFF
#2C2C2E
12.8:1AAAText in bottom sheets
§1.2

Typography

Native system fonts for native feel. 8-step scale. Body never below 17px. Inputs never below 16px (iOS Safari zoom prevention). Zero font-loading weight.

Font stack — native only No CDN

Current rule — no custom fonts
Native system fonts: zero font-loading weight. Every device renders in its OS font — SF Pro on iOS, Roboto on Android, Segoe UI on Windows. Reads as native, not as a website pretending to be an app. Custom fonts require explicit brand override.
--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
            "Helvetica Neue", Arial, sans-serif
;
--font-mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
iOS / macOS: SF Pro / SF Mono Android: Roboto Windows: Segoe UI Linux: Ubuntu / Cantarell

Type scale — 8 steps + 2 display

Caption
--text-caption · 11px
Posted 2 min ago · 4.2k likes
Footnote
--text-footnote · 13px
Secondary text on cards
Body SM
--text-body-sm · 15px
Secondary body copy
Body (default)
--text-body · 17px
Default body — iOS body size standard
Callout
--text-callout · 16px
Button labels, alert body, input min
Headline
--text-headline · 17px/600
List-row titles (semibold)
Title 3
--text-title-3 · 20px
Modal headers
Title 2
--text-title-2 · 22px
Section headers
Title 1
--text-title-1 · 28px
Screen title
Large
--text-large · 34px
Onboarding
Display
--text-display · 40px
Marketing

Weights + line heights

TokenValueUse
--weight-regular400Body
--weight-medium500Button labels, headlines
--weight-semibold600Titles, emphasis
--weight-bold700Primary CTAs, displays
TokenValueUse
--leading-tight1.20Titles, displays
--leading-snug1.30Headlines
--leading-normal1.45Body (mobile glances)
--leading-relaxed1.55Long-form body
iOS zoom prevention — governance gate
All input fields must use font-size ≥ 16px (--text-callout or --text-body). Sub-16px triggers iOS Safari auto-zoom-on-focus. This is a hard lint failure — no exceptions.
§1.3

Spacing scale

4px base, 10-step. Generous density — thumb zones, not pixel zones. 16px horizontal screen padding default. Cramming causes misclicks.

--space-1
4px — inline gap
--space-2
8px — tight pair
--space-3
12px — default internal padding
--space-4
16px — card padding, between fields Screen default
--space-5
20px — section breaks within screen
--space-6
24px — between major blocks
--space-8
32px — screen padding bottom
--space-10
40px — onboarding generous
--space-12
48px — screen-to-screen separator
--space-16
64px — rare, top of empty state

Content widths

TokenValueUse
--content-mobile100vwFull-bleed default (phone)
--content-readable600pxCap on tablet for long text
--content-narrow400pxForms in modal context
§1.4

Elevation + shadows

Soft shadows with rounded corners for mobile-native feel. Cold neutral rgba — never warm oklch (that is Harbor's pattern). Shadows establish depth without heaviness.

Banned
Warm oklch shadows (Harbor pattern). Flat-with-borders-only chrome (looks like a desktop product on mobile).
sm
--shadow-sm
0 1px 2px rgba(0,0,0,0.06)
Cards at rest
md
--shadow-md
0 2px 8px rgba(0,0,0,0.10)
Cards raised
lg
--shadow-lg
0 4px 16px rgba(0,0,0,0.14)
Bottom sheet
xl
--shadow-xl
0 8px 32px rgba(0,0,0,0.20)
Modal, FAB
FAB
--shadow-fab
0 4px 12px rgba(0,0,0,0.18)
FAB — always raised
§1.5

Shape + radii

7-step radius scale. Generous defaults per iOS squircle convention. Rounded corners are mobile-native — sharp corners feel desktop-y on touch devices.

Banned
radius-none (sharp corners) on any touchable element. Super-rounded (>32px on cards) everywhere — visual chaos at small sizes.
sm · 8px
--radius-sm
Chips, badges
md · 12px
--radius-md
Buttons (default)
lg · 16px
--radius-lg
Cards, alerts
xl · 20px
--radius-xl
Sheet top corners
2xl · 24px
--radius-2xl
Feature cards
3xl · 32px
--radius-3xl
Onboarding cards
pill
--radius-full
Pill buttons, FAB
§1.6

Motion

Motion on mobile communicates spatial relationships — sheet rises from bottom, modal scales up from origin, page pushes from right (iOS) / fades (Android). Motion is part of the language, not optional.

Reduced-motion fallback
prefers-reduced-motion: opacity-only crossfades, no transforms, duration-fast. Never just shorten duration — remove transform animation entirely.
Durations
TokenValueUse
--duration-fast150msRipple (touch devices skip hover)
--duration-base250msSheet open, page push (default)
--duration-slow350msModal entry, drawer slide
Easings
TokenUse
--easing-emphasized0.2,0,0,1 — sheet slide
--easing-standard0.4,0,0.2,1 — default
--easing-decelerate0,0,0.2,1 — entries
--easing-accelerate0.4,0,1,1 — exits

Visual easing comparison

Emphasized (sheet slide in)
Easing reference — slow out (entries feel native, exits feel clean)
Decelerate — entry
Accelerate — exit
Banned motion patterns
Parallax on hero, scroll-jacking, animated GIF backgrounds, marquee text, rotating headlines.
§1.7

Iconography

Phosphor icons for web spec and cross-platform. SF Symbols for native iOS production; Material Symbols for native Android production. Phosphor's unified line+fill set lets tab-bar selected states swap cleanly within ONE family.

Never use Lucide for Current
Lucide is line-only, forcing a second icon family for fills. This creates a tone mismatch with platform-native production icons. Current web spec uses Phosphor exclusively — both line (default) and fill (selected state) variants within the same family.

Icon grid specification

ContextSizeVariantColor
Tab bar default24pxLine (regular)text-muted (inherit)
Tab bar selected24pxFillaccent-primary
List row leading20pxLinetext-secondary (inherit)
FAB28pxFillwhite on accent
Caption adjacent16pxLinetext-muted (inherit)
Severity / status20pxFillsemantic color

Phosphor sample grid — ~16 icons, line + fill variants

Line (default) — used for unselected tab bar and list rows
House
line
Search
line
Bell
line
User
line
Heart
line
Share
line
Chat
line
Settings
line
Fill variant — used for selected tab bar state (swap from line within same Phosphor family)
House
fill · selected
Search
fill · selected
Bell
fill · selected
User
fill · selected
Heart
fill · selected
Share
fill · selected
Chat
fill · selected
Settings
fill · selected

Tab bar — line to fill swap pattern

The same Phosphor icon family swaps from line variant (unselected) to fill variant (selected). No second icon family needed. Color shifts to accent-primary simultaneously.

9:41
For You Edit
A
Alex Chen
Liked your photo · 2m
S
Sara M.
Started following · 5m
Home
Search
Alerts
Profile
Dark · Home selected (fill)
9:41
Search Cancel
Search
R
Robinhood
Recent search
Home
Search
Alerts
Profile
Light · Search selected (fill)
Banned iconography
Emoji as icon. Mixing line + filled within the same row. Mixing icon families (Lucide line + Phosphor fill). Multi-color illustrations as UI icons. Animated icons except spinner / pull-to-refresh / FAB morph.
§1.8

Haptics

First-class on Current. Tokens map to platform APIs: iOS UIImpactFeedbackGenerator, Android HapticFeedbackConstants. Haptic is always paired with visible state change — never the only feedback.

Required pairing rule
Every haptic event MUST be paired with a visible state change. Haptic-only feedback is an accessibility failure. Required for: button press, toggle flip, swipe-action commit, pull-to-refresh trigger, sheet snap, drag end.
TokenPlatform APIUse when
--haptic-lightUIImpactFeedbackGenerator(.light)
HapticFeedbackConstants.KEYBOARD_TAP
UI taps, toggle changes, button press
--haptic-mediumUIImpactFeedbackGenerator(.medium)
HapticFeedbackConstants.VIRTUAL_KEY
Selection changes, segmented control, swipe threshold
--haptic-heavyUIImpactFeedbackGenerator(.heavy)
HapticFeedbackConstants.LONG_PRESS
Long-press confirmations, drag pick-up
--haptic-successUINotificationFeedbackGenerator(.success)
HapticFeedbackConstants.CONFIRM
Task completion, form submit success, milestone reached
--haptic-warningUINotificationFeedbackGenerator(.warning)
HapticFeedbackConstants.REJECT
Validation error (gentle), boundary reached
--haptic-errorUINotificationFeedbackGenerator(.error)
HapticFeedbackConstants.NO
Destructive confirmation, blocked action
Banned haptic patterns
Continuous haptic during scroll (vibration as user scrolls). Haptic without paired visible feedback (accessibility failure — assistive tech users miss the event).
§1.9

Safe areas

env(safe-area-inset-*) required for every fixed-position element. viewport-fit=cover must be set in the meta tag to opt into safe-area APIs. Status bar (top) + home indicator (bottom) must be respected.

Required: meta viewport
The page must declare: <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover"> — without viewport-fit=cover the env() safe-area APIs return 0 on all devices.
Tokenenv() mappingCovers
--safe-topenv(safe-area-inset-top)Status bar, notch
--safe-bottomenv(safe-area-inset-bottom)Home indicator
--safe-leftenv(safe-area-inset-left)Landscape notch
--safe-rightenv(safe-area-inset-right)Landscape notch
Computed tokenFormula
--bottom-nav-heightcalc(64px + var(--safe-bottom))
--header-heightcalc(56px + var(--safe-top))
--fab-offset-bottomcalc(16px + var(--safe-bottom))
env(safe-area-inset-top) ≈ 44px
For You
Scrollable content area
Tab bar · 64px
env(safe-area-inset-bottom) ≈ 34px
Purple = safe-area insets · Bottom nav height = 64px + safe-bottom = 98px on iPhone 14
/* Tab bar with safe-area */ .tab-bar { height: var(--bottom-nav-height); /* = calc(64px + env(safe-area-inset-bottom)) */ padding-bottom: var(--safe-bottom); position: fixed; bottom: 0; left: 0; right: 0; } /* FAB position */ .fab { position: fixed; bottom: var(--fab-offset-bottom); /* = calc(16px + env(safe-area-inset-bottom)) */ right: var(--space-4); }
§1.10

Touch targets

The Current keystone. Every interactive element has at least 44x44px hit target — even if visual size is smaller. Use padding to extend the tap area. Sub-44px = accessibility failure = governance lint failure.

TokenValueUse
--touch-target-min44pxAbsolute minimum — HIG + WCAG 2.5.5
--touch-target-md48pxDefault for any interactive element
--touch-target-lg56pxPrimary CTAs, FAB diameter

44px hit area visualization

Dashed blue border shows the 44x44px hit area. The colored square is the visual icon (24px). The transparent 20px padding on all sides extends the tap area to meet the 44px minimum.

24px visual
44×44 hit area
20px visual
44×44 hit area
See more
Text link
44px height min
24px checkbox
44×44 hit area
/* Pattern: small visual, 44px hit area via padding */ .icon-btn { width: var(--touch-target-min); /* = 44px */ height: var(--touch-target-min); display: flex; align-items: center; justify-content: center; /* SVG is 24px inside — padding makes it 44px */ }
§2

Voice + content

Sentence case. Action verbs. Labels under 12 characters. No em-dashes. Numbers as digits. Empty states show next action — not apology. Friendly microcopy is earned through context on consumer apps.

Do / Don't pairs

Do
Sign up
Sentence case, verb-only, short. Reads like speech.
Don't
Sign Up / Click to sign up
Title case is dated. Instruction prefix adds 5 words.
Do
Scan now
2 words. Action-first. Fits any button width.
Don't
Click here to scan a receipt
7 words. Instruction prefix. "Click" is wrong on touch.
Do
3 new messages
Digit. No fluff. Scannable in a glance.
Don't
You have three new messages waiting
Written number. Passive framing. 6 unnecessary words.
Do — empty state
Scan your first receipt
Action-forward. Names the next step. Zero apology.
Don't — empty state
You don't have any receipts yet
Dead-end framing. No path forward. User abandons.
Do — confirmation
Posted
Glanceable. Icon + 1 word. Done.
Don't
Your post has been successfully published!
8 words. Exclamation mark. Reads as system dialog.

Voice rules — quick reference

RuleCurrent requirementWhy
CaseSentence case everywhereReads like speech, not system UI
Button verbsSend, Save, Share, Post — never "OK" / "Done" / "Submit"Action-specific = native feel
Label lengthUnder 12 charactersTab bars + buttons truncate at 390px
NumbersAlways digits: "3 photos" not "three photos"Scanning beats reading on mobile
PunctuationNo em-dashes, no semicolonsShort punchy phrases; split into sentences
Empty statesName next action, never apologizeDead-end = abandonment on mobile
Body lengthUnder 2 lines on most cardsMobile users scroll, don't read
Friendly copy"Got it", "All set", "Let's go" — earned through contextConsumer app — warmth is correct here
§3

Foundation components

Layer 1 — the atomic building blocks. All have 44px minimum touch targets. All use native font stack. All have pressed-state (not hover-state) as primary interaction feedback.

1. Button — 4 variants × 3 sizes × 4 states

Variants
Sizes — all ≥ 44px height (touch target enforced)
sm 36px → use only non-primary actions in dense UI · md 44px → default · lg 56px pill → primary CTA
Touch target note
sm buttons (36px visual height) must have min-height:44px or equivalent padding extension for the hit area. Use sparingly — md is the default.
States — pressed (scale 0.96 + haptic-light) / disabled / loading
Current rule — no hover on touch
Pressed state (scale 0.96 + haptic-light) replaces hover as the primary interaction feedback. Any :hover-only state fails lint. Always pair with :focus-visible for keyboard users.

2. Icon button — 44px hit area enforced

All sizes — visual icon + 44px minimum hit area (dashed = hit area)
sm · 40px
hit 40px
md · 44px
hit 44px
lg · 48px
hit 48px
Required
All icon buttons must have aria-label. No tooltip alternative on mobile (no hover trigger). Hit-area extends to 44×44 even if the visual icon is 24px.

3. Field (text input / search) — 16px minimum font-size

Variants + states
Clear-X appears when field is filled — mobile fingers can't easily backspace
Phone format not recognized. Try +1 (555) 000-0000.
inputMode must match semantic
number inputs → inputmode="numeric" | email → inputmode="email" | phone → inputmode="tel". Correct keyboard type appears automatically on iOS/Android.

4. Selection controls — switch dominant on Current

iOS switch — dominant for binary settings (Current uses switch where Sentinel uses checkbox)
Push notifications
Get notified of activity
Dark mode
Matches system default
Segmented control — 3–4 options, slide indicator, haptic-medium on change
Checkbox + Radio — use when multi-select or exclusive-choice (not binary toggle)

5. Tag / Badge

Variants — sm (h:20px) and md (h:24px); pill for chips, rectangular for badges
New Active Beta 3 Draft 99+ Delivered Failed

6. Chip — filter, tag, removable; 40px touch hit area

All Photos Videos Music Saved Trending

7. Avatar — photo / initials / icon-fallback; stack with overflow

A
sm · 24px
JR
md · 32px
SK
lg · 40px
BM
xl · 56px
MP
xxl · 96px
icon fallback
Avatar stack — overlapping + overflow indicator
A
J
S
K
+3
7 members
§3C

Composite components

Layer 2 — composed from foundation components. Full-row tap targets. Gesture-aware. Sheet-based navigation preferred over center-screen modals.

8a. List-row card — the workhorse of Current

AC
Alex Chen
Design · 2 min ago
Notifications
Alerts and push settings
3
Do not disturb
Silence all notifications
Current rule
Entire card is the tap target — never nest multiple primary tap targets in one list row card. Full row 56–72px tall. Swipe left reveals destructive actions.

8b. Feature card + 8c. Action card

Premium membership
Unlock all features and remove ads.
Add to favorites
Save this for quick access from your home tab.

9. Bottom sheet — Current's modal replacement

3 snap points: peek (88px) · half (40–50%) · full (above safe-bottom)
Share post
Share via…
Copy link
Report post
Required behaviors
Swipe-down to dismiss. Backdrop tap dismisses. ScrollView inside locks to swipe direction at snap points. Drag handle 4×40px centered top. Destructive item uses error text, separated by divider.

10. Action sheet — native-style contextual menu

Alex Chen's post
Save post
Share to story
Not interested
Report post
Cancel

11. Snackbar / Toast — bottom-anchored, 4s auto-dismiss

Post shared successfully Undo
Upload failed. File over 10MB. Retry

12. Alert dialog — sparingly; only for irreversible actions

Delete account?
This permanently removes your profile, posts, and followers. This cannot be undone.
Use sparingly
Dialogs are heavyweight on mobile. Only use for irreversible system-level actions. Non-critical info → snackbar. Contextual options → bottom sheet.

13. FAB — max one per screen; represents THE primary action

Current rules
56px diameter md / 64px extended. Position: fixed bottom-right at --fab-offset-bottom + --space-4 right. Hides when keyboard opens. Morphs on scroll (shrinks to icon-only). --shadow-fab always. Max ONE per screen.

14. Pull-to-refresh — haptic-light on threshold cross

Refreshing…
Trigger: drag down past 80px threshold. Spinner appears as drag progresses, locks at threshold. haptic-light on threshold cross; haptic-success on data refresh complete.

15. Swipe list row actions

Archive
Delete
SM
Sara M.
Meeting tomorrow at 9am?
now
Left swipe reveals destructive (red) + non-destructive (yellow). haptic-medium on threshold cross. Swipe past threshold = commits. Swipe halfway = reveals + tap to confirm.

16. Loading states — skeleton preferred over full-screen spinner

Spinner · Skeleton · Progress
Spinner · 24px
Skeleton — matches content shape · Required for lists waiting >500ms
§3S

Screen layout vocabulary

Every screen is one of these six. Never build custom chrome — users already know how these work. Consistency is the affordance.

Banned
Hamburger menu as primary navigation (S1 tab bar only). Dead-end screens (every push must be poppable). Center-screen modals (use bottom sheets — S3). Floating transparent headers (solid header anchors nav).
For You
S1 · Tab bar shell
Detail
S2 · Stack nav
S3 · Bottom sheet
×
S4 · Fullscreen
S5 · Onboarding
S6 · Empty state
§3D

Domain layer — 5 named components

Layer 4 — product-specific composites that combine Layers 1–3 with Current rules: 44px touch targets, haptic feedback paired with visible state change, native gesture support, safe-area awareness.

26. Feed item — social / activity feed

Composes: List-row card + Avatar + Snackbar trigger + Bottom sheet. Long-press opens action bottom sheet (haptic-medium). Media tap opens full-screen takeover (S4).

9:41
AC
Alex Chen
2 min ago
Just shipped the new onboarding flow. 2 weeks of work in 3 screens.
Tap to view · S4 takeover
Home
Explore
Profile
26. Feed item
9:41
Stories
A
Alex
J
Jamie
S
Sara
Home
Search
Profile
27. Story bubbles
gradient ring = unwatched
muted ring = watched
2.4k
186
Share
@designmind
New onboarding pattern drops today
28. Video reel
S4 takeover · right-rail actions
swipe = next reel
9:41
Payment methods
VISA
•••• 4892
Expires 08/27
Default
•••• 2341
Expires 03/26
Add payment method
29. Payment method tile
swap opens sheet · remove needs alert
9:41
S
Sarah arriving in 3 min
Toyota Prius · ABC-1234
Picked up On the way Arriving Done
30. Ride tracking strip
sticky above tab bar
tap expands to full sheet + map
§4

UI patterns

Layer 3 — repeatable screen-level behaviors. Single-column forms, action-forward empty states, skippable onboarding, gestural search, scroll-collapsing detail screens, grouped settings.

Form layout — single-column only

Banned: multi-column forms
Multi-column form layouts fail at 390px viewport width — zigzag scanning pattern breaks on mobile. Always single-column, full-width fields.

Empty state — action-forward, never apologetic

Start a conversation

Send your first message and connect with people who matter.

Settings screen — grouped sections

Account
Edit profile
Notifications
Language English
Danger zone

Search experience

Recent searches
Design systems iOS HIG Material 3 Phosphor icons

Onboarding pager — 3–4 screens, skippable from any step

Your feed, your way

Curated content from people and topics you care about.

S5 · Onboarding pager
§5

Accessibility

WCAG 2.1 AA minimum. AAA for primary body text. Mobile-specific requirements on top of universal governance gates. Test matrix: VoiceOver (iOS), TalkBack (Android), keyboard nav.

WCAG conformance

AreaRequirementTarget
Body textContrast ratioAAA 7:1+
Large text (18pt+)Contrast ratioAA 3:1+
UI componentsContrast ratioAA 3:1+
Touch targetsMinimum hit area44×44px (WCAG 2.5.5)
Focus indicator2px outline + offset--color-focus-ring
Keyboard navFull tab orderAll interactive elements

Mobile test matrix

PlatformScreen readerBrowser
iOSVoiceOverSafari
AndroidTalkBackChrome
macOSVoiceOverSafari
WindowsNVDAFirefox

Keyboard navigation map

KeyAction
Tab / Shift+TabForward / back through focusable elements
Enter / SpaceActivate buttons, toggles, links
EscClose sheets, dialogs, menus — return focus to trigger
Arrow keysNavigate tabs, radio groups, menus, listboxes
Home / EndJump to first/last item in lists

Screen reader requirements

  • All icon-only buttons must have aria-label (no tooltip alternative on mobile)
  • All form fields must have programmatic labels (label[for] or aria-labelledby — never placeholder-as-label)
  • Live regions for toasts, async results, validation errors (aria-live="polite"; "assertive" for errors only)
  • Decorative SVGs: aria-hidden="true". Meaningful SVGs: role="img" + <title>
  • Haptic feedback MUST be paired with visible state change (haptic-only = a11y failure)
  • prefers-reduced-motion: opacity-only crossfades, remove transform animations entirely
§6

Governance + versioning

Universal governance from governance.spec.md + Current-specific overrides. 11 overrides total. SemVer. Deprecation: 2 MINOR versions before removal.

Current's 11 governance overrides

44px minimum touch target enforced
Any interactive element with hit area <44×44 fails lint
Body font-size ≥ 16px on inputs
iOS zoom prevention — hard lint failure, no exceptions
Native font stack required
Custom fonts require explicit brand override with performance budget
safe-area insets honored on all fixed-position elements
Any fixed element ignoring env(safe-area-inset-*) fails lint
Hover-only interactions banned
Any :hover-only state fails lint without :focus-visible or :active equivalent
Hamburger nav banned as primary navigation
Bottom tab bar (4–5 destinations) is the only primary nav pattern
Tab bar capped at 5 items
Overflow → "More" tab with bottom-sheet menu
Haptic feedback paired with visible state change
Haptic-only feedback fails accessibility review
Modal dialogs require bottom-sheet alternative consideration
Modal-only patterns flagged in review; sheet is preferred
inputMode matches input semantic
number → inputmode="numeric"; email → inputmode="email"; phone → inputmode="tel"
Both light + dark mode required
No "dark mode v2" excuses — both modes ship from day one

Versioning — SemVer

BumpWhen
MAJORBreaking token rename, removed component, breaking API change
MINORNew component, variant, token, deprecation notice
PATCHBug fix, doc update, non-visual refactor
Deprecation policy
Deprecated tokens/components ship for 2 MINOR versions before removal. Deprecation warnings logged in console at build time.

PR / release quality gate checklist

Contrast ratios verified (AA min, AAA body)
44px touch targets (all interactive)
Keyboard navigable
VoiceOver + TalkBack tested
prefers-reduced-motion honored
Both light + dark tokens defined
No raw hex in components
safe-area insets on all fixed elements
Changelog entry under correct semver bump
Input font-size ≥ 16px verified
BYOC

Required color roles

Bring Your Own Colors — if using a custom brand palette instead of Current's default pink/violet/cyan-green, these 25 roles MUST be supplied in both light AND dark mode. No "dark mode v2" excuses.

Rejected palettes — Prompt 1b will fail
Light mode only. Indigo/purple as DEFAULT brand primary (allowed as secondary/tertiary). Brand colors that fail 4.5:1 on either light or dark surface.

Brand accents — 3-step ramp, multi-accent allowed

#RoleLight exampleDark exampleRequired
1 Brand primary — light tint #FFE5ED rgba(255,51,102,0.18) Required
2 Brand primary — base #FF3366 #FF3366 Required
3 Brand primary — dark text #99001F #FFB3C6 Required
4–6 Brand secondary — same 3-step ramp Optional but recommended for variety Optional
7–9 Brand tertiary — same 3-step ramp Optional Optional

Surfaces — light (4) + dark (4) = 8 required

#RoleLightDark
10Page background Pure white OK Pure black OK (OLED)
11Card surface Light off-white iOS dark card
12Sheet surface Same as page Lifted over card
13Overlay (modal/sheet backdrop)rgba(0,0,0,0.40)rgba(0,0,0,0.60)

Text — light (4) + dark (4) = 8 required

#RoleLightDark
14Text primary #000 #FFF
15Text secondary #6B7280 #C7C7CC
16Text muted #9CA3AF #8E8E93
17Text on-accent #FFF #FFF (same)

Semantic (4) + Focus rings (2) = 6 required

#RoleLight / DarkRequirement
18Success iOS green conventionRequired
19Warning iOS orange conventionRequired
20Error iOS red conventionRequired
21Info iOS blue conventionRequired
22Focus ring — lightrgba(0,122,255,0.50)Required
23Focus ring — darkrgba(10,132,255,0.65)Required

Light + dark color pairs — visual reference

Page background
Light
Dark
Card surface
Light
Dark
Sheet surface
Light
Dark
Text primary
#000
#FFF
Accent primary
Same
Both
Focus ring
Light
Dark
When to pick

When to pick Current

Archetype selection guidance. Sample adjectives, real-world examples, and clear DO NOT PICK criteria with cross-links.

Pick Current if

Primary user is on mobile (phone, not tablet)
Session length is 1–5 min (multiple short sessions/day)
Navigation fits in 4–5 top-level destinations
Product is consumer-facing (not internal tools)
Bold saturation + multiple accent hues are OK
Touch + gesture are first-class (haptics, swipe, pinch)
Native shells (iOS, Android) are likely targets

Sample adjectives that route to Current Finding 5

Archetype-selection aid — demoted-adjective routing. If 3+ of your brand adjectives appear in this list, Current is likely your archetype.

bold immediate native glanceable friendly kinetic saturated gestural thumb-first consumer vibrant expressive mobile social transactional

Real-world examples — calibrate against these

Public apps that exemplify Current's principles. Use these to gut-check your archetype choice.

AppCurrent pattern demonstrated
Spotify mobileBottom tab bar, FAB-equivalent (play), bold accent green, native fonts
InstagramBottom tab bar, bottom sheets, swipe gestures, full-bleed media
Uber / LyftSingle primary action per screen, haptic feedback on key actions, safe-area aware
Airbnb mobileBottom sheets for filters, native controls throughout
TikTokFull-screen takeover layout (S4), bold saturation, vertical swipe navigation
Robinhood mobileGestural, glanceable data, single-accent variant
Cash App / VenmoSingle-accent variant of Current, transaction-focused
Linear mobile, Notion mobileSentinel-translated-to-Current: enterprise product with Current mobile companion

DO NOT pick Current if…

Desktop-first product → Sentinel or Harbor
Long-form reading is the activity → Meridian
Hours-long task focus → Sentinel
Data tables, dashboards, charts → Sentinel or Lattice
Marketing site / brand-forward → Harbor
Tablet-first (iPad Pro app) → consider Sentinel-with-touch adaptations
Failure modes

Failure modes

What breaks first when Current is misapplied. Recognizing these early prevents months of rework. Five predictable failure patterns with archetype corrections.

Failure 1
Desktop chrome incompetence — Current on a desktop product
Symptom: Bottom tab bar feels off on desktop. FAB looks lost on a 1920×1080 viewport. Touch-targets are oversized for mouse precision. Users feel the product is "made for phones."
Root cause: Current's vocabulary (bottom-anchored nav, FAB, sheets, swipe gestures) is platform-native to mobile; it reads as awkward on desktop where keyboard/mouse is primary.

Fix: Switch to Sentinel for desktop workflow; reserve Current for the mobile companion app.
Failure 2
Density famine — Current on data-heavy mobile
Symptom: A trader or analyst on mobile sees only 3 rows of data because Current's generous spacing and 17px body crowd the viewport. They zoom out, breaking touch targets.
Root cause: Current optimizes for tap-precision, not information density.

Fix: Switch to Lattice with mobile-aware adaptations (virtualization + horizontal scroll). Or rethink: maybe the user shouldn't be on mobile for this task at all.
Failure 3
Reading exhaustion — Current on long-form mobile reading
Symptom: User opens a docs page on mobile, finds 17px body at 1.45 leading inside generous chrome. Reading column is the full viewport (390px ≈ 50ch — too narrow). No sidebar TOC, no font controls.
Root cause: Current is glance-and-tap; Meridian is read-and-reference. The same generous spacing that helps scanning hurts sustained reading.

Fix: Switch to Meridian for the docs/article surface; keep Current for the rest of the mobile app shell.
Failure 4
Editorial tonal mismatch — Current for a marketing site
Symptom: Bold-saturation accents + native fonts read as "consumer app", not "brand-forward marketing." Site feels like a feature page, not a brand statement.
Root cause: Current's bold visual language is for product surfaces, not brand surfaces. Native fonts read as functional, not expressive. Harbor's fluid clamp typography and atmospheric atmosphere are purpose-built for this.

Fix: Switch to Harbor for marketing. Keep Current for the in-product experience.
Failure 5
Hamburger/desktop nav drift — Current implemented by desktop-first teams
Symptom: Desktop teams reach for hamburger menu on mobile, thinking "we'll keep it consistent across breakpoints." Engagement drops measurably. Primary nav becomes invisible to users.
Root cause: Hamburger is a Current banned pattern, but teams unfamiliar with mobile conventions default to it for cross-platform "consistency." This sacrifices the mobile UX for a pattern that doesn't serve either platform well.

Fix: This is NOT an archetype switch — it's a discipline fix within Current. Tighten governance: enforce the hamburger ban at lint time. Add the bottom tab bar requirement to your CI/CD quality gate.