--- name: ui-design description: "Applies distinctive, non-generic visual design to web UIs: typography, color systems, spacing, motion, backgrounds. Use when building or restyling any UI, landing page, dashboard, or component, or when output looks generic, bland, or like AI slop." license: MIT metadata: author: lammanhhoang version: "1.0.0" --- # UI Design ## The failure mode LLM-generated UIs converge on distribution: Inter on a white page, a purple-to-blue gradient hero, three feature cards with emoji icons, `border-radius: 8px` on everything. Each choice is individually defensible; together they are the statistical center of the training data, and users read that center instantly as AI slop. Your job is to make deliberate choices off that center. **Anti-second-order-convergence warning:** the escape hatch is also on distribution. If every "creative" choice lands on Space Grotesk on dark navy with a teal accent, that's still convergence — just a different attractor. Make a choice the training data wouldn't. If you can picture a hundred other generated sites with the same look, pick again. ## When to use - Building any new UI: landing page, dashboard, app screen, component, hosted page. - Restyling: "make it look better", "less generic", "more premium", "it looks bland/AI-generated". - Adding motion, backgrounds, or a color system to an existing UI. **When NOT to use:** - The project has a mandated design system or brand tokens (Material, corporate brand kit). Apply their tokens; only the Motion and Quality floor sections still apply. - Charts and data visualizations — use a dedicated data-visualization skill for those, if one is available; this skill governs the page around them. - Email templates — Gmail/Outlook strip the CSS this skill relies on. - Pure logic/backend work with no rendered surface. ## Rules RFC-2119 keywords. Cite by number when reviewing ("violates rule #3"). 1. You MUST NOT use any banned default from the table below. 2. You MUST pick exactly ONE characterful display face and ONE workhorse text face. No third family except an optional mono for data/code. 3. Display font weights MUST pair at extremes: 100–200 against 800–900, where the face supports it; single-weight display faces (Instrument Serif, Libre Caslon Display) compensate with extreme size instead. Never 400 vs 600 — that reads as no decision at all. 4. Type scale MUST jump at least 3x between body and hero: body 16–18px, hero ≥ 56px (mobile) / ≥ 72px, typically up to ~128px (desktop). Use `clamp()` for the in-between. 5. Color system MUST be ONE dominant color + ONE sharp accent, defined as CSS variables before any component is written. Accent covers ≤ 10% of any viewport. 6. Grays MUST be tinted with the dominant hue. Pure `#000`, `#fff`, `#808080` are banned (rule #1); use e.g. `oklch(0.97 0.01 )` instead of white. 7. You SHOULD author and manipulate color in OKLCH — perceptually uniform lightness makes ramps and hover states predictable. Fall back to hex only for legacy targets. 8. Spacing MUST sit on a consistent 4px or 8px rhythm. No 13px paddings. 9. Body text contrast MUST meet WCAG AA: 4.5:1 minimum (3:1 for ≥ 24px text and UI glyphs). Non-negotiable — no aesthetic justifies failing it. 10. Animate `transform` and `opacity` ONLY. Never animate `width`, `height`, `top`, `left`, `margin`, or `box-shadow` (paint/layout thrash). 11. `prefers-reduced-motion` support is MANDATORY on every animated surface. 12. Border radius MUST be a hierarchy (e.g. 2px controls, 8px cards, 24px modals) or deliberately sharp everywhere — never one uniform radius applied to everything. 13. Every design MUST have exactly ONE signature element — spend your boldness in one place (an oversized display headline, a hard-edged split layout, one wild accent usage). Two signature elements compete; zero is slop. 14. Backgrounds MUST NOT be flat default white or black. Minimum: tinted neutral. Better: grain, one-hue gradient, or structural texture (see references/AESTHETICS.md). ### Banned defaults | Banned | Why | Instead | |---|---|---| | Inter / Roboto / Arial / Open Sans / system-ui as **display** font | The single strongest slop signal | Characterful display face (see Typography); Inter is acceptable for dense body/UI text only | | Purple-to-blue gradient on white | The canonical AI-generated hero | 3+ stop gradient within ONE hue family, or a flat tinted field | | Teal accent on dark navy ("safe creative") | Second-order convergence attractor | Pick a hue pair you haven't seen this week | | Uniform border-radius on every element | Reads as unexamined default | Radius hierarchy (rule #12) or all-sharp | | Pure `#000` / `#fff` / `#808080` untinted grays | Dead, print-era neutrals | Tint every neutral with the dominant hue (rule #6) | | Three-card feature row with emoji icons | Layout cliché + emoji = double slop | Asymmetric grid, numbered editorial list, or a single dominant feature with supporting details | ## Workflow — two passes, plan before code ### PASS 1 — Design plan (no code yet) Write a compact plan containing exactly: 1. **Vibe**: 2–4 adjectives ("brutalist editorial, warm, unhurried"). 2. **Palette**: 4–6 named values with hex or oklch — `--bg`, `--surface`, `--ink`, `--dominant`, `--accent` (+ optional `--muted`). 3. **Font pairing with roles**: display face @ weights, text face @ weights, optional mono. 4. **ASCII wireframe** of the key screen (10–20 lines, shows hierarchy and asymmetry). 5. **The ONE signature element** and where the boldness budget is spent. ### PASS 2 — Self-critique the PLAN, then code Before writing any code, audit the plan: - Check every row of the banned-defaults table against the plan. - Convergence check: "Would 100 other models produce this exact plan?" If yes to any part, replace that part. - Contrast check the palette pairs that will carry text (rule #9). Only then write code, **tokens first**: create the CSS-variable / design-tokens layer before any component. Start from assets/design-tokens.example.css and replace values with the plan's — never ship the example values verbatim (that would be convergence too). ## Typography ONE display + ONE text face (rule #2). Where to look: variable fonts with personality axes, grotesques with quirks, humanist and high-contrast serifs — Google Fonts and Fontshare are free and license-safe. Quick picks: | Face | Source | Vibe | Role | |---|---|---|---| | Fraunces | Google | Warm editorial serif; variable SOFT/WONK axes | Display | | Instrument Serif | Google | High-contrast, fashion-editorial | Display | | Bricolage Grotesque | Google | Grotesque with quirks, friendly-loud | Display | | Clash Display | Fontshare | Techy fashion, sharp terminals | Display | | Syne | Google | Art-house, futurist width | Display | | Unbounded | Google | Ultra-wide, experimental | Display (short strings only) | | Archivo (Expanded) | Google | Industrial, signage | Display | | Newsreader | Google | Literary, long-form | Display or body serif | | General Sans | Fontshare | Neutral-plus workhorse | Text | | Hanken Grotesk / Schibsted Grotesk | Google | Clean text with character | Text | | IBM Plex Mono / Fragment Mono | Google | Data, labels, numerals | Mono | Pairing logic, weights, and 15+ more candidates: read references/AESTHETICS.md (load whenever picking fonts or building a palette). Concrete settings: hero letter-spacing −0.02em to −0.04em at 800–900 weight; body line-height 1.5–1.7; display line-height 0.95–1.1; use `font-feature-settings` / `font-variant-numeric: tabular-nums` for data tables. ## Color - ONE dominant + ONE accent (rule #5), everything else is tinted-neutral ramps of the dominant hue. - Build ramps in OKLCH: fix hue, step lightness 0.98 → 0.15, let chroma peak mid-ramp. Worked recipes and three complete example palettes: references/AESTHETICS.md. - Accent picking: rotate hue 150–210° from dominant for tension, 20–40° for harmony. Never default to teal-on-navy (banned table). - Dark themes: raise chroma slightly on dark surfaces (colors dull on dark), never use pure black — `oklch(0.16–0.22 0.01–0.03 )`. ## Backgrounds Never flat default white/black (rule #14). Escalating options — pick per vibe: 1. **Tinted neutral** — minimum viable: `oklch(0.97 0.012 )`. 2. **Grain/noise** — SVG `feTurbulence` overlay at 3–6% opacity; kills the "vector-clean" AI look instantly. 3. **One-hue gradient, 3+ stops** — same hue family, varying L and C only. 4. **Structural texture** — hairline grid, dot matrix, or oversized clipped display type at low opacity behind content. Exact CSS for all four (copy-paste ready): references/AESTHETICS.md § Backgrounds (load when styling any page-level surface). ## Motion - `transform` + `opacity` only (rule #10). - ONE orchestrated page-load reveal: elements rise 8–16px and fade in, staggered with `animation-delay` steps of 60–100ms. Everything after load is micro-interaction only. - Durations: micro-interactions 120–200ms; entrances 300–500ms; exits at ~2/3 of the entrance duration (exits MUST be faster than entrances). - Easing: entrances `cubic-bezier(0.22, 1, 0.36, 1)` (fast-out, settled); exits `ease-in`. No bounce/elastic/spring overshoot on professional UIs — ever. - `prefers-reduced-motion` (rule #11): collapse to opacity-only or none. ```css @keyframes rise { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } } .reveal > * { animation: rise 440ms cubic-bezier(0.22, 1, 0.36, 1) backwards; } .reveal > :nth-child(2) { animation-delay: 80ms; } .reveal > :nth-child(3) { animation-delay: 160ms; } .reveal > :nth-child(4) { animation-delay: 240ms; } @media (prefers-reduced-motion: reduce) { .reveal > * { animation: none; } } ``` ## Quality floor — ship silently Do all of these on every UI without being asked and **without announcing them**: - Responsive at 375px, 768px, 1280px viewports. Wide content scrolls in its own `overflow-x: auto` container; the page never scrolls horizontally. - Visible keyboard focus: style `:focus-visible` with a 2px offset outline in the accent color. Never `outline: none` without a replacement. - WCAG AA contrast on all text (rule #9). - `prefers-reduced-motion` handled (rule #11). - Tap targets ≥ 44px on touch layouts. ## What NOT to do - Do not present the user a menu of design directions — commit to one plan and build it. - Do not add a second font family "for variety" (rule #2). - Do not sprinkle the accent color everywhere; scarcity is what makes it sharp (rule #5). - Do not animate on scroll-into-view for every section — one load reveal, then restraint. - Do not announce the quality floor ("I also added accessibility!"). Just ship it. - Do not reuse the example token values from assets/design-tokens.example.css verbatim. ## Files - references/AESTHETICS.md — full font catalog with pairings, OKLCH palette recipes with worked examples, background technique cookbook. Load when picking fonts, building a palette, or styling backgrounds. - assets/design-tokens.example.css — filled-in example token file (color, type, spacing, radii, shadows, motion). Copy the structure, replace the values per PASS 1.