Agent Skill
2/7/2026

ui-design

Guides UI design decisions for polished, professional interfaces. Activates when creating components, layouts, cards, forms, tables, dashboards, or any user-facing UI. Covers visual hierarchy, spacing, typography, OKLCH color theming (light/dark mode), and Refactoring UI principles applied with Tailwind CSS v4 and shadcn/ui.

M
madlado87
0GitHub Stars
1Views
npx skills add madlado87/agentes

SKILL.md

Nameui-design
DescriptionGuides UI design decisions for polished, professional interfaces. Activates when creating components, layouts, cards, forms, tables, dashboards, or any user-facing UI. Covers visual hierarchy, spacing, typography, OKLCH color theming (light/dark mode), and Refactoring UI principles applied with Tailwind CSS v4 and shadcn/ui.

name: ui-design description: "Guides UI design decisions for polished, professional interfaces. Activates when creating components, layouts, cards, forms, tables, dashboards, or any user-facing UI. Covers visual hierarchy, spacing, typography, OKLCH color theming (light/dark mode), and Refactoring UI principles applied with Tailwind CSS v4 and shadcn/ui."

UI Design Principles

This skill codifies the design principles from Refactoring UI (Adam Wathan & Steve Schoger) applied to this project's stack: Tailwind CSS v4 + shadcn/ui. Follow these rules to produce polished, professional interfaces without needing a designer.

IMPORTANT: These are not suggestions — they are rules. Every component you create should follow them. When in doubt, apply the "squint test": blur your eyes and check if the visual hierarchy still reads correctly.

MANDATORY: Always use shadcn/ui components when one exists for the element you need. Never use raw HTML elements (<input>, <select>, <textarea>, <label>, <table>) when a shadcn/ui primitive is available. shadcn/ui components already apply the correct theme tokens, focus rings, border radii, and accessibility attributes. If a component isn't installed yet, install it with npx shadcn@latest add [component-name].

shadcn/ui Component Map

NeedUseNOT
Text input<Input> from @/components/ui/input<input>
Textarea<Textarea> from @/components/ui/textarea<textarea>
Select dropdown<Select> from @/components/ui/select<select>
Native select<NativeSelect> from @/components/ui/native-select<select>
Label<Label> from @/components/ui/label<label>
Button<Button> from @/components/ui/button<button>
Card container<Card>, <CardHeader>, <CardContent><div> with card classes
Data table<Table>, <TableHeader>, <TableRow>, <TableCell><table>, <thead>, <tr>, <td>
Badge/status<Badge> from @/components/ui/badge<span> with pill classes
Checkbox<Checkbox> from @/components/ui/checkbox<input type="checkbox">
Switch/toggle<Switch> from @/components/ui/switch<input type="checkbox">
Dialog/modal<Dialog> from @/components/ui/dialogcustom modal <div>
Separator<Separator> from @/components/ui/separator<hr> or border-b dividers

1. Visual Hierarchy

Visual hierarchy is the most important design concept. It determines how users scan and understand a page.

Rules

  • Three levers: size, weight/boldness, and color/contrast. Combine them — don't multiply. A heading should be large OR bold OR dark, rarely all three.
  • Primary content: larger, bolder, darker (e.g., text-lg font-semibold text-foreground).
  • Secondary content: smaller, normal weight, muted (e.g., text-sm text-muted-foreground).
  • Tertiary content: smallest, lightest (e.g., text-xs text-muted-foreground/70).
  • Emphasize by de-emphasizing: instead of making the primary element louder, make surrounding elements quieter.
  • Labels are secondary: form labels, table headers, and metadata labels support data — they shouldn't compete. Use text-sm font-medium text-muted-foreground for labels, not bold large text.
  • Skip labels when the format is obvious: email addresses, phone numbers, dates, and URLs don't need labels — their format is self-explanatory.

Tailwind Patterns

{/* ✅ Clear hierarchy (theme-aware) */}
<div>
  <h2 className="text-lg font-semibold text-foreground">Project Alpha</h2>
  <p className="text-sm text-muted-foreground">Created 3 days ago</p>
</div>

{/* ❌ No hierarchy — everything looks the same */}
<div>
  <h2 className="text-base font-normal text-foreground">Project Alpha</h2>
  <p className="text-base font-normal text-foreground">Created 3 days ago</p>
