|
| 1 | +--- |
| 2 | +alwaysApply: true |
| 3 | +--- |
| 4 | + |
| 5 | +Concise rules for building accessible, fast, delightful UIs. Use MUST/SHOULD/NEVER to guide decisions. |
| 6 | + |
| 7 | +## Interactions |
| 8 | + |
| 9 | +### Keyboard |
| 10 | + |
| 11 | +- MUST: Full keyboard support per [WAI-ARIA APG](https://www.w3.org/WAI/ARIA/apg/patterns/) |
| 12 | +- MUST: Visible focus rings (`:focus-visible`; group with `:focus-within`) |
| 13 | +- MUST: Manage focus (trap, move, return) per APG patterns |
| 14 | +- NEVER: `outline: none` without visible focus replacement |
| 15 | + |
| 16 | +### Targets & Input |
| 17 | + |
| 18 | +- MUST: Hit target ≥24px (mobile ≥44px); if visual <24px, expand hit area |
| 19 | +- MUST: Mobile `<input>` font-size ≥16px to prevent iOS zoom |
| 20 | +- NEVER: Disable browser zoom (`user-scalable=no`, `maximum-scale=1`) |
| 21 | +- MUST: `touch-action: manipulation` to prevent double-tap zoom |
| 22 | +- SHOULD: Set `-webkit-tap-highlight-color` to match design |
| 23 | + |
| 24 | +### Forms |
| 25 | + |
| 26 | +- MUST: Hydration-safe inputs (no lost focus/value) |
| 27 | +- NEVER: Block paste in `<input>`/`<textarea>` |
| 28 | +- MUST: Loading buttons show spinner and keep original label |
| 29 | +- MUST: Enter submits focused input; in `<textarea>`, ⌘/Ctrl+Enter submits |
| 30 | +- MUST: Keep submit enabled until request starts; then disable with spinner |
| 31 | +- MUST: Accept free text, validate after—don't block typing |
| 32 | +- MUST: Allow incomplete form submission to surface validation |
| 33 | +- MUST: Errors inline next to fields; on submit, focus first error |
| 34 | +- MUST: `autocomplete` + meaningful `name`; correct `type` and `inputmode` |
| 35 | +- SHOULD: Disable spellcheck for emails/codes/usernames |
| 36 | +- SHOULD: Placeholders end with `…` and show example pattern |
| 37 | +- MUST: Warn on unsaved changes before navigation |
| 38 | +- MUST: Compatible with password managers & 2FA; allow pasting codes |
| 39 | +- MUST: Trim values to handle text expansion trailing spaces |
| 40 | +- MUST: No dead zones on checkboxes/radios; label+control share one hit target |
| 41 | + |
| 42 | +### State & Navigation |
| 43 | + |
| 44 | +- MUST: URL reflects state (deep-link filters/tabs/pagination/expanded panels) |
| 45 | +- MUST: Back/Forward restores scroll position |
| 46 | +- MUST: Links use `<a>`/`<Link>` for navigation (support Cmd/Ctrl/middle-click) |
| 47 | +- NEVER: Use `<div onClick>` for navigation |
| 48 | + |
| 49 | +### Feedback |
| 50 | + |
| 51 | +- SHOULD: Optimistic UI; reconcile on response; on failure rollback or offer Undo |
| 52 | +- MUST: Confirm destructive actions or provide Undo window |
| 53 | +- MUST: Use polite `aria-live` for toasts/inline validation |
| 54 | +- SHOULD: Ellipsis (`…`) for options opening follow-ups ("Rename…") and loading states ("Loading…") |
| 55 | + |
| 56 | +### Touch & Drag |
| 57 | + |
| 58 | +- MUST: Generous targets, clear affordances; avoid finicky interactions |
| 59 | +- MUST: Delay first tooltip; subsequent peers instant |
| 60 | +- MUST: `overscroll-behavior: contain` in modals/drawers |
| 61 | +- MUST: During drag, disable text selection and set `inert` on dragged elements |
| 62 | +- MUST: If it looks clickable, it must be clickable |
| 63 | + |
| 64 | +### Autofocus |
| 65 | + |
| 66 | +- SHOULD: Autofocus on desktop with single primary input; rarely on mobile |
| 67 | + |
| 68 | +## Animation |
| 69 | + |
| 70 | +- MUST: Honor `prefers-reduced-motion` (provide reduced variant or disable) |
| 71 | +- SHOULD: Prefer CSS > Web Animations API > JS libraries |
| 72 | +- MUST: Animate compositor-friendly props (`transform`, `opacity`) only |
| 73 | +- NEVER: Animate layout props (`top`, `left`, `width`, `height`) |
| 74 | +- NEVER: `transition: all`—list properties explicitly |
| 75 | +- SHOULD: Animate only to clarify cause/effect or add deliberate delight |
| 76 | +- SHOULD: Choose easing to match the change (size/distance/trigger) |
| 77 | +- MUST: Animations interruptible and input-driven (no autoplay) |
| 78 | +- MUST: Correct `transform-origin` (motion starts where it "physically" should) |
| 79 | +- MUST: SVG transforms on `<g>` wrapper with `transform-box: fill-box` |
| 80 | + |
| 81 | +## Layout |
| 82 | + |
| 83 | +- SHOULD: Optical alignment; adjust ±1px when perception beats geometry |
| 84 | +- MUST: Deliberate alignment to grid/baseline/edges—no accidental placement |
| 85 | +- SHOULD: Balance icon/text lockups (weight/size/spacing/color) |
| 86 | +- MUST: Verify mobile, laptop, ultra-wide (simulate ultra-wide at 50% zoom) |
| 87 | +- MUST: Respect safe areas (`env(safe-area-inset-*)`) |
| 88 | +- MUST: Avoid unwanted scrollbars; fix overflows |
| 89 | +- SHOULD: Flex/grid over JS measurement for layout |
| 90 | + |
| 91 | +## Content & Accessibility |
| 92 | + |
| 93 | +- SHOULD: Inline help first; tooltips last resort |
| 94 | +- MUST: Skeletons mirror final content to avoid layout shift |
| 95 | +- MUST: `<title>` matches current context |
| 96 | +- MUST: No dead ends; always offer next step/recovery |
| 97 | +- MUST: Design empty/sparse/dense/error states |
| 98 | +- SHOULD: Curly quotes (" "); avoid widows/orphans (`text-wrap: balance`) |
| 99 | +- MUST: `font-variant-numeric: tabular-nums` for number comparisons |
| 100 | +- MUST: Redundant status cues (not color-only); icons have text labels |
| 101 | +- MUST: Accessible names exist even when visuals omit labels |
| 102 | +- MUST: Use `…` character (not `...`) |
| 103 | +- MUST: `scroll-margin-top` on headings; "Skip to content" link; hierarchical `<h1>`–`<h6>` |
| 104 | +- MUST: Resilient to user-generated content (short/avg/very long) |
| 105 | +- MUST: Locale-aware dates/times/numbers (`Intl.DateTimeFormat`, `Intl.NumberFormat`) |
| 106 | +- MUST: Accurate `aria-label`; decorative elements `aria-hidden` |
| 107 | +- MUST: Icon-only buttons have descriptive `aria-label` |
| 108 | +- MUST: Prefer native semantics (`button`, `a`, `label`, `table`) before ARIA |
| 109 | +- MUST: Non-breaking spaces: `10 MB`, `⌘ K`, brand names |
| 110 | + |
| 111 | +## Content Handling |
| 112 | + |
| 113 | +- MUST: Text containers handle long content (`truncate`, `line-clamp-*`, `break-words`) |
| 114 | +- MUST: Flex children need `min-w-0` to allow truncation |
| 115 | +- MUST: Handle empty states—no broken UI for empty strings/arrays |
| 116 | + |
| 117 | +## Performance |
| 118 | + |
| 119 | +- SHOULD: Test iOS Low Power Mode and macOS Safari |
| 120 | +- MUST: Measure reliably (disable extensions that skew runtime) |
| 121 | +- MUST: Track and minimize re-renders (React DevTools/React Scan) |
| 122 | +- MUST: Profile with CPU/network throttling |
| 123 | +- MUST: Batch layout reads/writes; avoid reflows/repaints |
| 124 | +- MUST: Mutations (`POST`/`PATCH`/`DELETE`) target <500ms |
| 125 | +- SHOULD: Prefer uncontrolled inputs; controlled inputs cheap per keystroke |
| 126 | +- MUST: Virtualize large lists (>50 items) |
| 127 | +- MUST: Preload above-fold images; lazy-load the rest |
| 128 | +- MUST: Prevent CLS (explicit image dimensions) |
| 129 | +- SHOULD: `<link rel="preconnect">` for CDN domains |
| 130 | +- SHOULD: Critical fonts: `<link rel="preload" as="font">` with `font-display: swap` |
| 131 | + |
| 132 | +## Dark Mode & Theming |
| 133 | + |
| 134 | +- MUST: `color-scheme: dark` on `<html>` for dark themes |
| 135 | +- SHOULD: `<meta name="theme-color">` matches page background |
| 136 | +- MUST: Native `<select>`: explicit `background-color` and `color` (Windows fix) |
| 137 | + |
| 138 | +## Hydration |
| 139 | + |
| 140 | +- MUST: Inputs with `value` need `onChange` (or use `defaultValue`) |
| 141 | +- SHOULD: Guard date/time rendering against hydration mismatch |
| 142 | + |
| 143 | +## Design |
| 144 | + |
| 145 | +- SHOULD: Layered shadows (ambient + direct) |
| 146 | +- SHOULD: Crisp edges via semi-transparent borders + shadows |
| 147 | +- SHOULD: Nested radii: child ≤ parent; concentric |
| 148 | +- SHOULD: Hue consistency: tint borders/shadows/text toward bg hue |
| 149 | +- MUST: Accessible charts (color-blind-friendly palettes) |
| 150 | +- MUST: Meet contrast—prefer [APCA](https://apcacontrast.com/) over WCAG 2 |
| 151 | +- MUST: Increase contrast on `:hover`/`:active`/`:focus` |
| 152 | +- SHOULD: Match browser UI to bg |
| 153 | +- SHOULD: Avoid dark color gradient banding (use background images when needed) |
0 commit comments