UI Design
Opinionated constraints for building better interfaces with agents.
When to Use
- Building UI components with Tailwind CSS
- Implementing animations or transitions
- Adding interactive elements with keyboard/focus behavior
- Reviewing frontend code for accessibility
- Designing layouts with proper z-index and spacing
- Working with loading states, error handling, or empty states
Stack
| 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) |
Components
| 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 |
Interaction
| 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 |
Animation
| 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 |
Typography
| 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 |
Layout
| 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 |
Performance
| 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 |
Design
| 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 |
Quick Reference
Allowed Animation Properties
transform, opacity
Forbidden Animation Properties
width, height, top, left, margin, padding, blur(), backdrop-filter
Required Accessibility Patterns
// Icon button - always add aria-label
<button aria-label="Close dialog">
<XIcon />
</button>
// Respect reduced motion
@media (prefers-reduced-motion: reduce) {
* { animation: none !important; }
}
Class Utility Pattern
import { clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
1---2name: ui-design-53description: 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.4---5
6# UI Design
7
8Opinionated constraints for building better interfaces with agents.
9
10## When to Use
11
12- Building UI components with Tailwind CSS
13- Implementing animations or transitions
14- Adding interactive elements with keyboard/focus behavior
15- Reviewing frontend code for accessibility
16- Designing layouts with proper z-index and spacing
17- Working with loading states, error handling, or empty states
18
19## Stack
20
21| Requirement | Rule |
22|-------------|------|
23| Tailwind CSS | MUST use defaults (spacing, radius, shadows) before custom values |
24| Animation library | MUST use `motion/react` (formerly `framer-motion`) for JS animation |
25| CSS animation | SHOULD use `tw-animate-css` for entrance and micro-animations |
26| Class logic | MUST use `cn` utility (`clsx` + `tailwind-merge`) |
27
28## Components
29
30| Requirement | Rule |
31|-------------|------|
32| Interactive primitives | MUST use accessible primitives (`Base UI`, `React Aria`, `Radix`) for keyboard/focus behavior |
33| Existing components | MUST use project's existing primitives first |
34| Consistency | NEVER mix primitive systems within the same interaction surface |
35| New primitives | SHOULD prefer [`Base UI`](https://base-ui.com/react/components) if compatible with stack |
36| Icon buttons | MUST add `aria-label` to icon-only buttons |
37| Custom behavior | NEVER rebuild keyboard or focus behavior by hand unless explicitly requested |
38
39## Interaction
40
41| Requirement | Rule |
42|-------------|------|
43| Destructive actions | MUST use `AlertDialog` for destructive or irreversible actions |
44| Loading states | SHOULD use structural skeletons |
45| Viewport height | NEVER use `h-screen`, use `h-dvh` |
46| Fixed elements | MUST respect `safe-area-inset` |
47| Error display | MUST show errors next to where the action happens |
48| Input behavior | NEVER block paste in `input` or `textarea` elements |
49
50## Animation
51
52| Requirement | Rule |
53|-------------|------|
54| Default | NEVER add animation unless explicitly requested |
55| Compositor props | MUST animate only `transform`, `opacity` |
56| Layout props | NEVER animate `width`, `height`, `top`, `left`, `margin`, `padding` |
57| Paint props | SHOULD avoid `background`, `color` except for small, local UI (text, icons) |
58| Entrance easing | SHOULD use `ease-out` on entrance |
59| Feedback timing | NEVER exceed `200ms` for interaction feedback |
60| Looping | MUST pause looping animations when off-screen |
61| Accessibility | MUST respect `prefers-reduced-motion` |
62| Custom easing | NEVER introduce custom easing curves unless explicitly requested |
63| Large surfaces | SHOULD avoid animating large images or full-screen surfaces |
64
65## Typography
66
67| Requirement | Rule |
68|-------------|------|
69| Headings | MUST use `text-balance` |
70| Body text | MUST use `text-pretty` for paragraphs |
71| Data | MUST use `tabular-nums` |
72| Dense UI | SHOULD use `truncate` or `line-clamp` |
73| Letter spacing | NEVER modify `letter-spacing` (`tracking-`) unless explicitly requested |
74
75## Layout
76
77| Requirement | Rule |
78|-------------|------|
79| Z-index | MUST use a fixed scale (no arbitrary `z-x`) |
80| Square elements | SHOULD use `size-x` instead of `w-x` + `h-x` |
81
82## Performance
83
84| Requirement | Rule |
85|-------------|------|
86| Blur effects | NEVER animate large `blur()` or `backdrop-filter` surfaces |
87| Will-change | NEVER apply `will-change` outside an active animation |
88| useEffect | NEVER use for anything expressible as render logic |
89
90## Design
91
92| Requirement | Rule |
93|-------------|------|
94| Gradients | NEVER use unless explicitly requested |
95| Purple/multicolor gradients | NEVER use |
96| Glow effects | NEVER use as primary affordances |
97| Shadows | SHOULD use Tailwind CSS default scale unless explicitly requested |
98| Empty states | MUST give one clear next action |
99| Accent colors | SHOULD limit to one per view |
100| Color tokens | SHOULD use existing theme or Tailwind CSS tokens before introducing new ones |
101
102## Quick Reference
103
104### Allowed Animation Properties
105```
106transform, opacity
107```
108
109### Forbidden Animation Properties
110```
111width, height, top, left, margin, padding, blur(), backdrop-filter
112```
113
114### Required Accessibility Patterns
115```tsx
116// Icon button - always add aria-label
117<button aria-label="Close dialog">
118 <XIcon />
119</button>
120
121// Respect reduced motion
122@media (prefers-reduced-motion: reduce) {
123 * { animation: none !important; }
124}
125```
126
127### Class Utility Pattern
128```tsx
129import { clsx } from 'clsx';
130import { twMerge } from 'tailwind-merge';
131
132export function cn(...inputs: ClassValue[]) {
133 return twMerge(clsx(inputs));
134}
135```