</div>

2. Spacing & Layout

Spacing Scale

Use Tailwind's constrained scale. Don't use arbitrary values.

TailwindpxUse for
gap-1 / p-14pxIcon + label gap, tight coupling
gap-2 / p-28pxRelated items within a group
gap-3 / p-312pxList items, small card padding
gap-4 / p-416pxStandard content padding, form fields
gap-6 / p-624pxCard padding, section gaps
gap-8 / p-832pxMajor section separation
gap-12 / p-1248pxPage section spacing
gap-16 / p-1664pxHero spacing, page top padding

Rules

  • Start with more space, then reduce: add more padding/margin than you think you need. It's easier to reduce than to add.
  • Group spacing rule: space BETWEEN groups must be larger than space WITHIN groups. This creates visual grouping without borders.
  • Don't stretch to fill: if content only needs 600px, use max-w-2xl — don't spread it across the full viewport.
  • Mobile spacing: reduce spacing on small screens. What's p-8 on desktop might be p-4 on mobile.
  • Consistent scale: don't mix arbitrary values. Stick to Tailwind's spacing scale.

Tailwind Patterns

{/* ✅ Proper grouping — more space between groups than within */}
<div className="space-y-8">             {/* 32px between sections */}
  <section className="space-y-3">       {/* 12px within section */}
    <h3 className="text-lg font-semibold">Section Title</h3>
    <p className="text-sm text-gray-500">Description text</p>
  </section>
  <section className="space-y-3">
    <h3 className="text-lg font-semibold">Another Section</h3>
    <p className="text-sm text-gray-500">More description</p>
  </section>
</div>

{/* ✅ Constrained width */}
<div className="mx-auto max-w-2xl">
  {/* Content that doesn't need full width */}
</div>

3. Typography

Rules

  • Use neutral sans-serifs for UI: the project uses Geist (loaded locally). It's a well-crafted neutral font — don't add more fonts.
  • Line length: 45–75 characters per line for readability. Use max-w-prose (~65ch) or max-w-2xl.
  • Left-align text longer than 2-3 lines. Center-align only for short headings or CTAs.
  • Right-align numbers in tables and data displays for easy scanning.
  • Font weight minimum: never go below font-normal (400) for UI text. To de-emphasize, use smaller size or lighter color instead.
  • Non-linear font scale: use Tailwind's built-in scale (text-xs through text-4xl). Don't create custom sizes between adjacent steps.
  • Letter spacing: add tracking-wide to uppercase text or small labels. Don't add letter-spacing to body text.

Tailwind Patterns

{/* ✅ Readable paragraph (theme-aware) */}
<p className="max-w-prose text-base leading-relaxed text-muted-foreground">
  Long paragraph text here...
</p>

{/* ✅ Right-aligned numbers in table */}
<td className="text-right tabular-nums text-sm text-foreground">1,234.56</td>

{/* ✅ Uppercase small label */}
<span className="text-xs font-medium uppercase tracking-wide text-muted-foreground">Status</span>

4. Color & Theming

Color Theory Foundations

The project uses OKLCH (Oklab Lightness, Chroma, Hue) — a perceptually uniform color space where equal numeric changes produce visually equal changes. This makes palette generation predictable: adjusting lightness doesn't shift hue, and colors at the same lightness appear equally bright.

OKLCH(Lightness, Chroma, Hue)
  Lightness: 0 (black) → 1 (white)
  Chroma:    0 (gray)  → 0.4 (vivid)
  Hue:       0–360° (color wheel angle)

Color Harmony Schemes (from the color wheel):

  • Monochromatic: one hue, vary lightness/chroma. Used for our neutral base.
  • Complementary: opposite hues (180° apart). Maximum contrast — use sparingly.
  • Analogous: adjacent hues (±30°). Harmonious — good for related UI elements.
  • Triadic: three hues 120° apart. Vibrant — good for chart palettes.
  • Split-complementary: one hue + two adjacent to its complement. Balanced contrast.

The 60-30-10 Rule

Applied to the semantic token system in globals.css:

ProportionTokensPurpose
60%background, card, popoverNeutral surfaces — the canvas
30%secondary, muted, accentSupporting tones — subtle differentiation
10%primary, destructiveChromatic attention — actions and alerts

