---
name: web-taste
description: >
  Designs React 19 + Next.js 16 + Tailwind CSS experiences with real
  taste — starting from user goals, not pixels. Use this skill whenever
  the user asks you to build a page, screen, form, dashboard, or any
  user-facing web UI. Trigger when the user says "build a settings
  page", "create a dashboard", "design this properly", "I want this
  to feel premium", "make it look like Linear/Stripe/Vercel", or any
  Next.js App Router / React / Tailwind UI task. Also trigger when
  reviewing UI for design quality, or when the user says the output
  "looks like a wireframe", "feels like Bootstrap", or "looks
  AI-generated." When building any user-facing web view, lean toward
  triggering this skill.
---

# Web Taste

Taste doesn't start at the pixel level. It starts at "who is this
person and what do they need?" The visual refinement is the LAST step.
The first step is understanding the user's world deeply enough that
the interface design feels inevitable — like it couldn't have been
designed any other way.

Your default mode skips straight to layout. It produces technically
correct React that looks generic because it was never grounded in
a real person's needs — a stack of cards, a sidebar, a settings list.
This skill changes the order of operations: think like a designer
first, then write code.

## Phase 0: The 0.5-Second Test (ORIENT before everything)

Before designing anything, answer ONE question:

> **What does the user SEE in the first half-second — before they
> read a single word?**

This is not about content. It's about the SHAPE of the screen.
Close your eyes and picture it. What dominates?

- A **single hero number** with a sparkline? → Analytics / metric dashboard
- A **gradient card** with bold white text? → Status / billing / hero page
- A **table with sticky chrome** and a search bar? → Data console / admin
- A **grid of media cards**? → Library / gallery / content collection
- A **center-aligned form** with generous space? → Sign-up / auth / single-action
- A **two-pane list + detail**? → Email / inbox / CRM
- A **timeline of events**? → Activity feed / audit log

If your answer is "a sidebar and some cards in a content area" →
STOP. That's a CMS theme, not a product. Go back and find the
visual shape that matches what this user actually needs.

Write the 0.5-second answer as the FIRST line of the experience
brief, as a JSDoc comment on the route:

```tsx
// 0.5s: One huge revenue number on black, sparkline pulsing below
export default async function DashboardPage() { ... }
```

This single sentence anchors every decision that follows. If the
code you write doesn't produce that shape, something went wrong.

## Phase 1: Design Thinking (Before You Touch React)

Before writing a single line of JSX, answer these questions. Write
the answers down as comments or in your thinking. If you skip this
phase, your output will look like every other AI-generated UI —
correct but soulless.

### 1. Who is the user?

Not "a SaaS admin." A real person with a context:

- What moment are they in when they open this page? (Triaging the
  morning's alerts? Sharing a link in a meeting? Reviewing the
  quarter before a board call?)
- What did they just do before arriving here? (Clicked a notification
  email? Searched? Followed a deep link from Slack?)
- What do they want to accomplish in under 10 seconds?

This shapes EVERYTHING. A user pasted into the page via a Slack
deep-link needs a giant headline that reorients them. A user with
the page open all day wants quiet chrome and big working space. A
user evaluating during a demo wants the value proposition to be
visible without scrolling.

### 2. What should they FEEL?

This is the question that separates designed products from CRUD
admin panels. Linear doesn't show you a kanban — it makes shipping
feel like an obsession. Stripe doesn't show you payment forms — it
makes commerce feel like a solved problem. Every design choice
serves that emotional goal.

Before choosing components, decide the emotional intent:

- **Focused / Productive** → muted palette, monochrome chrome, single
  accent color, generous space, Inter or system-ui sans
- **Confident / Trustworthy** → deeper blues/greens, clear data
  presentation, generous whitespace, classical proportions
- **Delighted / Premium** → unexpected microinteractions, rich
  gradients, subtle 3D, considered typography (Söhne, Inter Display)
- **Energetic / Bold** → high saturation, large display type,
  asymmetric layouts, strong photography
- **Calm / Editorial** → serif typography, narrow text columns, lots
  of breathing room, restrained color

The emotional intent drives every visual decision downstream: color
tokens, scale, spacing, whether data is listed or visualized,
whether the page feels dense or spacious.

### 3. What are their goals and pain points?

For each page, identify:

- **Primary goal** — the ONE thing most users come here to do
- **Secondary goals** — things some users occasionally need
- **Pain points** — what frustrates users in this domain?

A billing page: the primary goal isn't "see all billing info." It's
"change the payment method that just expired" or "download last
month's invoice for expense reports." The pain point is wading
through plan summaries and feature comparisons to find the one
action that needs doing.

### 4. What features serve those goals?

Map goals to features. Not "what features could this page have?"
but "what's the minimum set of features that makes the primary goal
effortless?" Every feature that doesn't serve a goal is clutter.

Group features by priority:

- **Must-have** — blocks the primary goal without it
- **Should-have** — significantly improves the experience
- **Could-have** — nice but the user doesn't miss it if it's absent

### 5. How do features become routes?

This is information architecture — deciding what goes where in the
App Router:

- **One primary action per route.** If a page tries to do two
  things, split it into two routes or use a sheet/dialog for the
  secondary task.
- **Group by user intent, not by data type.** A user doesn't think
  "I want to see my notification settings." They think "I want
  Slack to stop pinging me during deep work." Group features by
  the problem they solve, not by their technical category.
- **Navigation follows the user's mental model.** `/settings` →
  `/settings/billing` is obvious. `/admin/organizations/{org}/members/{member}/preferences/notifications`
  is six levels deep for something the user sets once. Use parallel
  routes (`@modal`) and intercepting routes for "open as overlay"
  patterns when full navigation is overkill.

### 6. What components serve each feature?

NOW you think about React — but through the lens of user intent:

- **Server Component vs Client Component** — RSC by default. Use
  `'use client'` only when interactivity is required. Don't ship a
  hydration boundary for a list that doesn't need one.
- **Dialog vs Popover vs Sheet vs Full page** — see [ux-modality](../../experimental/web-rules/references/ux-modality.md).
  Pick the lightest container that fits.
- **Form vs separate fields** — Server Actions + `<form action={}>`
  for anything that mutates. Standalone fields with `useOptimistic`
  for live-saving settings.
- **Table vs Card grid vs List** — Table when scanning columns is
  the job (sortable, filterable, compare values). Card grid for
  heterogeneous browse. List for homogeneous rows with one primary
  identifier per row.
- **shadcn/ui vs custom** — shadcn/ui Radix-based primitives are
  the default. Custom components only when no primitive fits.

The component choice IS the design. A `<DataTable>` with sticky
header for a financial dashboard feels precise and bureaucratic. A
gradient card grid for the same data feels exploratory and editorial.
Neither is wrong — the right choice depends on who the user is and
what moment they're in.

## Phase 2: Visual Design

After Phase 1, you know who the user is, what they need, how they
should feel, and what components serve those needs. Now make it
beautiful. The emotional intent from Phase 1 drives every choice here.

### 1. Hierarchy Through Scale

Not just font weight — dramatic scale contrast. The most important
thing on screen should be *physically large*, not just bold.

- **Hero numbers at display scale** — a revenue figure, a count, a
  percentage should dominate the page. Use `text-6xl` or `text-7xl`
  with `font-semibold tracking-tight tabular-nums`. Linear's
  velocity charts use 64-72 px display numbers. Don't shrink
  important data into a `text-sm` row.
- **Supporting text whispers** — everything that isn't the hero
  element gets `text-sm` or `text-xs` in `text-muted-foreground`.
  The contrast between the hero and the support IS the hierarchy.
- **Space as luxury** — leave empty areas. A number floating in a
  sea of background is more powerful than the same number crammed
  into a dense `DataTable`. Space communicates importance.

```tsx
// Hero metric — scale dominates, whisper labels
<section className="p-8">
  <p className="text-sm text-muted-foreground">Net revenue · last 30 days</p>
  <p className="mt-2 text-7xl font-semibold tracking-tight tabular-nums">
    ${(revenue / 100).toLocaleString()}
  </p>
  <p className="mt-2 flex items-center gap-1 text-sm text-emerald-600">
    <ArrowUpRight className="size-4" aria-hidden="true" />
    +12.4% vs prior period
  </p>
</section>
```

### 2. Color Is Math, Not Vibes

NEVER pick colors by hand. Color harmony is a solved mathematical
problem. This skill bundles a palette generator that computes every
color from a single seed hue — analogous harmony, WCAG contrast
validated, light and dark mode variants emitted as CSS custom
properties and a Tailwind config snippet.

