Product UI Craft
This skill guides creation of production-grade product interfaces that feel polished, responsive, and premium. The goal: interfaces that look like they came from an expert product designer, not AI-generated templates.
Inspiration: Linear, Stripe, Superhuman, Figma, Notion, Slack
Core Philosophy
Production quality comes from invisible craft — details users feel but can't pinpoint:
- Keyboard-first — Power users never touch the mouse
- Instant feedback — Optimistic updates, zero perceived latency
- Graceful states — Loading, empty, and error states that build trust
- Systematic consistency — Design tokens, not hardcoded values
- Purposeful motion — Physics-based animation that feels natural
If users notice the interface, something is wrong. Great product UI disappears.
Decision Framework
When Starting a New Interface
Define the interaction model first
- What are the primary keyboard shortcuts?
- How does selection work?
- What's the command palette scope?
Establish the token system
- Set up semantic colors, spacing scale, and typography
- Never hardcode values — always use tokens
- See tokens.md
Map all states
- Loading: skeleton or spinner?
- Empty: what action do we guide toward?
- Error: how do we help recovery?
- See states.md
Plan motion intentionally
- What transitions maintain spatial continuity?
- Where does micro-interaction add delight?
- What should be instant?
- See animation.md
Choosing Animation Approach
| Scenario |
Approach |
Timing |
| Button hover/active |
CSS transition |
100-150ms |
| Dropdown open |
CSS or spring |
150-200ms |
| Modal/dialog |
Spring animation |
200-300ms |
| List reorder |
Layout animation |
spring |
| State indicator |
CSS transition |
instant-150ms |
| Loading spinner |
Linear rotation |
continuous |
Rule: If you're debating whether to animate something, make it instant. Animation should be obvious wins.
Loading State Selection
| Duration |
Pattern |
| <200ms |
No indicator (feels instant) |
| 200ms-2s |
Spinner in context |
| >2s |
Skeleton screen |
| Unknown/long |
Progress bar + message |
Empty State Design
Ask: "What should the user do next?"
- First-time user → Guide to primary action
- Filtered to zero → Offer to clear filters
- Completed state → Celebrate (inbox zero)
- Search no results → Suggest alternatives
Implementation Checklist
Keyboard & Focus
States
Motion
Visual Polish
Reference Files
Detailed implementation patterns organized by domain:
- interactions.md — Command palette, keyboard shortcuts, focus management, selection patterns, drag interactions, cursor states
- animation.md — Easing functions, spring physics, micro-interactions, page transitions, loading animations
- states.md — Loading states, empty states, error handling, edge cases, optimistic updates
- tokens.md — Color tokens, typography scale, spacing system, shadows, theming
- patterns.md — Concrete code recipes: navigation, lists, forms, toasts, modals, menus, scroll behavior
Quick Patterns
Command Palette Trigger
useEffect(() => {
const handler = (e: KeyboardEvent) => {
if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
e.preventDefault();
setOpen(prev => !prev);
}
};
window.addEventListener('keydown', handler);
return () => window.removeEventListener('keydown', handler);
}, []);
Spring Animation Config
// Framer Motion presets
const springs = {
snappy: { type: 'spring', stiffness: 400, damping: 30 },
smooth: { type: 'spring', stiffness: 300, damping: 30 },
bouncy: { type: 'spring', stiffness: 400, damping: 15 },
};
Semantic Color Token Structure
:root {
/* Surfaces */
--surface-primary: #ffffff;
--surface-secondary: #f9fafb;
--surface-hover: rgba(0, 0, 0, 0.04);
/* Text */
--text-primary: #111827;
--text-secondary: #6b7280;
--text-tertiary: #9ca3af;
/* Accent */
--accent: #3b82f6;
--accent-subtle: rgba(59, 130, 246, 0.1);
}
Skeleton Shimmer
.skeleton {
background: linear-gradient(90deg,
var(--surface-secondary) 0%,
var(--surface-tertiary) 50%,
var(--surface-secondary) 100%);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
}
@keyframes shimmer {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
Toast Entrance
<motion.div
initial={{ opacity: 0, y: 20, scale: 0.95 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, scale: 0.95 }}
transition={{ type: 'spring', stiffness: 400, damping: 25 }}
>
Anti-Patterns to Avoid
Don't:
- Use
ease or linear for UI transitions (use ease-out or springs)
- Animate width/height/top/left (use transform)
- Show spinners for fast operations (<200ms)
- Use
alert() or browser-native dialogs
- Hardcode colors, spacing, or font sizes
- Forget keyboard navigation
- Skip empty/error states
- Over-animate (if in doubt, make it instant)
Do:
- Prefer CSS transitions for simple state changes
- Use springs for entrances and complex motion
- Show skeletons that match content structure
- Make all destructive actions reversible (undo)
- Test with keyboard-only navigation
- Handle all edge cases (0, 1, many, overflow)
- Respect
prefers-reduced-motion
1---2name: product-ui3description: Build production-grade product interfaces with the invisible craft that makes professional software feel polished, responsive, and premium. Use this skill when building web applications, dashboards, SaaS interfaces, or any product UI that should feel like Linear, Stripe, Superhuman, Figma, Notion, or Slack. Focuses on interaction design, physics-based animation, state handling, and systematic design tokens — the quality users feel but can't pinpoint.4---56# Product UI Craft78This skill guides creation of production-grade product interfaces that feel polished, responsive, and premium. The goal: interfaces that look like they came from an expert product designer, not AI-generated templates.910**Inspiration:** Linear, Stripe, Superhuman, Figma, Notion, Slack1112## Core Philosophy1314Production quality comes from invisible craft — details users feel but can't pinpoint:15- **Keyboard-first** — Power users never touch the mouse16- **Instant feedback** — Optimistic updates, zero perceived latency17- **Graceful states** — Loading, empty, and error states that build trust18- **Systematic consistency** — Design tokens, not hardcoded values19- **Purposeful motion** — Physics-based animation that feels natural2021If users notice the interface, something is wrong. Great product UI disappears.2223## Decision Framework2425### When Starting a New Interface26271. **Define the interaction model first**28 - What are the primary keyboard shortcuts?29 - How does selection work?30 - What's the command palette scope?31322. **Establish the token system**33 - Set up semantic colors, spacing scale, and typography34 - Never hardcode values — always use tokens35 - See [tokens.md](references/tokens.md)36373. **Map all states**38 - Loading: skeleton or spinner?39 - Empty: what action do we guide toward?40 - Error: how do we help recovery?41 - See [states.md](references/states.md)42434. **Plan motion intentionally**44 - What transitions maintain spatial continuity?45 - Where does micro-interaction add delight?46 - What should be instant?47 - See [animation.md](references/animation.md)4849### Choosing Animation Approach5051| Scenario | Approach | Timing |52|----------|----------|--------|53| Button hover/active | CSS transition | 100-150ms |54| Dropdown open | CSS or spring | 150-200ms |55| Modal/dialog | Spring animation | 200-300ms |56| List reorder | Layout animation | spring |57| State indicator | CSS transition | instant-150ms |58| Loading spinner | Linear rotation | continuous |5960**Rule:** If you're debating whether to animate something, make it instant. Animation should be obvious wins.6162### Loading State Selection6364| Duration | Pattern |65|----------|---------|66| <200ms | No indicator (feels instant) |67| 200ms-2s | Spinner in context |68| >2s | Skeleton screen |69| Unknown/long | Progress bar + message |7071### Empty State Design7273Ask: "What should the user do next?"74- **First-time user** → Guide to primary action75- **Filtered to zero** → Offer to clear filters76- **Completed state** → Celebrate (inbox zero)77- **Search no results** → Suggest alternatives7879## Implementation Checklist8081### Keyboard & Focus82- [ ] Command palette (⌘K) implemented83- [ ] Primary actions have shortcuts84- [ ] Tab order is logical85- [ ] Focus trapping in modals86- [ ] Focus visible styles (not default)87- [ ] Roving tabindex for lists8889### States90- [ ] Skeleton screens match content structure91- [ ] Empty states guide action92- [ ] Errors are specific and recoverable93- [ ] 0, 1, many cases handled94- [ ] Text overflow handled (truncation + tooltip)95- [ ] Optimistic updates where appropriate9697### Motion98- [ ] Spring animations for entrances99- [ ] Reduced motion respected100- [ ] No layout-triggering animations101- [ ] Transitions under 300ms102- [ ] Staggered animations for lists103104### Visual Polish105- [ ] Semantic color tokens used106- [ ] Consistent spacing rhythm107- [ ] Text selection styled108- [ ] Custom scrollbar (if needed)109- [ ] Focus rings match brand110- [ ] Shadows create hierarchy111112## Reference Files113114Detailed implementation patterns organized by domain:115116- **[interactions.md](references/interactions.md)** — Command palette, keyboard shortcuts, focus management, selection patterns, drag interactions, cursor states117- **[animation.md](references/animation.md)** — Easing functions, spring physics, micro-interactions, page transitions, loading animations118- **[states.md](references/states.md)** — Loading states, empty states, error handling, edge cases, optimistic updates119- **[tokens.md](references/tokens.md)** — Color tokens, typography scale, spacing system, shadows, theming120- **[patterns.md](references/patterns.md)** — Concrete code recipes: navigation, lists, forms, toasts, modals, menus, scroll behavior121122## Quick Patterns123124### Command Palette Trigger125```tsx126useEffect(() => {127 const handler = (e: KeyboardEvent) => {128 if ((e.metaKey || e.ctrlKey) && e.key === 'k') {129 e.preventDefault();130 setOpen(prev => !prev);131 }132 };133 window.addEventListener('keydown', handler);134 return () => window.removeEventListener('keydown', handler);135}, []);136```137138### Spring Animation Config139```tsx140// Framer Motion presets141const springs = {142 snappy: { type: 'spring', stiffness: 400, damping: 30 },143 smooth: { type: 'spring', stiffness: 300, damping: 30 },144 bouncy: { type: 'spring', stiffness: 400, damping: 15 },145};146```147148### Semantic Color Token Structure149```css150:root {151 /* Surfaces */152 --surface-primary: #ffffff;153 --surface-secondary: #f9fafb;154 --surface-hover: rgba(0, 0, 0, 0.04);155 156 /* Text */157 --text-primary: #111827;158 --text-secondary: #6b7280;159 --text-tertiary: #9ca3af;160 161 /* Accent */162 --accent: #3b82f6;163 --accent-subtle: rgba(59, 130, 246, 0.1);164}165```166167### Skeleton Shimmer168```css169.skeleton {170 background: linear-gradient(90deg,171 var(--surface-secondary) 0%,172 var(--surface-tertiary) 50%,173 var(--surface-secondary) 100%);174 background-size: 200% 100%;175 animation: shimmer 1.5s infinite;176}177178@keyframes shimmer {179 0% { background-position: 200% 0; }180 100% { background-position: -200% 0; }181}182```183184### Toast Entrance185```tsx186<motion.div187 initial={{ opacity: 0, y: 20, scale: 0.95 }}188 animate={{ opacity: 1, y: 0, scale: 1 }}189 exit={{ opacity: 0, scale: 0.95 }}190 transition={{ type: 'spring', stiffness: 400, damping: 25 }}191>192```193194## Anti-Patterns to Avoid195196**Don't:**197- Use `ease` or `linear` for UI transitions (use ease-out or springs)198- Animate width/height/top/left (use transform)199- Show spinners for fast operations (<200ms)200- Use `alert()` or browser-native dialogs201- Hardcode colors, spacing, or font sizes202- Forget keyboard navigation203- Skip empty/error states204- Over-animate (if in doubt, make it instant)205206**Do:**207- Prefer CSS transitions for simple state changes208- Use springs for entrances and complex motion209- Show skeletons that match content structure210- Make all destructive actions reversible (undo)211- Test with keyboard-only navigation212- Handle all edge cases (0, 1, many, overflow)213- Respect `prefers-reduced-motion`