skills/ui-design/SKILL.md
Opinionated constraints for building better interfaces with agents. Use when building UI components, implementing animations, designing layouts, reviewing frontend accessibility, or working with Tailwind CSS, motion/react, or accessible primitives like Radix/Base UI.
npx skillsauth add ckorhonen/claude-skills ui-designInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Opinionated constraints for building better interfaces with agents.
| Requirement | Rule |
|-------------|------|
| Tailwind CSS | MUST use defaults (spacing, radius, shadows) before custom values |
| Animation library | MUST use motion/react (formerly framer-motion) for JS animation |
| CSS animation | SHOULD use tw-animate-css for entrance and micro-animations |
| Class logic | MUST use cn utility (clsx + tailwind-merge) |
| Requirement | Rule |
|-------------|------|
| Interactive primitives | MUST use accessible primitives (Base UI, React Aria, Radix) for keyboard/focus behavior |
| Existing components | MUST use project's existing primitives first |
| Consistency | NEVER mix primitive systems within the same interaction surface |
| New primitives | SHOULD prefer Base UI if compatible with stack |
| Icon buttons | MUST add aria-label to icon-only buttons |
| Custom behavior | NEVER rebuild keyboard or focus behavior by hand unless explicitly requested |
| Requirement | Rule |
|-------------|------|
| Destructive actions | MUST use AlertDialog for destructive or irreversible actions |
| Loading states | SHOULD use structural skeletons |
| Viewport height | NEVER use h-screen, use h-dvh |
| Fixed elements | MUST respect safe-area-inset |
| Error display | MUST show errors next to where the action happens |
| Input behavior | NEVER block paste in input or textarea elements |
| Requirement | Rule |
|-------------|------|
| Default | NEVER add animation unless explicitly requested |
| Compositor props | MUST animate only transform, opacity |
| Layout props | NEVER animate width, height, top, left, margin, padding |
| Paint props | SHOULD avoid background, color except for small, local UI (text, icons) |
| Entrance easing | SHOULD use ease-out on entrance |
| Feedback timing | NEVER exceed 200ms for interaction feedback |
| Looping | MUST pause looping animations when off-screen |
| Accessibility | MUST respect prefers-reduced-motion |
| Custom easing | NEVER introduce custom easing curves unless explicitly requested |
| Large surfaces | SHOULD avoid animating large images or full-screen surfaces |
| Requirement | Rule |
|-------------|------|
| Headings | MUST use text-balance |
| Body text | MUST use text-pretty for paragraphs |
| Data | MUST use tabular-nums |
| Dense UI | SHOULD use truncate or line-clamp |
| Letter spacing | NEVER modify letter-spacing (tracking-) unless explicitly requested |
| Requirement | Rule |
|-------------|------|
| Z-index | MUST use a fixed scale (no arbitrary z-x) |
| Square elements | SHOULD use size-x instead of w-x + h-x |
| Requirement | Rule |
|-------------|------|
| Blur effects | NEVER animate large blur() or backdrop-filter surfaces |
| Will-change | NEVER apply will-change outside an active animation |
| useEffect | NEVER use for anything expressible as render logic |
| Requirement | Rule | |-------------|------| | Gradients | NEVER use unless explicitly requested | | Purple/multicolor gradients | NEVER use | | Glow effects | NEVER use as primary affordances | | Shadows | SHOULD use Tailwind CSS default scale unless explicitly requested | | Empty states | MUST give one clear next action | | Accent colors | SHOULD limit to one per view | | Color tokens | SHOULD use existing theme or Tailwind CSS tokens before introducing new ones |
The most common UI design failures occur in spacing, color contrast, mobile responsiveness, state management, and visual hierarchy. These gotchas are discovered through repeated agent mistakes when building interfaces.
Problem: Using an inconsistent mix of Tailwind spacing values without a deliberate system makes layouts feel incoherent.
// ❌ WRONG - arbitrary pixel values, no spacing system
<div className="ml-[13px] mr-[22px] pt-[7px]">Content</div>
// ✅ CORRECT - use system scales
<div className="ml-4 mr-4 pt-4">Content</div>
Why it fails:
2px, some is 16px, no pattern)Fix:
0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, etc.)px-4 py-2p-6gap-8ml-6 (not ml-3)gap-x for all horizontal lists)Problem: Color combinations that fail WCAG AA contrast ratios (4.5:1 for text, 3:1 for UI components) making content illegible for low-vision users.
// ❌ WRONG - gray-400 text on gray-100 background fails contrast
<p className="text-gray-400 bg-gray-100">Important content</p>
// ✅ CORRECT - gray-700 on gray-100 passes WCAG AA
<p className="text-gray-700 bg-gray-100">Important content</p>
Why it fails:
Fix:
text-gray-900/text-white on any background (always safe)text-gray-700 on light backgroundstext-gray-300 on dark backgroundsgray-400 on gray-100 ❌red-500 on pink-100 ❌text-gray-400 on white, text-gray-600 on colorred-700 not red-300)Problem: Touch targets smaller than 44×44px, horizontal scrolling, or layouts that don't adapt to narrow viewports.
// ❌ WRONG - button too small, no responsive layout
<button className="px-2 py-1 text-xs">Click me</button>
<div className="flex gap-2">
<div className="w-80">Sidebar</div>
<div className="w-80">Content</div>
</div>
// ✅ CORRECT - hit target size, responsive stack
<button className="px-4 py-3 md:px-6 md:py-3">Click me</button>
<div className="flex flex-col md:flex-row gap-4">
<div className="w-full md:w-80">Sidebar</div>
<div className="flex-1">Content</div>
</div>
Why it fails:
Fix:
px-4 py-3 (standard: px-6 py-3)flex-col by default, md:flex-row for desktopw-full on mobile, md:w-80 or md:w-1/3 on desktopflex-1 instead of fixed widths for flexible layoutsProblem: Interfaces that only show the happy path, failing silently when loading, erroring, or empty.
// ❌ WRONG - no feedback states
<button onClick={save}>Save</button>
<div>{data.map(item => <ItemCard item={item} />)}</div>
// ✅ CORRECT - all states accounted for
<button onClick={save} disabled={loading}>
{loading ? 'Saving...' : 'Save'}
</button>
{loading && <LoadingSkeleton />}
{error && <ErrorAlert message={error} />}
{!loading && data.length === 0 && <EmptyState action="Create your first item" />}
{!loading && !error && data.map(item => <ItemCard item={item} />)}
Why it fails:
Fix:
disabled={true} + opacity-50 or cursor-not-allowedProblem: All text the same size, all colors the same weight, all spacing the same distance — nothing stands out.
// ❌ WRONG - flat hierarchy
<div className="text-lg text-gray-700">
<div className="text-lg text-gray-700">Subheading</div>
<div className="text-lg text-gray-700">Subtitle</div>
<button className="text-lg px-4 py-2">Action</button>
</div>
// ✅ CORRECT - clear hierarchy
<div>
<h1 className="text-3xl font-bold text-gray-900">Title</h1>
<h2 className="text-xl font-semibold text-gray-700 mt-6">Subheading</h2>
<p className="text-base text-gray-600 mt-2">Subtitle</p>
<button className="mt-6 px-6 py-3 bg-blue-600 text-white font-semibold rounded">
Primary Action
</button>
</div>
Why it fails:
Fix:
text-3xl (h1) → text-2xl (h2) → text-xl (h3)text-base (paragraphs) → text-sm (secondary)font-bold or font-semiboldfont-semiboldfont-normalfont-normal with lighter colortext-gray-900text-gray-600text-gray-500gap-8 (largest)gap-4gap-2transform, opacity
width, height, top, left, margin, padding, blur(), backdrop-filter
// Icon button - always add aria-label
<button aria-label="Close dialog">
<XIcon />
</button>
// Respect reduced motion
@media (prefers-reduced-motion: reduce) {
* { animation: none !important; }
}
import { clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
documentation
Create or expand an Idea.md / IDEA.md file from a rough description, existing repo, conversation history, notes, or other early-stage product inputs. Use when the user asks to "write an Idea.md", "turn this into an idea file", "capture this product idea", "expand this concept", or wants a repo-grounded concept brief before validation, PRD, or implementation work.
development
Write structured implementation plans from specs or requirements before touching code. Use when given a spec, requirements doc, or feature description, when user says "plan this out", "write a plan for", "how should we implement", or before starting any multi-step coding task.
testing
Expert guidance for video editing with ffmpeg, encoding best practices, and quality optimization. Use when working with video files, transcoding, remuxing, encoding settings, color spaces, or troubleshooting video quality issues.
development
Design and implement retro/cyberpunk/hacker-style terminal UIs. Covers React (Tuimorphic), SwiftUI (Metal shaders), and CSS approaches. Use when creating terminal aesthetics, CRT effects, neon glow, scanlines, phosphor green displays, or retro-futuristic interfaces.