System · Atlas

UX

Operator experience — states, heuristics, disclosure, empty/error.

ux.md

Meridian — ux.md

User experience — twin pillar with dx.md. Typed: uxLaws in lib/design/experience.ts.
Ranking: influences.md.
Primary absorb: Polaris Pro (meaning, predictable, juicy) + Cursor (keyboard, object-bound) + Fluent (rest→hover→selected, focus stroke) + Linear (quiet chrome) + Vercel (every state) + NN/g heuristics.

0. Twin pillars

UX (this file) and DX (dx.md) outrank cinema polish. If a change improves a screenshot but worsens operator clarity or agent reliability, reject it.

1. North-star UX principles

#PrincipleSourceMeridian rule
1Obsess over usefulnessVercel DEEvery control finishes a task
2Assign meaningPolarisColor/icon/depth = role
3Predictable then juicyPolarisSame look → same behavior; press felt
4Complexity available, not requiredVercelProgressive disclosure
5Quiet chrome, loud contentLinearBrand tint limited in chrome
6Object-bound AICursor appNo floating chatbot
7Focus ≠ fillFluent color/interactionRing/stroke for keyboard
8Visibility of statusNN/g #1Loading + agent activity always visible
9Error prevention & recoveryNN/g #5 · #9Specific copy + retry; confirm destructive
10Recognition over recallNN/g #6Show labels / kbd hints; don’t rely on memory

NN/g → Meridian map (compressed)

HeuristicEncode
1 StatusSkeletons, bg-activity-*, mono tool names
3 Control & freedomEscape, undo-friendly defaults, non-modal where possible
4 ConsistencyClosed radii / states / typeset presets
5–9 ErrorsemptyAndErrorOwned; never silent failure
7 FlexibilityKeyboard power + pointer defaults (data-surface)
8 MinimalistQuiet chrome; one idea per cinema frame
10 HelptypesetClass("docs") drawers — not walls of tip text

Agent UI disclosure (Nielsen on progressive disclosure)

  1. Level 1 — outcome + decision-critical ask
  2. Level 2 — activity / tool trace (one click)
  3. Never narrate every tool call in the primary pane — and never hide the ledger entirely.

2. Interaction state model (Fluent + Polaris)

rest → hover → pressed → selected
                 ↘ focus-visible (parallel track)
StateVisualNotes
RestNeutral surfaceNo brand border by default
Hoverbg-muted/30–40Quiet (Polaris: unobtrusive)
PressedSlightly stronger muted / scale microJuicy but short
Selectedbg-brand/10Not a thick brand border
Focus-visiblering-ring / thicker strokeFluent: focus changes container stroke, not only fill
DisabledReduced opacity + pointer-events-noneStill contrast-safe where needed
Dangerbg-destructive/10 + destructive textNever brand

Rest → hover → selected (Fluent)

Controls generally get darker through interaction on paper; selected uses brand wash. Dark mode follows token aliases — never hardcode branches.

3. Control affordances

ControlAffordAnti
Button primaryBrand fill, clear labelNeon, chevron garnish
Button ghostTransparent + hover mutedBrand border chrome
Inputrounded-lg, thin border or edgeShadow-md wells
Row / nav itemFull-row hit target, h-7–h-9Tiny icon-only without label (unless toolbar)
Checkbox / toggleInstant check feedbackNovelty bounce loops
Menu itemrounded-lg, focus = accent washNested hover traps

4. Keyboard & command

Cursor / Linear operators expect:

Shortcut patternUX
⌘K / command paletteJump anywhere
Arrow + Enter in listsMove selection
⌘↵ in agent composerSubmit bound to object
Tab / Shift+TabVisible focus rings always

Show kbd hints in mono near search — never as the only discoverability path.

5. Information architecture patterns

Indexes (Polaris density + Linear rows)

  1. Filter/search chrome (compact)
  2. Dense table/list
  3. Selection opens detail or agent rail
  4. Empty / error / loading owned (Vercel)

Detail

  1. Object title + mono ID
  2. Primary actions
  3. Properties
  4. Optional AI rail bound to this ID

Marketing film

One idea per svh stage — see cinema laws. CTAs: one primary + optional secondary; no chevrons.

6. Feedback & messaging

KindTreatment
SuccessBrand-linked success token + short copy
Warningwarning semantic — not brand
DestructiveConfirm; destructive colors
Agent activitybg-activity-* + mono tool name
Toast / inlinePrefer inline near object; avoid modal spam

7. Progressive disclosure

Default visibleReveal when needed
Core nav + indexAgent rail
Primary CTASecondary tools
Paper applicationTheater marketing
Summary metaFull provenance timeline

OpenAI-style lesson: product is hero; complexity waits (influences §9).

8. Empty, loading, error (mandatory)

Every index/detail must define:

StateMinimum
LoadingSkeleton or quiet mono “Loading…” — no layout jump
EmptyOne sentence + one action
ErrorSpecific message + retry
No permissionClear next step

9. Touch vs pointer

LaneTargetNotes
mobile≥44×44Fluent / Apple HIG aligned
application32px controlsDense but clickable
desktop28px chrome OKKeyboard primary

10. Accessibility UX

  1. Never color-only meaning (Fluent + WCAG)
  2. Body text ≥ 4.5:1; large ≥ 3:1 (check:contrast)
  3. Theater stages lift --brand via data-tone="theater"
  4. prefers-reduced-motion honored (see animations.md)
  5. Labels on icon buttons (aria-label)

11. Anti-patterns

BanWhy
Floating chatbotUnbound AI
Brand borders on every selected thingNoise
Hover-only critical actionsPointer exclusive
Scroll-jacking tutorialsCinema ban
Status color as decorationPolaris meaning
Infinite onboarding modalsUsefulness fails

12. Proofs

ProofWhere
Resource empty / loading / errorWorkbench + Composer demos
Outcome → activity traceAgent rails (data-slot="agent-outcome" / agent-trace)
Keyboard parity/meridian/theme#keyboard · /meridian/surfaces#keyboard

13. Agent checklist

  • States: rest/hover/focus/selected/disabled/empty/loading/error
  • Selected = brand wash; focus = ring
  • AI: outcome first, trace one click away
  • Keyboard path exists
  • meridian-a11y + contrast + experience gates

Sources

Raw