Theme Token System

All colors are defined as CSS custom properties in globals.css with :root (light) and .dark (dark) overrides. The @theme inline directive maps them to Tailwind utility classes.

Token naming convention — every token follows the background/foreground pair pattern:

--primary           → background color of primary elements
--primary-foreground → text color ON primary backgrounds

Always pair them: bg-primary text-primary-foreground. This ensures contrast in both modes.

Complete token map:

TokenLight Mode PurposeDark Mode Behavior
background / foregroundPage background + body textInverted lightness
card / card-foregroundCard surface + textSlightly elevated from background
popover / popover-foregroundDropdown/dialog surfaceSame as card in dark
primary / primary-foregroundBrand action color + its textInverted: light on dark surface
secondary / secondary-foregroundSubtle background + textDarker neutral
muted / muted-foregroundDisabled/subdued areas + textDarker neutral
accent / accent-foregroundHover highlights + textDarker neutral
destructive / destructive-foregroundError/danger elementsHigher lightness for dark bg
borderBorders and dividersSemi-transparent white
inputInput field bordersSlightly brighter than border
ringFocus ringsAdjusted for visibility

Rules

  • Design in grayscale first: get hierarchy right with spacing, size, and weight before adding color.
  • Use semantic tokens, not raw colors: bg-primary not bg-blue-600, text-muted-foreground not text-gray-500. Tokens adapt to light/dark mode automatically.
  • Use dark grays, not black: text-foreground (oklch 0.145) instead of text-black. Pure black feels harsh.
  • Don't use gray text on colored backgrounds: use white with reduced opacity (text-primary-foreground/70) or a color tinted toward the background.
  • Primary colors for actions: buttons, links, active states. Use bg-primary text-primary-foreground.
  • Accent borders: border-t-4 border-t-primary for visual flair on cards.
  • Semantic colors: destructive for errors/danger. For success/warning/info, define custom token pairs if needed (see examples/custom-theme-color.css).

Branding the Theme

To apply a brand color, change --primary in globals.css to the brand's OKLCH value:

/* Blue brand — hue 260° */
:root {
  --primary: oklch(0.488 0.243 264);
  --primary-foreground: oklch(0.985 0 0);
}
.dark {
  --primary: oklch(0.688 0.2 264);
  --primary-foreground: oklch(0.145 0 0);
}

To derive related colors from a brand hue, use color harmony:

  • Accent: shift hue ±30° (analogous) — harmonious complement
  • Chart palette: distribute across hue wheel at 72° intervals (360° / 5 colors)
  • Destructive: keep at red hue (~27°) — it's semantic, not brand

Tailwind Patterns

{/* ✅ Semantic tokens — adapts to light/dark mode automatically */}
<h1 className="text-foreground">Title</h1>
<p className="text-muted-foreground">Secondary text</p>

{/* ✅ Card using theme tokens */}
<div className="rounded-lg border border-border bg-card p-6 shadow-sm">
  <h3 className="text-card-foreground">Card Title</h3>
</div>

{/* ✅ Accent border using theme token */}
<div className="rounded-lg border border-border border-t-4 border-t-primary bg-card p-6 shadow-sm">
  Card content
</div>

{/* ✅ On colored background — use foreground pair with opacity */}
<div className="bg-primary p-4">
  <h3 className="font-semibold text-primary-foreground">Title</h3>
  <p className="text-primary-foreground/70">Secondary text</p>
</div>

5. Shadows & Depth

Rules

  • Use shadows instead of borders: shadows create softer separation and feel more polished. Reserve borders for dividers within a container.
  • Shadow scale matches elevation: shadow-sm for subtle lift (cards at rest), shadow-md for moderate (dropdowns, popovers), shadow-lg for high (modals, dialogs).
  • Combine shadow + border for cards: shadow-sm border border-border creates a subtle, polished card.
  • Separation without borders: use background color changes, extra spacing, or shadows to create visual separation between sections.

Tailwind Patterns

{/* ✅ Card with shadow + subtle border (theme-aware) */}
<div className="rounded-lg border border-border bg-card p-6 shadow-sm">
  Card content
</div>

