| name | web-design |
| description | Web design reference for building production-grade interfaces. Covers layout, typography, color, spacing, shadows, animation, accessibility, responsive design, components, performance, and UX psychology. Use when building UI, reviewing design quality, choosing design tokens, or making any visual design decision. |
| metadata | {"author":"pascalorg","version":"1.0.0"} |
Web Design
A practitioner-sourced reference for building web interfaces well. Synthesized from Refactoring UI, Tailwind CSS, shadcn/ui, Laws of UX, animations.dev, detail.design, Every Layout, Web Interface Guidelines, jakub.kr, userinterface.wiki (Raphael Salaja), and other authoritative sources.
Use this skill whenever you are building, reviewing, or improving a web interface.
Implementation Priority
When building or reviewing a UI, work through these tiers in order. Each tier depends on the ones above it — fixing a shadow detail is wasted effort if the layout is broken.
Tier 1: Structure (get this right first)
- Semantic HTML. Correct elements (
<button>, <nav>, <main>, headings in order). Everything else builds on this.
- Layout. Grid/flex structure, spacing scale, content width constraints (
max-w-prose, 12-col grid). Does the page hold together at every viewport?
- Responsive behavior. Mobile-first, intrinsic sizing (
auto-fill grids, clamp(), flex-wrap). No content hidden on small screens without reason.
- Typography fundamentals. Type scale, line height (1.5 body, 1.1-1.25 headings), line length (
max-width: 65ch), rem units for font sizes.
Tier 2: Visual System (the design backbone)
- Color and contrast. Palette applied, semantic tokens set, WCAG AA contrast met (4.5:1 text, 3:1 UI). Dark mode if needed.
- Visual hierarchy. Four text levels working (foreground, muted, muted/70, muted/50). Primary action obvious. Squint test passes.
- Component states. Every interactive element has hover, focus-visible, active, disabled, loading, error, and empty states accounted for.
- Spacing and proportion. Outer padding >= inner padding. No ambiguous gaps. Button padding ratio (2x horizontal : 1x vertical). Input heights match button heights.
Tier 3: Interaction and Motion (make it feel alive)
- Keyboard and accessibility. Focus styles, tab order, skip links,
aria attributes, touch targets (44px+). Test with keyboard only.
- Transitions. Hover/active feedback (150ms ease-out), state transitions (200-300ms), correct easing per direction (ease-out for enter, ease-in for exit).
- Animation. Stagger entrances, subtle exits, interruptible transitions,
prefers-reduced-motion respected. Spring physics where appropriate.
Tier 4: Polish (the last 10% that makes it feel crafted)
- Shadows and depth. Layered shadows, shadows-instead-of-borders where appropriate, consistent light source. Dark mode: surface lightness instead of shadows.
- Optical adjustments. Icon-side button padding, concentric border radii, play button offset, tabular-nums on data, image outlines.
- Micro-interactions. Contextual icon animation, blur on stagger entrances, copy-to-clipboard feedback, optimistic updates.
- Defensive CSS.
min-width: 0 on flex children, overflow-wrap: break-word, scrollbar-gutter: stable, text truncation, safe-area padding.
- Final checks. Squint test, grayscale test, swap test, "would a human ship this?" test. No AI slop (gratuitous gradients, identical metric cards, centered everything).
Rule of thumb: If you're debating a shadow opacity while the layout breaks at 768px, stop and go back to Tier 1.
Table of Contents
- Layout and Spacing
- Typography
- Color
- Shadows and Depth
- Visual Hierarchy
- Animation and Motion
- Components
- Responsive Design
- Accessibility
- Performance
- UX Psychology
- Design Tokens
- Polish and Craft
- Microcopy and UX Writing
- AI Slop Prevention
- Advanced Craft
- Defensive CSS
- Modern CSS Reset
- Predictive Prefetching
- Audio Feedback and Sound Design
1. Layout and Spacing
Spacing Scale
Use a consistent mathematical scale rooted in a base unit. The standard base is 4px (0.25rem). Every spacing value should be a multiple of this base.
| Token | Value | Use |
|---|
| 0.5 | 2px | Hairline gaps, icon padding |
| 1 | 4px | Tight inline spacing |
| 1.5 | 6px | Small component internal padding |
| 2 | 8px | Default gap between related items |
| 3 | 12px | Compact card padding |
| 4 | 16px | Standard card/section padding |
| 5 | 20px | Comfortable padding |
| 6 | 24px | Section padding |
| 8 | 32px | Section gaps |
| 10 | 40px | Large section gaps |
| 12 | 48px | Page section spacing |
| 16 | 64px | Major page divisions |
| 20 | 80px | Hero spacing |
| 24 | 96px | Large hero spacing |
Spacing Rules
- Outer padding >= inner padding. A card's outer margin must equal or exceed its internal padding. Interior elements relate more closely to each other than to external elements.
- Proximity signals relationship. Elements closer together are perceived as related (Gestalt Law of Proximity). Use spacing deliberately to group or separate.
- Eliminate ambiguous spacing. If the gap between two elements could belong to either, it's ambiguous. Make relationships clear through asymmetric spacing.
- Eliminate dead zones. Use padding on child elements instead of margins on containers. Every pixel between interactive items should be clickable.
- Use consistent increments. Don't pick arbitrary values. Constrain to the scale. Three similar spacings (14px, 16px, 18px) look like mistakes -- pick one.
Layout Primitives
These CSS patterns create responsive layouts without media queries:
Stack -- Vertical flow with consistent gaps:
.stack > * + * { margin-block-start: var(--space); }
Cluster -- Horizontal wrapping with gaps:
.cluster { display: flex; flex-wrap: wrap; gap: var(--space); }
Sidebar -- Two columns where one is fixed:
.sidebar { display: flex; flex-wrap: wrap; gap: var(--space); }
.sidebar > :first-child { flex-basis: 20rem; flex-grow: 1; }
.sidebar > :last-child { flex-basis: 0; flex-grow: 999; min-inline-size: 60%; }
Grid (auto-fill) -- Responsive columns without breakpoints:
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 250px), 1fr)); gap: var(--space); }
Center -- Constrained width with centering:
.center { max-inline-size: var(--measure); margin-inline: auto; padding-inline: var(--space); }
Grid and Flex Guidance
- Use CSS Grid for two-dimensional layouts (rows and columns together).
- Use Flexbox for one-dimensional layouts (single row or column).
- Prefer
fr units over percentages in Grid -- fr distributes space after gaps, preventing overflow.
- Use
gap instead of margins between items.
- A 12-column grid provides maximum flexibility (divisible by 1, 2, 3, 4, 6).
- Set
min-width: 0 on flex children to prevent content overflow.
- Use
align-self: start on sticky sidebar elements inside grid layouts.
In Practice: Tailwind Layout Patterns
Page shell with sidebar:
<div className="flex min-h-screen">
<aside className="hidden lg:flex w-64 flex-col border-r bg-muted/40 p-4">
<nav className="flex flex-col gap-1">{/* nav items */}</nav>
</aside>
<main className="flex-1 p-6">
<div className="mx-auto max-w-4xl space-y-8">{children}</div>
</main>
</div>
Card grid that adapts without breakpoints:
<div className="grid grid-cols-[repeat(auto-fill,minmax(min(100%,280px),1fr))] gap-4">
{items.map(item => <Card key={item.id} {...item} />)}
</div>
Content section with consistent vertical rhythm:
<section className="space-y-6 py-12">
<h2 className="text-2xl font-semibold tracking-tight">Features</h2>
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
{features.map(f => <FeatureCard key={f.id} {...f} />)}
</div>
</section>
Card with proper spacing hierarchy (outer > inner):
<div className="rounded-lg border bg-card p-6"> {}
<div className="space-y-4"> {}
<h3 className="font-semibold leading-tight">Title</h3>
<p className="text-sm text-muted-foreground">Description text here.</p>
</div>
</div>
2. Typography
Type Scale
Use a mathematical ratio to generate harmonious font sizes. Choose the ratio based on context:
| Ratio | Name | Best For |
|---|
| 1.067 | Minor Second | Dense UI, dashboards |
| 1.125 | Major Second | Compact interfaces |
| 1.200 | Minor Third | General purpose (recommended default) |
| 1.250 | Major Third | Content-heavy sites |
| 1.333 | Perfect Fourth | Marketing, editorial |
| 1.414 | Augmented Fourth | Bold presentation |
| 1.500 | Perfect Fifth | Dramatic hierarchy |
| 1.618 | Golden Ratio | High-impact landing pages |
Font Size Rules
- Body text minimum: 16px (1rem). This is the browser default. Never go smaller for primary reading content.
- Use
rem for font sizes. This respects both browser zoom and the user's default font size preference. Pixels prevent users from scaling text.
- Use
rem for media queries. When users increase their default font size, rem-based breakpoints trigger mobile layouts on wider screens, giving them more room.
- Use
px for padding, borders, and decorative elements. These shouldn't scale with text size.
- Use
rem for vertical margins. Spacing between paragraphs should grow with text.
Fluid Typography
Use clamp() for responsive sizing that scales smoothly between breakpoints:
font-size: clamp(2rem, 1.5rem + 2vw, 3rem);
Define a small-screen scale (e.g., 1.2x at 320px) and a large-screen scale (e.g., 1.333x at 1500px). The browser interpolates between them.
Line Height
- Body text: 1.5. This meets WCAG criteria and improves readability for all users.
- Headings: 1.1 - 1.25. Larger text needs tighter leading.
- The bigger the text, the less line-height it needs. Scale inversely.
- Headings and buttons: 1.1 is a good default.
Line Length
- Optimal: 60-75 characters (about 8-10 words per line).
- Maximum: 80 characters. Beyond this, readers lose their place.
- Use
max-width: 65ch on content containers to enforce this.
Letter Spacing
- Large text: reduce letter-spacing. Bigger text needs less space between characters.
- Small text: increase letter-spacing slightly.
- All-caps text: add +0.05 to +0.1em letter-spacing. Uppercase letters crowd each other without extra space.
- Use
font-variant-numeric: tabular-nums in tables and timers so digits maintain consistent width.
Text Wrapping
h1, h2, h3, h4, h5, h6 { text-wrap: balance; }
p { text-wrap: pretty; }
p, h1, h2, h3, h4, h5, h6 { overflow-wrap: break-word; }
Font Rendering
body { -webkit-font-smoothing: antialiased; }
html { text-rendering: optimizeLegibility; -webkit-text-size-adjust: 100%; }
Font Weight
- Never use weights below 400 -- they become illegible on most screens.
- Use 500-600 for medium headings.
- Use 700 for strong emphasis.
- Prevent layout shift from weight changes: Use a hidden
::after pseudo-element with bold text to reserve the bold width, preventing jitter when toggling active states.
OpenType Features
Modern fonts ship with OpenType features that dramatically improve typographic quality. Enable them intentionally:
| Feature | CSS | Use |
|---|
| Tabular numbers | font-variant-numeric: tabular-nums | Tables, dashboards, pricing, timers -- equal-width digits align in columns |
| Oldstyle numbers | font-variant-numeric: oldstyle-nums | Body text/prose -- digits with ascenders/descenders blend with lowercase |
| Slashed zero | font-variant-numeric: slashed-zero | Code-adjacent UIs, IDs, error codes -- disambiguate 0 from O |
| Proper fractions | font-variant-numeric: diagonal-fractions | Recipes, specs -- converts 1/2 to typographic fraction |
| Contextual alternates | font-feature-settings: "calt" 1 | Usually on by default -- keep enabled for smart glyph adjustments |