---
name: frontend-design
description: >-
Guide for creating distinctive, production-grade frontend interfaces. Covers
visual design systems, typography, color, motion, spatial composition, and
web interface standards. Use when designing UI, establishing design systems,
or reviewing visual quality of frontend code.
---
# Frontend Design
Create interfaces with intentional aesthetics and professional execution. Avoid generic "template" looks.
## Design Process
Before writing code, answer these questions:
1. **Purpose** — What problem does this interface solve? Who uses it?
2. **Tone** — What aesthetic direction? (brutalist, luxury, playful, editorial, retro-futuristic, etc.)
3. **Constraints** — Device targets, accessibility requirements, performance budget
4. **Signature** — What single element makes this memorable?
Commit to a direction. Timid design is worse than bold design with flaws.
## Typography
### Font Selection
Avoid overused defaults. Choose typefaces that reinforce the interface's personality.
```css
/* BAD: Generic, seen everywhere */
font-family: 'Inter', 'Roboto', 'Arial', sans-serif;
/* GOOD: Distinctive and paired intentionally */
--font-display: 'Instrument Serif', serif; /* headings */
--font-body: 'Satoshi', sans-serif; /* body text */
--font-mono: 'Berkeley Mono', monospace; /* code */
```
### Typographic Details
```css
/* Tabular numbers for aligned columns */
.data-cell { font-variant-numeric: tabular-nums; }
/* Balanced headings prevent widow words */
h1, h2, h3 { text-wrap: balance; }
/* Proper ellipsis character */
.truncate::after { content: '\2026'; } /* … not ... */
```
Handle text overflow deliberately:
```css
/* Single line truncation */
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Multi-line clamping */
.clamp-3 { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
/* Flex children need min-w-0 for truncation to work */
.flex-child { min-width: 0; }
```
## Color Systems
### Theme Architecture
Build on CSS custom properties with clear semantic naming.
```css
:root {
/* Primitives */
--gray-50: oklch(0.98 0.005 240);
--gray-900: oklch(0.15 0.01 240);
--accent: oklch(0.65 0.25 30);
/* Semantic tokens */
--color-bg: var(--gray-50);
--color-text: var(--gray-900);
--color-text-muted: oklch(0.55 0.01 240);
--color-border: oklch(0.88 0.005 240);
--color-accent: var(--accent);
--color-accent-hover: oklch(0.58 0.28 30);
}
@media (prefers-color-scheme: dark) {
:root {
--color-bg: var(--gray-900);
--color-text: var(--gray-50);
--color-text-muted: oklch(0.65 0.01 240);
--color-border: oklch(0.28 0.005 240);
}
}
```
### Color Principles
- **Commit to a palette** — 1 dominant + 1–2 accents. Avoid 5-color rainbows.
- **Use oklch** — Perceptually uniform, better than hex/hsl for programmatic palettes.
- **Contrast ratios** — 4.5:1 minimum for body text, 3:1 for large text (WCAG AA).
## Motion & Animation
### Rules
1. **Respect user preference**: Always honor `prefers-reduced-motion`
2. **Animate only `transform` and `opacity`**: These are GPU-composited, everything else triggers layout/paint
3. **Never use `transition: all`**: List specific properties
4. **Animations must be interruptible**: User actions should cancel running animations
```css
/* GOOD: Specific properties, respects user preference */
.card {
transition: transform 200ms ease-out, opacity 150ms ease-out;
}
@media (prefers-reduced-motion: reduce) {
.card { transition: none; }
}
```
### High-Impact Moments
Focus animation budget on:
- **Page transitions** — Orchestrated staggered reveals
- **State changes** — Loading → content, collapsed → expanded
- **Feedback** — Button press, form submission confirmation
Skip: random hover effects on every element, scroll-jacking, decorative background animations.
## Spatial Composition
### Layout Principles
- **Asymmetry is interesting** — Perfectly centered grid layouts are predictable
- **Intentional negative space** — White space is a design element, not a mistake
- **Grid-breaking elements** — One element that overlaps or extends beyond the grid creates focus
- **Diagonal flow** — Eye movement along diagonals is more engaging than horizontal scan
### Responsive Strategy
```css
/* Fluid typography */
h1 { font-size: clamp(2rem, 5vw, 4rem); }
/* Container queries for component-level responsiveness */
.card-container { container-type: inline-size; }
@container (min-width: 400px) {
.card { grid-template-columns: 200px 1fr; }
}
```
## Backgrounds & Atmosphere
Build visual depth through layered treatments:
```css
/* Noise texture overlay */
.surface::after {
content: '';
position: absolute;
inset: 0;
background: url('/noise.svg');
opacity: 0.03;
pointer-events: none;
}
/* Gradient mesh */
.hero {
background:
radial-gradient(ellipse at 20% 50%, oklch(0.7 0.15 250 / 0.3), transparent 50%),
radial-gradient(ellipse at 80% 20%, oklch(0.7 0.2 30 / 0.2), transparent 50%),
var(--color-bg);
}
```
## Images & Media
```tsx
// Explicit dimensions prevent layout shift
// Below-fold: lazy load
// Above-fold: prioritize
// Decorative: empty alt
```
## Interactive States
Every interactive element needs 4 states:
| State | Treatment |
|-------|-----------|
| Default | Base appearance |
| Hover | Increased contrast or subtle shift |
| Focus | Visible ring via `:focus-visible` |
| Active/Pressed | Slight scale or color change |
| Disabled | Reduced opacity + `cursor: not-allowed` |
```css
.button {
background: var(--color-accent);
transition: background 150ms ease-out, transform 100ms ease-out;
}
.button:hover { background: var(--color-accent-hover); }
.button:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }
.button:active { transform: scale(0.98); }
```
## Anti-Patterns
| Avoid | Instead |
|-------|---------|
| Inter/Roboto/Arial everywhere | Choose distinctive, project-appropriate typefaces |
| Purple gradient on white | Commit to a specific, cohesive palette |
| Uniform card grids | Vary sizes, add featured/hero cards |
| Shadows on everything | Use shadow purposefully for elevation hierarchy |
| `border-radius: 9999px` on all elements | Match radius to content type and context |
| Identical hover effects | Tailor interaction feedback to element function |