{"topic":"foundations","format":"markdown","content":"# Foundations\n\nGoal: one system for everything downstream. Nothing here is visible on its\nown. It sets up the tokens and global defaults that keep every other rule\nconsistent, and fixes the details users only notice when they're wrong.\n\nThe UI Polish token roles live in `tokens.css` (`--ui-*`). Map each role onto\nthe project's existing variable instead of adding a second system.\n\n## 1. Token inventory\n\nRead the project's existing tokens first. Consolidate before decorating:\n\n- **Durations & easings**: if the project animates with literals, introduce\n  the ladder from [motion.md](motion.md) as CSS custom properties (or the\n  project's theme keys) and migrate existing literals to it as you touch them.\n- **Radius**: one scale (e.g. 10 / 14 / 22 + full). See\n  [surfaces.md](surfaces.md).\n- **Shadows, borders, semantic colors**: name what repeats. Three ad-hoc\n  grays doing the same job become one token.\n- Put new tokens where the project already keeps them (its theme config or\n  `:root`). Never inline a value that two components share.\n\nDo not rename or restructure existing tokens — extend only.\n\n## 2. Focus (one definition, everywhere)\n\nThe most common focus defect is `outline: none` with nothing in its place. The\nsecond is a ring around the control, dark or haloed, that shouts louder than\nthe control itself. Draw focus inside the control's own edge instead: a thin\nline in the control's text color, so it reads on a dark primary button and a\nlight ghost button alike, and nothing grows outside the shape.\n\n```css\n:focus-visible {\n  outline: 1.5px solid color-mix(in oklch, currentColor 55%, transparent);\n  outline-offset: -1.5px; /* hugs the inside of the edge, follows the radius */\n}\n```\n\nFields darken and thicken their edge, nothing outside:\n\n```css\n.input:focus {\n  box-shadow: inset 0 0 0 1px var(--ui-field-focus);\n}\n```\n\nInvalid fields use the same edge in the danger color.\n\n- `:focus-visible`, not `:focus`, on buttons and links: clicks shouldn't paint\n  focus. Text fields show it on `:focus`, because the caret already tells.\n- Check it on every surface a control sits on: if the line disappears\n  (a control whose text color matches its fill), raise its alpha.\n- Pick one field treatment and use it on every field.\n- Never animate it; never remove it.\n\n## 3. Selection, caret, taps\n\nCheap, high-signal details — each one line, applied once:\n\n```css\n::selection { background: /* accent at ~20-30% */; color: /* readable fg */; }\nhtml { -webkit-tap-highlight-color: transparent; }\n```\n\nCustom selection color is near-universal in polished products and absent in\nunpolished ones. Verify selected text stays readable (don't use a dark accent\nwith dark text).\n\n## 4. Global rendering hygiene\n\n```css\nhtml {\n  -webkit-font-smoothing: antialiased;\n  scrollbar-gutter: stable;        /* no layout shift when dialogs lock scroll */\n}\n* { min-width: 0; }                /* flex/grid children can actually shrink */\n```\n\n- If the project uses `scroll-behavior: smooth`, pair it with\n  `@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }`.\n- Scroll lock for modals: `html:has(dialog[open]) { overflow: hidden; }`\n  (with `scrollbar-gutter: stable` this is shift-free).\n- Dark mode: verify `color-scheme: light dark` (or the applicable one) is set\n  so native controls, scrollbars, and form elements match the theme.\n\n## 5. Interaction affordances\n\n- Two cursors only: `pointer` on things that act, `default` everywhere\n  else, including disabled, busy and draggable things. No `not-allowed`,\n  `progress`, `grab` or `wait`; the state belongs in the control's look, not\n  the cursor. Text inputs keep the browser's own text cursor.\n- Hit areas ≥ 40×40px for touch targets. Grow small icon buttons with\n  padding or a pseudo-element (`::after { inset: -8px }`), never by scaling\n  the icon.\n- `user-select: none` on button labels and other pure-control text so\n  double-clicks don't highlight chrome; never on content.\n- `touch-action: manipulation` on buttons removes the 300ms-legacy/double-tap\n  ambiguity on some mobile browsers.\n\n## 6. Semantic sweep\n\nWhile you're at the foundation layer, fix what CSS can't:\n\n- Clickable `<div>`s → `<button>` (type=\"button\") or `<a>`. This is the\n  cheapest accessibility *and* polish win — buttons get focus, Enter/Space,\n  and touch behavior for free.\n- Icon-only buttons get `aria-label`.\n- Images get real `alt` text (or `alt=\"\"` when decorative) and explicit\n  dimensions/`aspect-ratio` to prevent layout shift.\n- Exactly one `<h1>` per view; heading levels don't skip.\n\n## 7. Check before moving on\n\n- Tab through the main flow: every stop visible, order sensible.\n- Select text on a few surfaces: readable.\n- Open a modal: no background scroll, no width jump.\n- Toggle dark mode (if present): tokens hold, no hardcoded whites/blacks\n  bleeding through.\n"}