{/* ✅ Separation via background color (no border needed) */}
<div className="bg-muted p-8">
  <div className="bg-card p-6 rounded-lg shadow-sm">
    Card in section
  </div>
</div>

{/* ❌ Too many borders — feels heavy */}
<div className="border border-border p-4">
  <div className="border-b border-border pb-2 mb-2">Header</div>
  <div className="border-b border-border pb-2 mb-2">Item 1</div>
  <div>Item 2</div>
</div>

6. Actions & Buttons

Rules

  • Hierarchy-first: not every action needs to be a prominent button. Primary actions get filled buttons, secondary get outlined/ghost, tertiary get text links.
  • Destructive ≠ prominent: a delete button in a settings page doesn't need to be big and red. Use a ghost button or link style with destructive color.
  • One primary action per section: if everything is primary, nothing is primary.
  • Button sizing: use shadcn/ui variants (default, outline, ghost, destructive, link) and sizes (sm, default, lg).

Tailwind Patterns

import { Button } from "@/components/ui/button";

{/* ✅ Action hierarchy */}
<div className="flex items-center gap-3">
  <Button>Save Changes</Button>                    {/* Primary */}
  <Button variant="outline">Cancel</Button>        {/* Secondary */}
  <Button variant="ghost" size="sm">Reset</Button> {/* Tertiary */}
</div>

{/* ✅ Destructive but not screaming */}
<Button variant="ghost" className="text-destructive hover:text-destructive">
  Delete Account
</Button>

7. Forms

Rules

  • Use shadcn/ui form primitives: <Input>, <Textarea>, <Select>, <Label>, <Checkbox>, <Switch>. Never use raw HTML form elements.
  • Labels above inputs, not beside (better for mobile and scanning).
  • Don't use placeholder as label: placeholders disappear on focus, losing context.
  • Group related fields: use space-y-4 within a group, space-y-8 between groups.
  • Error messages below the field: use text-sm text-destructive.
  • Optional fields should say "(optional)" — don't mark required fields with asterisks.
  • Input widths should match expected content: an email field can be full width, but a zip code field should be narrow.
  • Install missing components: if <Input> or <Label> aren't installed, run npx shadcn@latest add input label.
  • All forms use react-hook-form with zodResolver(schema) — Zod schemas reused as validators
  • One [Entity]Form component per feature, reused for create and edit
  • defaultValues always explicit. Mutations handled by parent, not the form

Tailwind Patterns

import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";

{/* ✅ Form field with shadcn/ui components */}
<div className="space-y-1.5">
  <Label htmlFor="email">Email address</Label>
  <Input id="email" type="email" />
</div>

{/* ✅ Select with shadcn/ui */}
<div className="space-y-1.5">
  <Label htmlFor="role">Role</Label>
  <Select>
    <SelectTrigger>
      <SelectValue placeholder="Select a role" />
    </SelectTrigger>
    <SelectContent>
      <SelectItem value="admin">Admin</SelectItem>
      <SelectItem value="editor">Editor</SelectItem>
      <SelectItem value="viewer">Viewer</SelectItem>
    </SelectContent>
  </Select>
</div>

{/* ✅ Optional field */}
<div className="space-y-1.5">
  <Label htmlFor="bio">
    Bio <span className="font-normal text-muted-foreground">(optional)</span>
  </Label>
  <Textarea id="bio" />
</div>

{/* ✅ Form with grouped sections */}
<form className="space-y-8">
  <fieldset className="space-y-4">
    <legend className="text-base font-semibold text-foreground">Personal Info</legend>
    {/* Label + Input fields */}
  </fieldset>
  <fieldset className="space-y-4">
    <legend className="text-base font-semibold text-foreground">Address</legend>
    {/* Label + Input fields */}
  </fieldset>
</form>

8. Cards & Lists

Rules

  • Use <Card> from shadcn/ui: use <Card>, <CardHeader>, <CardTitle>, <CardDescription>, <CardContent>, <CardFooter> — not raw <div> with card classes. Install with npx shadcn@latest add card.
  • Use <Badge> for status indicators: install with npx shadcn@latest add badge. Don't hand-craft pill <span> elements.
  • Card content hierarchy: title (semibold, text-card-foreground) → description (text-muted-foreground) → metadata (small, lightest).
  • List item spacing: divide-y divide-border for bordered lists, space-y-2 for spaced lists.
  • Empty states: always show EmptyState from @/features/feedback — never a blank space.