**Before writing any view code, run the palette generator:**

```bash
python scripts/generate_palette.py \
  --seed <hue-degrees> \
  --mode both \
  --items <collection-count> \
  --app "App Name"
```

Seed hue guide:

- 0–30° = warm (creative, social, dating, food)
- 30–60° = golden (finance, productivity, mail)
- 60–150° = green (health, fitness, sustainability)
- 150–210° = cyan/teal (developer tools, infra, cloud)
- 210–270° = blue (trust, fintech, enterprise SaaS)
- 270–330° = purple (creative, AI, premium)
- 330–360° = pink/red (energy, gaming, e-commerce)

Paste the generated `:root` and `.dark` CSS custom properties into
`app/globals.css`, and use ONLY those tokens via Tailwind utilities
(`bg-primary`, `text-muted-foreground`, `border-border`). The
palette is computed — every color is mathematically related to the
seed, contrast ratios are pre-validated, and light/dark variants
are included.

Rules that never break:

- **One seed hue per app.** Everything derives from it.
- **Collections use analogous variations** (the `--items` flag),
  not random hues. They sit together because they're ±30° of seed.
- **Never use raw Tailwind palette classes** like `bg-blue-500`,
  `text-red-600`, `border-gray-200` — those don't theme and aren't
  contrast-audited. Use semantic tokens.
- **Use `bg-background`, `text-foreground`, `border-border`,
  `text-primary`** — not ad-hoc `Color(...)` calls or arbitrary
  classes scattered through component code.

### 3. Show Data, Don't List It

When data is the content (analytics, financial stats, progress),
VISUALIZE it instead of putting it in a `LabeledRow`:

- **Sparklines and area charts** for trends over time (Recharts,
  Tremor, or hand-rolled SVG)
- **Gauges and rings** for progress toward a goal
- **Large hero numbers** with delta arrows and percentage chips
- **Heatmaps** for activity by time-of-day or by-day-of-week
- **Color-coded bars** for composition (revenue by source, time split)

A `<dl><dt>Revenue</dt><dd>$8,432</dd></dl>` is information. A large
"$8,432" in `text-6xl tabular-nums` with a sparkline below it is an
*experience*. The emotional intent from Phase 1 tells you which one
to use.

### 4. Card-Based Composition

Don't default to a single bordered container for everything. Compose
with `rounded-xl border bg-card` containers when the content is
heterogeneous:

- Cards with `rounded-xl border bg-card text-card-foreground p-6`
- Each card is a self-contained visual unit with its own hierarchy
- Cards can have gradient backgrounds for visual richness
  (think Vercel deployment cards, Linear cycle cards)
- Use CSS grid with `auto-rows-fr` for equal-height card grids

Tables are for homogeneous scannable data. Cards are for dashboards,
overviews, and content-rich pages where each item has its own story.

### 5. Content Realism

The data IS the design. Every preview tells a coherent story:

- Real names ("Elena Marsh"), plausible numbers ("$47.83", "4.3K"),
  varied lengths, temporal realism ("2 hours ago", "Yesterday")
- Data relationships that make sense (Designer → Design dept)
- If your preview data looks fake (`"Item 1"`, `"Lorem ipsum"`,
  `foo@example.com`), your design looks fake

Generate seed data with a coherent narrative — even in fixtures.

### 6. Restraint

What you leave out defines taste. No instruction headers ("Welcome
to your dashboard"). No uniform icon decorations on every row. No
tutorial overlays. No demo naming. For every element, ask: "what
happens if I remove this?" If nothing — remove it.

### 7. Craft

The invisible details that feel right:

- `tabular-nums` on changing numbers so digits don't reflow
- `tracking-tight` on display headings
- `transition-colors duration-150` on every interactive element
- `hover:` + `focus-visible:` + `active:` states on every button
- `motion-safe:` on transforms (see [acc-reduce-motion](../../experimental/web-rules/references/acc-reduce-motion.md))
- `truncate` + `line-clamp-N` instead of overflow hidden + ellipsis JS
- `font-variant-numeric: tabular-nums` everywhere numbers tick
- Accessibility as design, not compliance (see [web-rules](../../experimental/web-rules/SKILL.md))

### 8. Character

Each page has a distinct personality. Character comes from:

- Domain-appropriate color palettes (cool blue for fintech, warm
  ochre for crafts, vivid green for sustainability)
- Content-specific typography pairings (a serif headline + Inter
  body feels editorial; mono in the chrome feels developer-y)
- Cover the navigation bar — can you still tell what app this is?

If a user dropped into your page with the nav bar covered, would
they know what app they're in? Linear's character is in its sharp
chrome and cycle visualization. Stripe's is in its perfectly-aligned
data tables and trust-blue accent. Find yours.

## Applying Both Phases

When asked to build a route, layout, or component:

1. **Phase 1** — Think through the user, their goals, feature
   groupings, route structure, and component choices. Write brief
   notes (as code comments or in your response) showing your design
   reasoning. This is not optional — it's what separates a designed
   experience from a decorated layout.

2. **Phase 2** — Write the React/Next.js/Tailwind code with all
   eight fundamentals applied. Start with realistic data and seed
   fixtures. Build minimal, add only what earns its place, then
   polish with craft details.

3. **Self-check** — Before finishing, ask: "Would a real user using
   this product in the moment I identified in Phase 1 feel like
   this page was designed for them?" If not, something in Phase 1
   was wrong — go back.

## No Taste vs Taste — Concrete Examples

For full before/after code (a generic AI-looking dashboard vs a
Stripe-inspired revenue page) and the **Component Palette Quick
Reference** table that maps reflex choices to taste-driven ones,
read `references/code-examples.md`.

The short version of the contrast: a "no taste" page jumps straight
to layout — instruction header, numbered placeholders, `text-gray-*`
and `bg-blue-*` hardcoded, generic component naming. A "taste" page
opens with a design comment naming the user and emotional intent,
puts the most important number at hero scale with `tabular-nums`,
uses semantic tokens (`text-muted-foreground`, `bg-card`), and lets
a sparkline support the hero rather than compete with it.

Reference apps worth studying: Linear, Stripe, Vercel, Notion, and
high-craft consumer apps (Cash App, Things, Arc). Each has a
distinct character driven by deliberate choices in chrome,
typography, and data presentation.

## The Screen Becomes the Content

Study Linear's issue detail view: the page isn't a form *about* an
issue — the entire screen IS the issue. The title is the page
heading at hero scale. The description IS the body. Actions live in
chrome that fades away. There's no "Edit issue" page; editing
happens in place.

This is the highest level of taste: the UI dissolves into the
content. The page doesn't frame the data — it becomes the data.

Techniques for this:

- **In-place editing** — `contenteditable` on the title and body so
  the page IS the editor. Save on blur via Server Action.
- **Mono in chrome, sans in content** — gives developer tools a
  distinct character without making the body unreadable.
- **Smart typography in lists** — Linear bolds the issue ID prefix
  and leaves the title in regular weight. This tiny detail makes
  identifier-scanning dramatically faster. Find the equivalent
  typographic hierarchy for your domain.
- **Detail views look like read mode** — even in edit-able products,
  the detail view doesn't show a form UI. The same content reads
  beautifully *and* edits in place.

## Reference: Web Design DNA

When making specific design decisions, read `references/web-design-dna.md`
in this skill's directory. It synthesizes patterns from systematically
studying Linear, Stripe, Vercel, Notion, and high-craft consumer apps
(Cash App, Things, Arc) — real components, real measurements, real
design analysis.

Key sections to consult:

- **Three modes of web app design** — Dashboard (data-heavy, dark
  chrome), Utility (CRUD-driven, light surfaces), Editorial (content-
  first, expressive typography)
- **Universal measurements** — card radius (12-16 px), header height
  (56-64 px), content max-width (1024-1280 px)
- **Onboarding templates** — feature-list vs hero-illustration patterns
- **Button hierarchy** — filled primary, outline secondary, ghost
  tertiary; destructive isolated by position not just color
- **Detail-page-becomes-content** — the Linear / Notion technique
- **Empty states** — show structure, not "no data" messages

## The Mindset

You are not a developer who can also design. You are a designer who
thinks about people first and expresses the result in React,
Next.js, and Tailwind. The code is the medium. The product is the
moment when a human opens their browser and the interface feels
like it was made just for them.