Tailwind Patterns

import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";

{/* ✅ Card with shadcn/ui components */}
<Card>
  <CardHeader>
    <div className="flex items-center justify-between">
      <CardTitle className="text-sm">Card Title</CardTitle>
      <Badge variant="secondary">Active</Badge>
    </div>
    <CardDescription>Description goes here</CardDescription>
  </CardHeader>
  <CardFooter className="text-xs text-muted-foreground/70">
    <span>Updated 2h ago</span>
    <span className="ml-4">3 tasks</span>
  </CardFooter>
</Card>

9. Tables & Data

Rules

  • Use <Table> from shadcn/ui: use <Table>, <TableHeader>, <TableBody>, <TableRow>, <TableHead>, <TableCell> — not raw HTML table elements. Install with npx shadcn@latest add table.
  • Right-align numbers, left-align text.
  • De-emphasize headers: table headers are labels — <TableHead> already applies muted styling.
  • Alternate row shading or use subtle borders — not both. <TableRow> supports hover states.
  • Constrain column widths for long text: use truncate or max-w-xs to prevent layout blow-up.

10. Responsive Design

Rules

  • Mobile-first: base classes are for mobile, md: and lg: for larger screens.
  • Reduce spacing on mobile: p-4 md:p-6 lg:p-8.
  • Reduce heading scale on mobile: desktop heading may be text-3xl, mobile may be text-xl.
  • Stack on mobile, side-by-side on desktop: flex flex-col md:flex-row.
  • Don't shrink everything proportionally: some elements (icons, buttons) have minimum usable sizes.

Design Checklist

Before finishing any UI component, verify:

  • Squint test: blur your eyes — does the hierarchy still read? Primary content stands out?
  • Grayscale test: remove color mentally — does hierarchy work without it?
  • Theme test: toggle light/dark mode — do all elements remain readable?
  • Semantic tokens: using bg-card, text-foreground, border-border — not hardcoded colors?
  • Token pairing: every bg-[token] has its matching text-[token]-foreground?
  • Spacing: consistent scale, more space between groups than within
  • Typography: clear size/weight hierarchy, readable line length (max-w-prose)
  • Labels: de-emphasized, skipped when format is obvious
  • Empty state: shows EmptyState, not blank space or null
  • Actions: clear primary/secondary/tertiary hierarchy
  • Responsive: tested at 375px and 1280px
  • Shadows: used instead of heavy borders where possible
  • shadcn/ui: using <Input>, <Label>, <Select>, <Card>, <Table>, <Badge> — not raw HTML?

DO NOT

  • DO NOT use text-black — use text-foreground for the darkest text.
  • DO NOT use hardcoded colors (bg-white, text-gray-500, border-gray-200) — use semantic tokens (bg-card, text-muted-foreground, border-border) so themes work.
  • DO NOT use bg-[color] without its paired text-[color]-foreground — contrast breaks in dark mode.
  • DO NOT give every piece of content equal visual weight — establish hierarchy.
  • DO NOT use borders for every separation — prefer shadows, spacing, or background colors.
  • DO NOT use gray text on colored backgrounds — use the foreground pair with opacity.
  • DO NOT make all buttons look equally important — one primary, rest secondary/ghost.
  • DO NOT skip empty states — use EmptyState from @/features/feedback.
  • DO NOT use arbitrary spacing values — stick to Tailwind's constrained scale.
  • DO NOT center-align text longer than 2-3 lines — left-align for readability.
  • DO NOT go below font-normal (400) weight — de-emphasize with size or color instead.
  • DO NOT make destructive actions visually dominant by default — most are secondary actions.
  • DO NOT define new colors without both :root and .dark values — themes will break.
  • DO NOT use raw HTML form elements (<input>, <select>, <textarea>, <label>) — always use shadcn/ui components (<Input>, <Select>, <Textarea>, <Label>).
  • DO NOT use raw <table> elements — use shadcn/ui <Table> components.
  • DO NOT hand-craft card or badge markup — use shadcn/ui <Card> and <Badge> components.
Skills Info
Original Name:ui-designAuthor:madlado87