refactoring-ui
Audit and fix visual hierarchy, spacing, color, and depth in web UIs. Use when the user mentions "my UI looks off" (or amateur/unprofessional), "fix the design", "Tailwind styling", "color palette", "visual hierarchy", "design system", "spacing scale", or "component styling". Als
Install
npx skills add https://github.com/wondelai/skills/tree/main/plugins/ux-design/skills/refactoring-ui
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install wondelai-skills@llmmart
git clone https://github.com/wondelai/skills.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole wondelai/skills collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
Refactoring UI Design System
A practical, opinionated approach to UI design. Apply these principles when generating frontend code, reviewing designs, or advising on visual improvements.
Core Principle
Design in grayscale first. Add color last. This forces proper hierarchy through spacing, contrast, and typography before relying on color as a crutch.
The foundation: Great UI isn't about talent — it's about systems. Constrained scales for spacing, type, color, and shadows produce consistently professional results. Start with too much white space and remove; leave details (icons, shadows, micro-interactions) until layout and hierarchy work.
Scoring
Goal: 10/10. Score by counting satisfied rows in the Quick Diagnostic (8 yes/no checks): score = round(satisfied / 8 × 10). Bands follow directly: 10 = all 8 pass (hierarchy reads blurred and in grayscale, every value on a scale); 9 = exactly 1 gap (usually weak hierarchy or thin white space); 6-8 = 2-3 gaps; <=5 = 4+ gaps (arbitrary spacing, color doing the work hierarchy should, or failing contrast). Always state the current score and the specific diagnostic rows to fix to reach 10/10.
The Refactoring UI Framework
Seven principles for building professional interfaces without a designer:
1. Visual Hierarchy
Core concept: Not everything can be important. Create hierarchy through three levers: size, weight, and color.
Why it works: When every element competes for attention, nothing stands out; deliberately de-emphasizing secondary content makes primary content powerful by contrast.
Key insights:
- Combine levers, don't multiply — primary text = large OR bold OR dark, not all three; save "all three" for the single most important element
- Labels are secondary — form labels, table headers, and metadata support the data, not compete with it; make them smaller, lighter, or uppercase-small
- Semantic color ≠ visual weight — a muted secondary button often beats screaming red for routine destructive actions
Product applications:
| Context | Hierarchy Technique | Example |
|---|---|---|
| Form fields | De-emphasize labels, emphasize values | Small uppercase label above large value |
| Dashboards | Key metric large, context small | "$42,300" large, "vs last month" small |
| Tables | De-emphasize headers, emphasize data | Headers uppercase small gray, data normal |
Design patterns:
- Three-level hierarchy: Size (large/base/small), Weight (bold/medium/normal), Color (dark/medium/light gray)
- Button hierarchy: primary (filled), secondary (outlined or muted), tertiary (text only)
Ethical boundary: Don't use hierarchy tricks to hide important information like pricing, terms, or cancellation options.
See references/advanced-patterns.md when designing components beyond static layout — interaction/hover/focus states, form design, empty states, border-radius systems, text truncation, and responsive breakpoints.
2. Spacing & Sizing
Core concept: Use a constrained spacing scale, not arbitrary values. Spacing defines relationships — closer elements read as more related.
Why it works: Arbitrary spacing (padding: 13px) creates inconsistency; a fixed scale forces deliberate decisions and harmonious layouts. Generous spacing feels premium; dense feels overwhelming.
Key insights:
- Use the scale: 4, 8, 16, 24, 32, 48, 64px
- Start with too much white space, then remove — you'll almost never remove enough
- Spacing between groups must exceed spacing within groups
- Constrain widths: text to 45-75 characters (
max-w-prose), forms to 300-500px; full-width is almost never right
Product applications:
| Context | Spacing Strategy | Example |
|---|---|---|
| Icon + label | Tight coupling (4px) | Small gap keeps them connected |
| Card sections | Section separation (24px) | Title, content, footer blocks |
| Page sections | Major sections (48-64px) | Hero, features, testimonials |
CSS patterns:
p-1(4px)p-2(8px)p-4(16px)p-6(24px)p-8(32px)p-12(48px)p-16(64px)max-w-prose(65ch)max-w-md(28rem)max-w-lg(32rem)max-w-xl(36rem)gap-2for related items,gap-6for section separation
3. Typography
Core concept: Use a modular type scale, constrain line heights by context, and limit to two font families maximum.
Why it works: A modular scale (steps growing ~1.2× each) creates natural visual rhythm; tight line heights on headings and relaxed on body text improve readability in each context.
Key insights:
- Scale: 12, 14, 16, 18, 20, 24, 30, 36px (~1.2 modular, hand-tuned)
- Headings: tight line height (1.0-1.25); body: relaxed (1.5-1.75); wider text needs more line height
- Avoid weights below 400 for body text; use bold (600-700) for emphasis, not everything
- Two fonts max: one for headings, one for body (or one family with weight variation)
Product applications:
| Context | Typography Rule | Example |
|---|---|---|
| Hero headline | 36px, line-height 1.1, bold | Large impactful statement |
| Body text | 16px, line-height 1.75, normal | Comfortable reading |
| Captions/labels | 12-14px, line-height 1.5, medium gray | Secondary information |
CSS patterns:
text-xs(12px)text-sm(14px)text-base(16px)text-lg(18px)text-xl(20px)font-normal(400)font-medium(500)font-semibold(600)font-bold(700)leading-tight(1.25)leading-normal(1.5)leading-relaxed(1.75)
4. Color
Core concept: Build a systematic palette with 5-9 shades per color, add subtle saturation to grays, and design in grayscale first.
Why it works: Random colors clash; a predefined shade system ensures consistency, and HSL adjustments create natural-feeling lighter and darker variants.
Key insights:
- Each color needs 5-9 shades from near-white to near-black (50-900); darkest is not pure black — use
#111827, not#000000 - Pure grays look lifeless — tint them (cool UI: blue like
#64748b; warm UI: yellow/brown like#78716c) - HSL: lighter = raise lightness, lower saturation, hue toward 60°; darker = the reverse, hue toward 0°/240°
- Contrast minimums: 4.5:1 body text, 3:1 large text (18px+); use
#374151(gray-700) on white, not lighter grays
Product applications:
| Context | Color Strategy | Example |
|---|---|---|
| Primary palette | 9 shades (50-900) of brand color | Blue-500 buttons, Blue-100 backgrounds |
| Semantic colors | Success/warning/error with shade ranges | Green-500 success, Red-500 errors |
| Text colors | Three levels: dark, medium, light | text-gray-900, text-gray-600, text-gray-400 |
CSS patterns:
text-gray-900(dark)text-gray-600(medium)text-gray-400(light)bg-blue-50for subtle backgrounds,bg-blue-500for primary actionsborder-gray-200for subtle borders,border-gray-300for stronger
See references/theming-dark-mode.md when building a dark theme — hex shade scales, why darkest is #111827 not black (halation), and conveying elevation via lightness instead of shadow. See references/accessibility-depth.md when contrast, focus rings, keyboard nav, or screen-reader support is in scope — full WCAG 2.1 AA checklist and fixes.
5. Depth & Shadows
Core concept: Use a shadow scale to convey elevation — small shadows for slightly raised elements, large shadows for floating ones.
Why it works: The eye reads shadow size as height above the page; a consistent scale makes elevation legible, so users intuit what's interactive, floating, or background.
Key insights:
- Small shadows = raised slightly (buttons, cards); large = floating (modals, dropdowns)
- Good shadows have two parts: a tight dark shadow for crispness plus a larger soft one for atmosphere
- Depth without shadows: lighter top border + darker bottom border, subtle gradients, overlapping elements
- Don't overuse — if everything floats, nothing has depth; shadow color is transparent dark, never opaque gray
Product applications:
| Context | Shadow Level | Example |
|---|---|---|
| Buttons | shadow-sm (subtle raise) |
Slightly elevated above surface |
| Dropdowns | shadow-lg (floating) |
Menu clearly above content |
| Modals | shadow-xl (highest) |
Overlay detached from page |
CSS patterns:
shadow-sm:0 1px 2px rgba(0,0,0,0.05)shadow-md:0 4px 6px rgba(0,0,0,0.1)shadow-lg:0 10px 15px rgba(0,0,0,0.1)shadow-xl:0 20px 25px rgba(0,0,0,0.15)
See references/animation-microinteractions.md when adding motion to interactive elements — durations, easing curves, loading states, and the prefers-reduced-motion rule.
6. Images & Icons
Core concept: Treat images as design elements, not afterthoughts. Size icons deliberately and use overlays to keep text readable on images.
Why it works: Poorly sized icons look awkward and unstyled images break consistency; deliberate treatment (overlays, object-fit, radius) makes interfaces feel polished.
Key insights:
- Size icons relative to context; use sets with consistent stroke width and style
- Never stretch or distort — use
object-fit: coverwith fixed aspect ratios and crop deliberately - Text over images needs an overlay (semi-transparent gradient)
- Empty states are an opportunity — use illustrations plus a clear CTA, not just text
Product applications:
| Context | Image/Icon Technique | Example |
|---|---|---|
| Hero images | Semi-transparent gradient overlay | Text readable over any photo |
| Avatars | Consistent size, rounded, fallback initials | 40px circle, object-fit cover |
| Empty states | Custom illustration + CTA | Friendly illustration with "Get started" |
CSS patterns:
object-fit: coverwith fixedaspect-ratiofor consistent display- Icon sizing:
w-4 h-4inline,w-6 h-6navigation,w-8 h-8feature icons - Overlay:
bg-gradient-to-t from-black/60 to-transparentfor text on images
7. Layout & Composition
Core concept: Don't center everything. Use alignment, overlap, and emphasis variation to create engaging compositions.
Why it works: A consistent left edge gives the eye a fixed return point per line, so it costs less to scan; centered multi-line text moves that edge every line and slows reading.
Key insights:
- Left-align by default; center only short headlines, heroes, single-action CTAs, and empty states
- Cards don't need to contain everything — let images bleed to edges or overlap containers
- Vary visual treatment in lists and feeds — feature some items, minimize others
- Use alignment to create relationships between unrelated elements
Product applications:
| Context | Layout Strategy | Example |
|---|---|---|
| Hero sections | Centered text, generous spacing | Short headline + subtext + single CTA |
| Blog feeds | Varied card sizes for emphasis | First post large, rest in 2-column grid |
| Content pages | Constrained width, left-aligned | max-w-prose container with left text |
CSS patterns:
text-leftby default,text-centeronly for heroes and short headlinesgrid grid-cols-3 gap-6for feature grids;max-w-4xl mx-autofor page containersoverflow-hiddenon cards withobject-fit: coverimages that bleed to edges
See references/data-visualization.md when laying out charts, tables, or dashboards — chart-type selection, color use in charts, table density, and dashboard composition.
Common Mistakes
| Mistake | Why It Fails | Fix |
|---|---|---|
| "Looks amateur" | Insufficient white space, unconstrained widths | More white space, constrain content widths |
| "Feels flat" | No depth differentiation | Subtle shadows, border-bottom on sections |
| "Text is hard to read" | Poor line-height, too wide, low contrast | Increase line-height, constrain width, boost contrast |
| "Everything looks the same" | No visual hierarchy | Vary size/weight/color between primary and secondary |
| "Feels cluttered" | Equal spacing everywhere | Group related items, larger gaps between groups |
| "Colors clash" | Random choices, no system | Reduce saturation, more grays, limit to palette |
| "Buttons don't pop" | Low contrast with surroundings | Increase contrast, add shadow |
| Arbitrary values | px values like 13, 17, 23 breed inconsistency | Stick to the spacing and type scales |
Quick Diagnostic
Audit any UI design:
| Question | If No | Action |
|---|---|---|
| Does hierarchy read when squinting (blur test)? | Elements competing | Increase primary/secondary contrast |
| Does it work in grayscale? | Color is a crutch | Strengthen size/weight/spacing hierarchy |
| Is there enough white space? | Probably not — most designs are too dense | Increase spacing, especially between groups |
| Are labels de-emphasized vs. values? | Labels competing with data | Smaller, lighter, or uppercase-small labels |
| Does spacing follow a consistent scale? | Arbitrary spacing = visual noise | Use 4/8/16/24/32/48/64 only |
| Is text width constrained? | Long lines fatigue readers | Apply max-w-prose (~65ch) |
| Do colors have sufficient contrast? | Accessibility failure | WCAG-check; use gray-700+ on white |
| Are shadows appropriate for elevation? | Elements float at wrong level | Match shadow scale to element purpose |
Further Reading
For the complete system with visual before/after examples:
- "Refactoring UI" by Adam Wathan & Steve Schoger (the full book with hundreds of visual examples)
- "The Design of Everyday Things" by Don Norman (foundational design thinking and usability)
- "Don't Make Me Think" by Steve Krug (web usability principles that complement Refactoring UI)
- Refactoring UI — Official site with resources and examples
About the Authors
Adam Wathan, creator of Tailwind CSS, and Steve Schoger, the visual designer behind its design language, wrote Refactoring UI to teach developers systematic, repeatable design techniques. Their approach replaces artistic talent with constrained systems — fixed scales for spacing, typography, color, and shadows — that produce professional results.
Files (skills)
-
references
-
accessibility-depth.md 11.5 KB
# Accessibility Deep Dive Comprehensive WCAG 2.1 AA compliance checklist with practical implementation guidance. ## Table of Contents 1. [Accessibility Philosophy](#accessibility-philosophy) 2. [WCAG 2.1 AA Checklist](#wcag-21-aa-checklist) 3. [Focus Management](#focus-management) 4. [Screen Reader Considerations](#screen-reader-considerations) 5. [Testing Tools](#testing-tools) 6. [Common Fixes Quick Reference](#common-fixes-quick-reference) --- ## Accessibility Philosophy Accessibility isn't just compliance—it improves UX for everyone: - Keyboard navigation helps power users - Good contrast helps in bright sunlight - Clear focus states help everyone understand what's selected - Proper headings help screen readers AND SEO --- ## WCAG 2.1 AA Checklist ### Perceivable Users must be able to perceive content. #### 1.1 Text Alternatives **All images need alt text:** ```html <!-- Decorative image (no alt) --> <img src="divider.svg" alt="" role="presentation"> <!-- Informative image --> <img src="chart.png" alt="Sales increased 40% from Q1 to Q2"> <!-- Functional image (button/link) --> <button> <img src="search.svg" alt="Search"> </button> <!-- Complex image (needs long description) --> <figure> <img src="diagram.png" alt="System architecture overview" aria-describedby="diagram-desc"> <figcaption id="diagram-desc">Detailed description of the system architecture...</figcaption> </figure> ``` **Icons:** ```html <!-- Icon with visible text (icon is decorative) --> <button> <svg aria-hidden="true">...</svg> <span>Settings</span> </button> <!-- Icon-only button --> <button aria-label="Settings"> <svg aria-hidden="true">...</svg> </button> ``` #### 1.3 Adaptable **Semantic HTML structure:** ```html <!-- Use semantic elements --> <header>...</header> <nav>...</nav> <main> <article> <h1>Page Title</h1> <section> <h2>Section Title</h2> </section> </article> </main> <footer>...</footer> <!-- Not this --> <div class="header">...</div> <div class="nav">...</div> ``` **Heading hierarchy:** - One `<h1>` per page - Don't skip levels (h1 → h3) - Headings describe content structure **Form labels:** ```html <!-- Explicit label --> <label for="email">Email</label> <input id="email" type="email"> <!-- Implicit label --> <label> Email <input type="email"> </label> <!-- Hidden label (for visual designs without labels) --> <label for="search" class="sr-only">Search</label> <input id="search" type="search" placeholder="Search..."> ``` #### 1.4 Distinguishable **Color contrast requirements:** | Content Type | Minimum Ratio | Tool | |--------------|---------------|------| | Normal text (<18px) | 4.5:1 | WebAIM Contrast Checker | | Large text (≥18px or ≥14px bold) | 3:1 | | | UI components & graphics | 3:1 | | **Common contrast fixes:** ```css /* Too light - fails */ .text-light { color: #9ca3af; } /* gray-400: 3.1:1 on white */ /* Passes AA */ .text-muted { color: #6b7280; } /* gray-500: 4.6:1 on white */ /* Passes AAA */ .text-strong { color: #374151; } /* gray-700: 9.1:1 on white */ ``` **Don't rely on color alone:** ```html <!-- Bad: only color indicates error --> <input class="border-red-500"> <!-- Good: color + icon + text --> <input class="border-red-500" aria-invalid="true" aria-describedby="error"> <p id="error" class="text-red-600"> <svg aria-hidden="true">⚠️</svg> Email is required </p> ``` **Text resize:** - Content must be readable at 200% zoom - Use relative units (rem, em) not px for text - Test by zooming browser to 200% --- ### Operable Users must be able to operate the interface. #### 2.1 Keyboard Accessible **All functionality must work with keyboard:** | Key | Expected Behavior | |-----|-------------------| | Tab | Move to next focusable element | | Shift+Tab | Move to previous focusable element | | Enter | Activate links, buttons | | Space | Activate buttons, toggle checkboxes | | Arrows | Navigate within components (tabs, menus, radios) | | Escape | Close modals, dropdowns, cancel actions | **Focus must be visible:** ```css /* Don't remove focus outlines */ :focus { outline: none; /* ❌ Never do this without replacement */ } /* Do provide visible focus */ :focus-visible { outline: 2px solid #3b82f6; outline-offset: 2px; } /* Or use ring utility */ .focusable:focus-visible { @apply ring-2 ring-blue-500 ring-offset-2; } ``` **Keyboard traps:** - Modal dialogs should trap focus inside - But must have a way to exit (Escape key, close button) ```jsx // Focus trap for modals function Modal({ isOpen, onClose, children }) { const modalRef = useRef(); useEffect(() => { if (isOpen) { // Focus first focusable element const firstFocusable = modalRef.current.querySelector('button, input, a'); firstFocusable?.focus(); // Trap focus inside const handleTab = (e) => { if (e.key === 'Tab') { // ... trap logic } if (e.key === 'Escape') { onClose(); } }; document.addEventListener('keydown', handleTab); return () => document.removeEventListener('keydown', handleTab); } }, [isOpen]); // Return focus on close } ``` **Skip links:** ```html <body> <a href="#main-content" class="sr-only focus:not-sr-only"> Skip to main content </a> <nav>...</nav> <main id="main-content">...</main> </body> ``` #### 2.4 Navigable **Page titles:** - Unique, descriptive page titles - Format: `Page Name | Site Name` **Focus order:** - Must follow logical reading order - Don't use positive `tabindex` values (messes up order) - Only use `tabindex="0"` (make focusable) or `tabindex="-1"` (programmatically focusable) **Link purpose:** ```html <!-- Bad --> <a href="/article">Click here</a> <a href="/article">Read more</a> <!-- Good --> <a href="/article">Read more about accessibility best practices</a> <!-- Or with context --> <a href="/article" aria-describedby="article-title">Read more</a> ``` --- ### Understandable Users must be able to understand content and operation. #### 3.1 Readable **Language declaration:** ```html <html lang="en"> <body> <p>This is English.</p> <p lang="fr">Ceci est français.</p> </body> </html> ``` #### 3.2 Predictable **Consistent navigation:** - Same navigation in same location across pages - Same elements behave the same way **No unexpected changes:** - Form inputs don't auto-submit on change - No unexpected pop-ups - Focus doesn't move unexpectedly ```html <!-- Bad: changes page on select --> <select onchange="window.location = this.value">...</select> <!-- Good: requires explicit action --> <select id="region">...</select> <button onclick="navigate()">Go</button> ``` #### 3.3 Input Assistance **Error identification:** ```html <label for="email">Email</label> <input id="email" type="email" aria-invalid="true" aria-describedby="email-error" > <p id="email-error" class="error"> Please enter a valid email address (e.g., name@example.com) </p> ``` **Required fields:** ```html <label for="name"> Name <span aria-hidden="true">*</span> <span class="sr-only">(required)</span> </label> <input id="name" required aria-required="true"> ``` **Error prevention for critical actions:** - Confirm destructive actions - Allow review before submission - Provide undo capability --- ### Robust Content must work with current and future technologies. #### 4.1 Compatible **Valid HTML:** - Unique IDs - Complete start/end tags - Proper nesting **ARIA usage:** ```html <!-- If you use ARIA, use it correctly --> <!-- Roles --> <div role="button" tabindex="0" onclick="...">Fake Button</div> <!-- Better: just use <button> --> <!-- States --> <button aria-pressed="true">Bold</button> <button aria-expanded="false" aria-controls="menu">Menu</button> <!-- Live regions --> <div aria-live="polite" aria-atomic="true"> <!-- Screen reader announces changes here --> </div> ``` --- ## Focus Management ### When to Manage Focus | Scenario | Focus Action | |----------|--------------| | Modal opens | Focus first element inside modal | | Modal closes | Return focus to trigger element | | Error occurs | Focus error message or first invalid field | | New content loads | Focus heading or first new element | | Item deleted | Focus previous/next item or container | ### Implementation ```jsx // Store trigger reference const triggerRef = useRef(); function openModal() { triggerRef.current = document.activeElement; setIsOpen(true); } function closeModal() { setIsOpen(false); // Return focus after state update setTimeout(() => triggerRef.current?.focus(), 0); } ``` ### Roving Tabindex (for component groups) ```jsx // Tab panels, menu items, radio groups function Tabs({ tabs }) { const [activeIndex, setActiveIndex] = useState(0); const handleKeyDown = (e) => { if (e.key === 'ArrowRight') { setActiveIndex((activeIndex + 1) % tabs.length); } if (e.key === 'ArrowLeft') { setActiveIndex((activeIndex - 1 + tabs.length) % tabs.length); } }; return ( <div role="tablist" onKeyDown={handleKeyDown}> {tabs.map((tab, i) => ( <button key={tab.id} role="tab" tabIndex={i === activeIndex ? 0 : -1} aria-selected={i === activeIndex} ref={i === activeIndex ? (el) => el?.focus() : null} > {tab.label} </button> ))} </div> ); } ``` --- ## Screen Reader Considerations ### Announce Dynamic Changes ```html <!-- Live region for status messages --> <div aria-live="polite" class="sr-only" id="status"></div> <script> function showSuccess(message) { document.getElementById('status').textContent = message; } </script> ``` ### Hide Decorative Content ```html <!-- Hidden from screen readers --> <svg aria-hidden="true">...</svg> <span aria-hidden="true">•</span> ``` ### Provide Context ```html <!-- Ambiguous button --> <button>Delete</button> <!-- Clear button --> <button aria-label="Delete comment by John">Delete</button> <!-- Or use aria-describedby --> <button aria-describedby="comment-123-author">Delete</button> <span id="comment-123-author" class="sr-only">comment by John</span> ``` --- ## Testing Tools ### Automated Testing | Tool | What It Catches | |------|-----------------| | axe DevTools | ~30% of WCAG issues | | WAVE | Similar to axe, visual overlay | | Lighthouse | Basic accessibility audit | | ESLint a11y plugin | Catches issues in JSX | ### Manual Testing Required Automated tools miss ~70% of issues. Manual testing needed for: - Keyboard navigation flow - Screen reader experience - Focus management - Meaningful alt text - Logical heading structure ### Testing Checklist - [ ] Navigate entire page with keyboard only - [ ] Test with screen reader (VoiceOver, NVDA) - [ ] Check color contrast with tool - [ ] Zoom to 200% and verify usability - [ ] Test with high contrast mode - [ ] Verify focus indicators visible - [ ] Check heading structure with outline tool - [ ] Run axe DevTools audit - [ ] Test forms with validation errors --- ## Common Fixes Quick Reference | Issue | Fix | |-------|-----| | Missing alt text | Add descriptive alt or `alt=""` for decorative | | Low contrast | Use gray-600+ for text on white | | Missing focus style | Add `focus-visible` ring/outline | | Click-only interaction | Add keyboard handler + focusability | | Missing form labels | Add `<label>` with `for` attribute | | Heading skip | Use h1→h2→h3 in order | | Color-only indicator | Add icon/text alongside color | | Modal focus trap | Trap focus, allow Escape to close | | Auto-playing media | Add pause control, don't autoplay | | Motion | Respect `prefers-reduced-motion` | -
advanced-patterns.md 10.7 KB
# Advanced Design Patterns Extended reference for complex scenarios. Load only when deeper guidance needed. ## Table of Contents 1. [Empty States](#empty-states) 2. [Form Design](#form-design) 3. [Image Treatment](#image-treatment) 4. [Icon Usage](#icon-usage) 5. [Interaction States](#interaction-states) 6. [Color Psychology](#color-psychology) 7. [Border Radius System](#border-radius-system) --- ## Empty States Empty states are opportunities, not afterthoughts. **Good empty states include:** - Illustration or icon (not generic) - Clear explanation of what goes here - Primary action to remedy emptiness - Optional secondary actions ``` ❌ "No items" ✅ "No projects yet. Create your first project to get started." [+ Create Project] ``` **Match the tone.** A todo app empty state can be playful; an enterprise dashboard should be professional. --- ## Form Design ### Input Sizing Match input width to expected content: - Email/URL: Full width or ~400px - Phone: ~200px - ZIP code: ~100px - Street address: Full width - City: ~200px - State dropdown: ~150px ### Placeholder vs. Label **Never use placeholder as the only label.** Placeholders disappear on focus. Always have a visible label. Placeholders work for: - Format hints: "MM/DD/YYYY" - Examples: "e.g., john@example.com" - Optional clarification ### Input States ``` Default: border-gray-300 Focus: border-blue-500 ring-2 ring-blue-200 Error: border-red-500 ring-2 ring-red-200 Disabled: bg-gray-100 text-gray-400 Success: border-green-500 (sparingly) ``` ### Button Hierarchy One primary action per view. Everything else is secondary or tertiary. ``` Primary: Solid color, high contrast (bg-blue-600 text-white) Secondary: Outlined or muted (border border-gray-300) Tertiary: Text only (text-blue-600 hover:underline) Danger: Red but not screaming (bg-red-600 for confirm, text-red-600 for trigger) ``` ### Form Layout - One column for simple forms - Two columns ONLY when inputs are related (First/Last name, City/State) - Labels above inputs on mobile, beside on desktop (optional) - Group related fields with subtle boundaries or spacing --- ## Image Treatment ### Background Images **Problem:** Text over images is often unreadable. **Solutions:** 1. Semi-transparent overlay: `bg-black/50` 2. Gradient overlay: `bg-gradient-to-t from-black/80 to-transparent` 3. Text shadow: `text-shadow: 0 2px 4px rgba(0,0,0,0.5)` 4. Solid color box behind text 5. Choose images with natural dark/simple areas for text ### User Avatars - Always have a fallback (initials, generic icon) - Consistent size per context (32px list, 48px card, 96px profile) - Round for people, square with border-radius for companies/products - Border adds polish: `ring-2 ring-white` for overlapping avatars ### Hero Images - Don't stretch—use `object-cover` - Consider `aspect-ratio` for consistency - Compress appropriately (WebP, quality 80%) --- ## Icon Usage ### Sizing Icons should feel balanced with adjacent text: - 12-14px text: 16px icon - 16px text: 20px icon - 18-20px text: 24px icon ### Icon + Text Pairing Always align icon center with text baseline or center. Add consistent gap (8px typical). ```html <span class="flex items-center gap-2"> <IconSettings class="w-5 h-5" /> <span>Settings</span> </span> ``` ### When to Use Icons - Navigation items - Common actions (edit, delete, share) - Status indicators - Feature lists (with caution—don't overdo) ### When NOT to Use Icons - Don't add icons just to fill space - Skip icons on buttons with clear text ("Submit", "Continue") - Avoid decorative-only icons that add no meaning --- ## Interaction States Every interactive element needs visible state changes: ### Hover - Subtle background change - Slight shadow increase - Color shift (darken primary by 10%) ### Active/Pressed - Darker than hover - Slight scale down (`scale-95`) - Reduce shadow ### Focus - Obvious ring (critical for accessibility) - Don't rely on color alone - `focus-visible` for keyboard-only focus ### Loading - Disable interaction - Show spinner or skeleton - Maintain layout size (prevent shift) ```css /* Example button states */ .btn { @apply bg-blue-600 hover:bg-blue-700 active:bg-blue-800; @apply focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2; @apply disabled:opacity-50 disabled:cursor-not-allowed; } ``` --- ## Color Psychology Use color purposefully: | Color | Association | Use for | |-------|-------------|---------| | Blue | Trust, calm, professional | Primary actions, links, corporate | | Green | Success, growth, go | Success states, positive actions | | Red | Error, danger, urgency | Errors, destructive actions, alerts | | Yellow/Orange | Warning, attention | Warnings, highlights | | Purple | Premium, creative | Premium features, creative apps | | Gray | Neutral, professional | Text, backgrounds, borders | ### Avoid - Red for non-destructive primary buttons - Green for errors (colorblind users) - Low-saturation colors for important actions - More than 3 accent colors per interface --- ## Border Radius System Stay consistent. Pick a system: **Sharp/Modern:** ``` none: 0 sm: 2px md: 4px lg: 6px full: 9999px (pills/circles) ``` **Soft/Friendly:** ``` none: 0 sm: 4px md: 8px lg: 12px xl: 16px full: 9999px ``` ### Rules - Nested elements: inner radius = outer radius - padding - Small elements get smaller radius (badges, tags) - Large elements can have larger radius (cards, modals) - Images inside cards: match card radius or use `overflow-hidden` --- ## Text Wrapping & Truncation ### When to Truncate - Navigation items - Table cells with fixed widths - Card titles (with hover to reveal) ```css .truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } ``` ### When NOT to Truncate - Body text - Important information - Search results - Error messages ### Multi-line Truncation ```css .line-clamp-2 { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; } ``` --- ## Responsive Breakpoints Standard breakpoints (Tailwind default): ``` sm: 640px (landscape phones) md: 768px (tablets) lg: 1024px (laptops) xl: 1280px (desktops) 2xl: 1536px (large screens) ``` ### Mobile-First Principles 1. Design for mobile first, add complexity for larger screens 2. Stack on mobile, side-by-side on desktop 3. Full-width inputs on mobile, constrained on desktop 4. Larger touch targets on mobile (44px minimum) 5. Hide secondary navigation in hamburger on mobile --- ## Modal and Overlay Patterns ### Modal Sizing | Content Type | Width | Height | |--------------|-------|--------| | Confirmation dialog | 400-500px | Auto (minimal) | | Form modal | 500-600px | Auto | | Content modal | 600-800px | 70-80vh max | | Full-screen modal | 100vw | 100vh | ### Modal Structure ``` ┌─────────────────────────────────────────────┐ │ Title ✕ Close │ ├─────────────────────────────────────────────┤ │ │ │ Modal content here │ │ │ ├─────────────────────────────────────────────┤ │ Cancel Primary Action│ └─────────────────────────────────────────────┘ ``` ### Backdrop ```css .backdrop { background: rgba(0, 0, 0, 0.5); /* Or with blur */ backdrop-filter: blur(4px); } ``` ### Modal Transitions ```css /* Fade + Scale */ .modal { opacity: 0; transform: scale(0.95); transition: opacity 200ms ease-out, transform 200ms ease-out; } .modal.open { opacity: 1; transform: scale(1); } ``` --- ## Dropdown and Menu Design ### Dropdown Positioning - Default: Below trigger, left-aligned - Flip: Above if no space below - Constrain to viewport ```css .dropdown { position: absolute; top: 100%; left: 0; margin-top: 4px; } ``` ### Menu Styling ```css .menu { min-width: 180px; max-height: 300px; overflow-y: auto; background: white; border-radius: 8px; box-shadow: 0 10px 25px rgba(0,0,0,0.15); } .menu-item { padding: 8px 12px; cursor: pointer; } .menu-item:hover { background: #f3f4f6; } .menu-divider { height: 1px; background: #e5e7eb; margin: 4px 0; } ``` ### Menu Item Types | Type | Visual | |------|--------| | Standard | Text label | | With icon | Icon + Label | | With shortcut | Label + Shortcut | | With description | Label + Description | | Destructive | Red text | | Disabled | Grayed out | --- ## Navigation Patterns ### Top Navigation ``` ┌─────────────────────────────────────────────────┐ │ Logo Nav Item Nav Item Nav Item CTA │ └─────────────────────────────────────────────────┘ ``` - Sticky on scroll (optional) - Collapse to hamburger on mobile - Clear active state ### Side Navigation ``` ┌──────────────┬──────────────────────────────────┐ │ Logo │ │ │ │ │ │ Dashboard │ Main Content │ │ Projects │ │ │ Settings │ │ │ │ │ │ ───────── │ │ │ Account │ │ │ Logout │ │ └──────────────┴──────────────────────────────────┘ ``` - Collapsible to icons on desktop - Full overlay on mobile - Group related items ### Breadcrumbs ``` Home > Category > Subcategory > Current Page ``` - Clickable except current page - Truncate middle items if too long - Show on detail/nested pages ### Tabs ```css .tabs { display: flex; border-bottom: 1px solid #e5e7eb; } .tab { padding: 12px 16px; border-bottom: 2px solid transparent; } .tab.active { border-bottom-color: #3b82f6; color: #3b82f6; } ``` - Underline or pill style - Horizontal scroll on mobile if many tabs - Consider vertical tabs for settings -
animation-microinteractions.md 9.3 KB
# Animation & Microinteractions Guidelines for when and how to animate UI elements effectively. ## Table of Contents 1. [The Purpose of Animation](#the-purpose-of-animation) 2. [Timing & Duration](#timing-duration) 3. [Easing Functions](#easing-functions) 4. [Common Animation Patterns](#common-animation-patterns) 5. [Loading States](#loading-states) 6. [Microinteractions](#microinteractions) 7. [Accessibility Considerations](#accessibility-considerations) 8. [Performance Guidelines](#performance-guidelines) 9. [Animation Checklist](#animation-checklist) --- ## The Purpose of Animation Animation should serve a function, not just look nice. ### Valid Reasons to Animate | Purpose | Example | |---------|---------| | **Feedback** | Button press confirmation, form submission success | | **Orientation** | Showing where something came from or went to | | **Focus** | Drawing attention to important changes | | **Teaching** | Demonstrating how something works | | **Continuity** | Maintaining context during state changes | | **Delight** | Occasional surprise (use sparingly) | ### Invalid Reasons to Animate - "It looks cool" - "The competition does it" - "To show off our skills" - Every state change - To hide slow performance --- ## Timing & Duration ### Duration Guidelines | Animation Type | Duration | Rationale | |----------------|----------|-----------| | Micro-feedback (hover, press) | 100-150ms | Must feel instant | | Simple transitions (fade, slide) | 150-250ms | Noticeable but quick | | Complex transitions (modal, navigation) | 250-350ms | Need time to follow | | Entrances/Reveals | 200-400ms | Can be slightly longer | | Decorative/Emphasis | 300-500ms | Purpose is to be noticed | ### The 200ms Rule Most UI animations should be around 200ms: - Faster than 100ms → Too fast to perceive - Slower than 400ms → Feels sluggish, interrupts flow **Exception:** Loading and progress indicators can be slower because they represent real waiting. ### Duration by Distance Longer travel distance = longer duration (but not proportionally). ``` Small movement (8-16px): 100-150ms Medium movement (50-100px): 150-250ms Large movement (full screen): 250-350ms ``` --- ## Easing Functions Easing makes motion feel natural. Linear motion looks robotic. ### Common Easing Curves | Easing | Use For | Feel | |--------|---------|------| | **ease-out** | Elements entering | Fast start, gentle stop | | **ease-in** | Elements leaving | Gentle start, fast exit | | **ease-in-out** | Elements moving within view | Smooth throughout | | **linear** | Progress indicators, opacity changes | Mechanical (intentional) | ### When to Use Each **Ease-out (default for entrances):** ```css transition: transform 200ms ease-out; ``` - Modals appearing - Notifications sliding in - Dropdowns opening - Tooltips appearing **Ease-in (for exits):** ```css transition: opacity 150ms ease-in; ``` - Modals dismissing - Elements fading out - Notifications leaving **Ease-in-out (for on-screen movement):** ```css transition: transform 250ms ease-in-out; ``` - Tab indicators sliding - Carousel transitions - Drawer/sidebar toggling ### Custom Cubic Bezier For more personality, customize curves: ```css /* Snappy entrance */ transition: transform 200ms cubic-bezier(0.34, 1.56, 0.64, 1); /* Smooth overshoot */ transition: transform 300ms cubic-bezier(0.175, 0.885, 0.32, 1.275); ``` --- ## Common Animation Patterns ### Button States **Hover:** ```css .btn { transition: background-color 100ms ease-out, transform 100ms ease-out; } .btn:hover { background-color: var(--btn-hover); } ``` **Active/Pressed:** ```css .btn:active { transform: scale(0.97); } ``` **Loading state:** ```css .btn.loading { opacity: 0.7; pointer-events: none; } .btn.loading .spinner { animation: spin 1s linear infinite; } ``` ### Modal Entrance/Exit **Enter:** ```css .modal { opacity: 0; transform: scale(0.95) translateY(-10px); transition: opacity 200ms ease-out, transform 200ms ease-out; } .modal.open { opacity: 1; transform: scale(1) translateY(0); } ``` **Exit:** ```css .modal.closing { opacity: 0; transform: scale(0.95); transition: opacity 150ms ease-in, transform 150ms ease-in; } ``` ### Dropdown/Menu ```css .dropdown { opacity: 0; transform: translateY(-8px); pointer-events: none; transition: opacity 150ms ease-out, transform 150ms ease-out; } .dropdown.open { opacity: 1; transform: translateY(0); pointer-events: auto; } ``` ### Toast/Notification ```css .toast { transform: translateX(100%); transition: transform 300ms ease-out; } .toast.visible { transform: translateX(0); } .toast.exiting { transform: translateX(100%); transition: transform 200ms ease-in; } ``` ### Skeleton Loading ```css .skeleton { background: linear-gradient( 90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75% ); background-size: 200% 100%; animation: shimmer 1.5s infinite; } @keyframes shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } } ``` --- ## Loading States ### Types of Loading Indicators | Type | Use When | Example | |------|----------|---------| | **Spinner** | Unknown duration, short wait expected | Button submission | | **Progress bar** | Known progress, longer operations | File upload | | **Skeleton** | Loading content layout | Feed items | | **Pulse/Shimmer** | Refreshing existing content | Pull to refresh | ### Spinner Guidelines - Don't show immediately (wait 300-500ms) - If wait < 1 second, spinner may not be needed - Position in context (where content will appear) - Provide cancel option for long operations ```jsx // Delay spinner to avoid flash for fast operations const [showSpinner, setShowSpinner] = useState(false); useEffect(() => { if (isLoading) { const timer = setTimeout(() => setShowSpinner(true), 400); return () => clearTimeout(timer); } setShowSpinner(false); }, [isLoading]); ``` ### Progress Bar Guidelines - Show percentage when meaningful - Don't let it jump backwards - Consider indeterminate state if progress unknown - Complete to 100% before hiding ### Skeleton Screen Guidelines - Match layout of actual content - Use consistent bone shapes - Animate subtly (shimmer, not bounce) - Replace with content immediately when loaded --- ## Microinteractions Small animations that provide feedback and delight. ### Effective Microinteractions **Toggle switches:** ```css .toggle-thumb { transition: transform 150ms ease-out; } .toggle.on .toggle-thumb { transform: translateX(20px); } ``` **Checkbox check:** ```css .checkmark { stroke-dasharray: 20; stroke-dashoffset: 20; transition: stroke-dashoffset 200ms ease-out; } .checkbox.checked .checkmark { stroke-dashoffset: 0; } ``` **Like/Heart animation:** ```css .heart { transform: scale(1); transition: transform 150ms ease-out; } .heart.liked { animation: pop 300ms ease-out; } @keyframes pop { 0% { transform: scale(1); } 50% { transform: scale(1.2); } 100% { transform: scale(1); } } ``` **Input focus:** ```css .input { border-color: #ccc; transition: border-color 150ms ease-out, box-shadow 150ms ease-out; } .input:focus { border-color: #3b82f6; box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.2); } ``` ### When Microinteractions Help - Confirming user action occurred - Showing state change clearly - Making interface feel responsive - Guiding attention to changes ### When to Skip - Repetitive actions (every keystroke) - Performance-critical paths - Accessibility mode (respect reduce-motion) --- ## Accessibility Considerations ### Respect User Preferences ```css @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; } } ``` ### Provide Alternatives - Don't rely on animation alone to convey information - Ensure state changes are visible without animation - Allow users to disable animations in app settings ### Avoid Problematic Animations | Avoid | Reason | |-------|--------| | Flashing/strobing | Can trigger seizures | | Parallax scrolling | Causes motion sickness | | Auto-playing video | Distracting, accessibility | | Infinite loops | Drains attention, battery | --- ## Performance Guidelines ### GPU-Accelerated Properties Animate these for smooth 60fps: - `transform` (translate, scale, rotate) - `opacity` Avoid animating (causes reflow/repaint): - `width`, `height` - `top`, `left`, `right`, `bottom` - `margin`, `padding` - `border-width` - `font-size` ### Use will-change Sparingly ```css /* Only for elements about to animate */ .modal { will-change: transform, opacity; } /* Remove after animation */ .modal.static { will-change: auto; } ``` ### Batch Animations Start animations together, not staggered excessively: - 0-50ms stagger: feels cohesive - 100ms+ stagger: feels slow, sequential ### Test on Low-end Devices What's smooth on your MacBook may stutter on a budget Android phone. Test on real devices or throttle CPU in DevTools. --- ## Animation Checklist Before shipping an animation: - [ ] Does it serve a purpose (not just decoration)? - [ ] Is duration appropriate for the action? - [ ] Does easing feel natural? - [ ] Does it work with `prefers-reduced-motion`? - [ ] Is it GPU-accelerated (transform/opacity)? - [ ] Does it perform well on low-end devices? - [ ] Can the interface function without it? -
data-visualization.md 11.5 KB
# Data Visualization Design Practical guide to designing charts, tables, and data displays that communicate clearly. ## Table of Contents 1. [Chart Selection](#chart-selection) 2. [Bar Charts](#bar-charts) 3. [Line Charts](#line-charts) 4. [Pie Charts](#pie-charts) 5. [Color in Data Visualization](#color-in-data-visualization) 6. [Tables](#tables) 7. [Dashboard Layout](#dashboard-layout) 8. [Chart Annotations](#chart-annotations) 9. [Accessibility in Data Viz](#accessibility-in-data-viz) 10. [Common Mistakes](#common-mistakes) 11. [Quick Reference](#quick-reference) --- ## Chart Selection ### Choosing the Right Chart Type | Data Type | Best Chart | Avoid | |-----------|-----------|-------| | Part-to-whole | Pie (≤5 slices), stacked bar | Pie with many slices | | Change over time | Line chart, area chart | Pie chart | | Comparison (few items) | Bar chart (horizontal or vertical) | Line chart | | Comparison (many items) | Horizontal bar chart | Vertical bars (labels hard) | | Distribution | Histogram, box plot | Pie chart | | Correlation | Scatter plot | Line chart | | Composition over time | Stacked area, stacked bar | Multiple pie charts | ### When NOT to Use a Chart Sometimes a simple number is better: - Single data point → Big number display - Two numbers to compare → Side-by-side with % change - Very few data points → Table might be clearer ``` ❌ Pie chart with 2 slices (73% vs 27%) ✅ "73% of users completed onboarding" ``` --- ## Bar Charts ### Orientation **Vertical bars:** Best when comparing few categories with short labels **Horizontal bars:** Best when comparing many categories or labels are long ### Bar Chart Rules 1. **Always start at zero** - Truncated axes mislead 2. **Order meaningfully** - By value (descending) or logical order (time, alphabet) 3. **Space bars correctly** - Gap between bars = 50-100% of bar width 4. **Label directly** - Put values on bars, not in legend 5. **Limit categories** - 5-7 bars maximum; group others as "Other" ### Bar Styling ```css /* Bar appearance */ .bar { fill: var(--primary); border-radius: 2px 2px 0 0; /* Slight rounding on top only */ } /* Hover state */ .bar:hover { fill: var(--primary-dark); } /* Spacing */ .bar-gap: 8px; .bar-width: 32px; ``` --- ## Line Charts ### When to Use - Continuous data over time - Trends matter more than individual values - Comparing multiple series over same time period ### Line Chart Rules 1. **Start Y-axis at zero** (usually) - Unless all values are in narrow range 2. **Limit to 4-5 lines** - More becomes unreadable 3. **Use distinct line styles** - Different colors, consider dashes for printing 4. **Label lines directly** - Not just in legend 5. **Highlight important points** - Mark significant events or thresholds ### Line Styling ```css /* Line appearance */ .line { stroke-width: 2px; fill: none; } /* Data points */ .data-point { r: 4px; fill: white; stroke-width: 2px; } /* Only show points on hover or for few data points */ .data-point { opacity: 0; } .line-group:hover .data-point { opacity: 1; } ``` --- ## Pie Charts ### When to Use (Rarely) - Showing parts of a whole - 2-5 slices maximum - Exact values less important than proportions - Users understand percentages must sum to 100% ### When NOT to Use - Comparing values (use bar chart) - More than 5 categories - Values that don't sum to 100% - Showing change over time ### Pie Chart Rules 1. **Limit slices** - Maximum 5; combine small values into "Other" 2. **Order by size** - Largest slice starting at 12 o'clock 3. **Label directly** - Put labels on or near slices, not in legend 4. **Show percentages** - Values help interpretation 5. **Consider donut** - Center can show total or key metric ### Alternative: Simple Numbers Often clearer than a pie chart: ``` ┌─────────────────────────────────┐ │ 67% Mobile 23% Desktop 10% Tablet │ └─────────────────────────────────┘ ``` --- ## Color in Data Visualization ### Categorical Colors For different categories, use distinct hues: ``` Blue: #3b82f6 Orange: #f97316 Green: #22c55e Purple: #8b5cf6 Yellow: #eab308 ``` **Rules:** - Maximum 5-7 distinct colors - Avoid red/green together (colorblindness) - Test with colorblind simulator ### Sequential Colors For continuous data (low to high), use single hue with varying lightness: ``` Light: #dbeafe (low values) #93c5fd #60a5fa #3b82f6 Dark: #1d4ed8 (high values) ``` ### Diverging Colors For data with meaningful center (positive/negative, above/below average): ``` Negative: #ef4444 (red) Neutral: #f3f4f6 (gray) Positive: #22c55e (green) ``` ### Colorblind-Safe Palettes Test your palette with: - Coblis Color Blindness Simulator - Chrome DevTools rendering settings **Safe combinations:** - Blue + Orange (deuteranopia safe) - Blue + Yellow - Purple + Green + Orange --- ## Tables ### When Tables Beat Charts - Precise values matter - Users need to look up specific data - Multiple attributes per item - Data will be exported/copied ### Table Design Principles **1. Align numbers right** ``` Revenue ──────── $1,234 $12,456 $123,789 ``` **2. Align text left** ``` Product Name ──────────── Widget A Premium Widget Widget Pro Max ``` **3. Use consistent precision** ``` ❌ $1234, $567.89, $2.5k ✅ $1,234.00, $567.89, $2,500.00 ``` **4. Minimize borders** ```css /* Light horizontal lines only */ tr { border-bottom: 1px solid #e5e7eb; } /* No vertical borders, no heavy lines */ ``` **5. Generous padding** ```css td { padding: 12px 16px; } ``` ### Table Patterns **Zebra striping:** ```css tr:nth-child(even) { background-color: #f9fafb; } ``` **Hover highlighting:** ```css tr:hover { background-color: #f3f4f6; } ``` **Sortable columns:** ```html <th> Revenue <button aria-label="Sort by revenue">▼</button> </th> ``` **Sticky headers:** ```css thead th { position: sticky; top: 0; background: white; } ``` --- ## Dashboard Layout ### Visual Hierarchy ``` ┌──────────────────────────────────────────────────────┐ │ KEY METRICS (big numbers) │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │ $45.2K │ │ 1,234 │ │ 78.3% │ │ 4.2 │ │ │ │ Revenue │ │ Users │ │ Conv. │ │ Rating │ │ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ ├──────────────────────────────────────────────────────┤ │ PRIMARY CHART (most important trend) │ │ ┌──────────────────────────────────────────────┐ │ │ │ │ │ │ │ [Line chart: Revenue over time] │ │ │ │ │ │ │ └──────────────────────────────────────────────┘ │ ├──────────────────────────┬───────────────────────────┤ │ SUPPORTING CHARTS │ DETAIL TABLE │ │ ┌──────────────────┐ │ ┌───────────────────────┐ │ │ │ [Bar chart] │ │ │ Recent Transactions │ │ │ └──────────────────┘ │ │ ... │ │ │ ┌──────────────────┐ │ │ ... │ │ │ │ [Donut chart] │ │ └───────────────────────┘ │ │ └──────────────────┘ │ │ └──────────────────────────┴───────────────────────────┘ ``` ### Key Metric Cards ```html <div class="metric-card"> <span class="label">Revenue</span> <span class="value">$45,234</span> <span class="change positive">↑ 12.3%</span> </div> ``` ```css .metric-card { padding: 20px; background: white; border-radius: 8px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); } .value { font-size: 2rem; font-weight: 600; } .change.positive { color: #22c55e; } .change.negative { color: #ef4444; } ``` --- ## Chart Annotations ### When to Annotate - Explain anomalies - Mark significant events - Highlight thresholds - Call out key insights ### Annotation Patterns **Event markers:** ``` ╭─── Product launch Revenue ──────────/│\────────────── │ ``` **Threshold lines:** ``` Revenue ────────────────────── - - - - - Target: $50k - - - - ────────────────────────────── ``` **Callout boxes:** ``` ┌──────────────────────────┐ │ 📈 Best month ever! │ │ +47% vs. last year │ └─────────────┬────────────┘ │ ▼ ``` --- ## Accessibility in Data Viz ### Color Considerations - Don't rely on color alone - Add patterns, labels, or icons - Ensure sufficient contrast - Test with colorblind simulators ### Screen Reader Support ```html <figure role="img" aria-label="Bar chart showing monthly revenue"> <figcaption class="sr-only"> Revenue by month: January $10,000, February $12,000... </figcaption> <svg>...</svg> </figure> ``` ### Data Tables as Alternative For complex charts, provide data table version: ```html <details> <summary>View data as table</summary> <table>...</table> </details> ``` --- ## Common Mistakes | Mistake | Problem | Fix | |---------|---------|-----| | 3D charts | Distorts perception | Use flat 2D | | Dual Y-axes | Confusing comparisons | Separate charts | | Too many colors | Visual noise | Limit to 5-7 | | Missing zero | Misleading differences | Start at zero | | No labels | Hard to interpret | Label directly | | Decorative elements | Chartjunk distracts | Remove non-data ink | | Inconsistent scales | Misleading comparison | Match scales | --- ## Quick Reference ### Chart Decision Tree ``` What do you want to show? │ ├─ Comparison → Bar chart │ ├─ Change over time → Line chart │ ├─ Part of whole → Pie (≤5) or stacked bar │ ├─ Distribution → Histogram │ ├─ Relationship → Scatter plot │ └─ Single value → Big number ``` ### Minimum Viable Chart Styling ```css /* Axes */ .axis line, .axis path { stroke: #e5e7eb; } .axis text { fill: #6b7280; font-size: 12px; } /* Grid */ .grid line { stroke: #f3f4f6; } /* Data */ .bar, .line, .point { fill: #3b82f6; stroke: #3b82f6; } /* Labels */ .label { fill: #374151; font-size: 11px; } ``` -
theming-dark-mode.md 9.9 KB
# Theming & Dark Mode Design Creating effective color themes and implementing dark mode correctly. ## Table of Contents 1. [Dark Mode Philosophy](#dark-mode-philosophy) 2. [Dark Mode Color Principles](#dark-mode-color-principles) 3. [Building a Dark Mode Palette](#building-a-dark-mode-palette) 4. [Implementation Strategies](#implementation-strategies) 5. [Component Considerations](#component-considerations) 6. [Testing Dark Mode](#testing-dark-mode) 7. [Theme Toggle UI](#theme-toggle-ui) 8. [Advanced: Multiple Themes](#advanced-multiple-themes) 9. [Common Mistakes](#common-mistakes) 10. [Quick Reference](#quick-reference) --- ## Dark Mode Philosophy Dark mode isn't just inverting colors—it requires deliberate design decisions to maintain usability, hierarchy, and aesthetics. ### Why Dark Mode Matters - **User preference:** Many users prefer it - **Eye strain:** Reduces strain in low-light environments - **Battery life:** Saves power on OLED screens - **Accessibility:** Some users have photosensitivity - **Professional expectation:** Users expect modern apps to support it --- ## Dark Mode Color Principles ### Don't Just Invert | Light Mode | Bad Dark Mode | Good Dark Mode | |------------|---------------|----------------| | White `#ffffff` | Black `#000000` | Dark gray `#18181b` | | Black text `#000000` | White text `#ffffff` | Off-white `#fafafa` | | Gray `#6b7280` | Gray `#6b7280` | Lighter gray `#a1a1aa` | ### Key Principles **1. Use dark grays, not pure black** Pure black (`#000000`) creates harsh contrast and "halation" (text appears to glow). ```css /* Background scale for dark mode */ --bg-base: #09090b; /* Deepest background */ --bg-subtle: #18181b; /* Cards, elevated surfaces */ --bg-muted: #27272a; /* Hover states, inputs */ --bg-emphasis: #3f3f46; /* Active states */ ``` **2. Reduce contrast slightly** Max contrast in dark mode is harsher than in light mode. ```css /* Text colors for dark mode */ --text-primary: #fafafa; /* ~95% white, not 100% */ --text-secondary: #a1a1aa; /* Muted text */ --text-tertiary: #71717a; /* Subtle text */ ``` **3. Desaturate colors** Bright saturated colors on dark backgrounds cause eye strain. ```css /* Light mode brand color */ --primary-light: #3b82f6; /* Bright blue */ /* Dark mode - slightly desaturated */ --primary-dark: #60a5fa; /* Lighter, less saturated */ ``` **4. Elevate with lightness, not shadow** In dark mode, shadows are invisible. Show elevation with lighter surfaces. ```css /* Light mode: shadow for depth */ .card-light { background: white; box-shadow: 0 4px 6px rgba(0,0,0,0.1); } /* Dark mode: lighter surface for depth */ .card-dark { background: #27272a; /* Lighter than base */ box-shadow: none; /* Or very subtle */ } ``` --- ## Building a Dark Mode Palette ### Step 1: Define Your Gray Scale Create 9-10 shades from near-black to near-white: ```css /* Dark mode gray scale (Zinc example) */ --gray-950: #09090b; /* Deepest background */ --gray-900: #18181b; /* Card backgrounds */ --gray-800: #27272a; /* Elevated surfaces */ --gray-700: #3f3f46; /* Borders, dividers */ --gray-600: #52525b; /* Disabled states */ --gray-500: #71717a; /* Placeholder text */ --gray-400: #a1a1aa; /* Secondary text */ --gray-300: #d4d4d8; /* Primary text (alt) */ --gray-200: #e4e4e7; /* Headings */ --gray-100: #f4f4f5; /* Emphasis text */ --gray-50: #fafafa; /* Primary text */ ``` ### Step 2: Adjust Accent Colors ```css /* Primary color adjustments */ /* Light mode: use 500-600 range */ --primary-light: #2563eb; /* Dark mode: use 400-500 range (lighter) */ --primary-dark: #3b82f6; /* Same for semantic colors */ --success-light: #16a34a; --success-dark: #22c55e; --error-light: #dc2626; --error-dark: #ef4444; ``` ### Step 3: Define Semantic Tokens ```css /* Semantic tokens that switch based on mode */ :root { --color-bg: var(--gray-50); --color-bg-subtle: var(--gray-100); --color-text: var(--gray-900); --color-text-muted: var(--gray-600); --color-border: var(--gray-200); --color-primary: var(--blue-600); } [data-theme="dark"] { --color-bg: var(--gray-950); --color-bg-subtle: var(--gray-900); --color-text: var(--gray-50); --color-text-muted: var(--gray-400); --color-border: var(--gray-800); --color-primary: var(--blue-400); } ``` --- ## Implementation Strategies ### Strategy 1: CSS Custom Properties ```css :root { --bg: #ffffff; --text: #18181b; } @media (prefers-color-scheme: dark) { :root { --bg: #18181b; --text: #fafafa; } } body { background: var(--bg); color: var(--text); } ``` ### Strategy 2: Data Attribute + Class ```html <html data-theme="dark"> ``` ```css [data-theme="light"] { --bg: #ffffff; } [data-theme="dark"] { --bg: #18181b; } ``` ```javascript // Toggle theme function toggleTheme() { const current = document.documentElement.dataset.theme; document.documentElement.dataset.theme = current === 'dark' ? 'light' : 'dark'; localStorage.setItem('theme', document.documentElement.dataset.theme); } // Initialize from preference function initTheme() { const saved = localStorage.getItem('theme'); const preferred = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; document.documentElement.dataset.theme = saved || preferred; } ``` ### Strategy 3: Tailwind Dark Mode ```html <!-- With class strategy --> <html class="dark"> <body class="bg-white dark:bg-zinc-950 text-zinc-900 dark:text-zinc-50"> ``` ```javascript // tailwind.config.js module.exports = { darkMode: 'class', // or 'media' for system preference only } ``` --- ## Component Considerations ### Cards and Surfaces ```css /* Light: white with shadow */ .card { background: white; box-shadow: 0 1px 3px rgba(0,0,0,0.1); } /* Dark: lighter surface, subtle or no shadow */ [data-theme="dark"] .card { background: var(--gray-900); box-shadow: 0 1px 3px rgba(0,0,0,0.3); /* Darker shadow if any */ /* Or: border: 1px solid var(--gray-800); */ } ``` ### Form Inputs ```css .input { background: white; border: 1px solid var(--gray-300); } [data-theme="dark"] .input { background: var(--gray-900); border: 1px solid var(--gray-700); } ``` ### Buttons ```css /* Primary button */ .btn-primary { background: var(--primary); color: white; } [data-theme="dark"] .btn-primary { /* Often same or slightly adjusted */ background: var(--primary-dark); } /* Secondary button */ .btn-secondary { background: var(--gray-100); color: var(--gray-900); } [data-theme="dark"] .btn-secondary { background: var(--gray-800); color: var(--gray-100); } ``` ### Images and Media ```css /* Reduce brightness/contrast of images in dark mode */ [data-theme="dark"] img:not([data-no-dim]) { filter: brightness(0.9) contrast(1.1); } /* Invert diagrams/illustrations if needed */ [data-theme="dark"] .diagram { filter: invert(1) hue-rotate(180deg); } ``` ### Syntax Highlighting Don't forget code blocks need dark mode variants: - Use dark theme variants of syntax highlighters - Or invert colors appropriately - Popular: One Dark, Dracula, Night Owl --- ## Testing Dark Mode ### Checklist - [ ] All text is readable (sufficient contrast) - [ ] Hierarchy still clear (headings vs body) - [ ] Focus states visible - [ ] Images don't blow out - [ ] Forms inputs clearly visible - [ ] Error/success states distinct - [ ] Loading states visible - [ ] Shadows/elevation still work - [ ] Icons visible (may need color swap) - [ ] Brand colors still recognizable ### Contrast Ratios Same WCAG requirements apply: - Normal text: 4.5:1 minimum - Large text: 3:1 minimum - UI components: 3:1 minimum **Common dark mode fails:** - Gray text on dark background - Colored text on colored backgrounds - Disabled states too subtle --- ## Theme Toggle UI ### Placement - Header/navigation (most common) - Settings page - Footer (less common) ### Icon Patterns ```html <!-- Sun/Moon toggle --> <button aria-label="Toggle dark mode"> <svg class="sun hidden dark:block">...</svg> <svg class="moon block dark:hidden">...</svg> </button> ``` ### State Options 1. **Light / Dark** - Simple toggle 2. **Light / Dark / System** - Respect OS preference option 3. **Auto only** - Always follow system (no toggle) ### Persistence ```javascript // Save preference localStorage.setItem('theme', 'dark'); // Load preference (with system fallback) const theme = localStorage.getItem('theme') || (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'); ``` --- ## Advanced: Multiple Themes ### Brand Themes ```css [data-theme="brand-a"] { --primary: #ff6b6b; --primary-hover: #ee5a5a; } [data-theme="brand-b"] { --primary: #4ecdc4; --primary-hover: #3dbdb5; } ``` ### Theme Structure ```css /* Base tokens (don't change) */ --spacing-4: 16px; --radius-md: 8px; /* Color tokens (change per theme) */ --color-primary: ...; --color-bg: ...; /* Component tokens (reference color tokens) */ --button-bg: var(--color-primary); --card-bg: var(--color-bg); ``` --- ## Common Mistakes | Mistake | Problem | Fix | |---------|---------|-----| | Pure black background | Harsh, looks flat | Use dark gray (#18181b) | | Pure white text | Too much contrast | Use off-white (#fafafa) | | Same saturated colors | Eye strain | Desaturate for dark mode | | Shadows for elevation | Invisible in dark | Use lighter surfaces | | Forgetting images | Can be too bright | Dim images slightly | | One contrast check | Colors interact differently | Check all combinations | | Forgetting focus states | Invisible borders | Ensure visible focus rings | --- ## Quick Reference ### Minimum Viable Dark Mode ```css :root { --bg: #ffffff; --bg-subtle: #f4f4f5; --text: #18181b; --text-muted: #71717a; --border: #e4e4e7; --primary: #2563eb; } @media (prefers-color-scheme: dark) { :root { --bg: #18181b; --bg-subtle: #27272a; --text: #fafafa; --text-muted: #a1a1aa; --border: #3f3f46; --primary: #3b82f6; } } body { background: var(--bg); color: var(--text); } ```
-
-
SKILL.md 15.3 KB
--- name: refactoring-ui description: 'Audit and fix visual hierarchy, spacing, color, and depth in web UIs. Use when the user mentions "my UI looks off" (or amateur/unprofessional), "fix the design", "Tailwind styling", "color palette", "visual hierarchy", "design system", "spacing scale", or "component styling". Also trigger when building consistent design tokens, creating dark mode themes, improving data-visualization clarity, or polishing UI details before launch. Covers grayscale-first workflow, constrained design scales, shadows, and component styling. For typeface selection, see web-typography. For usability audits, see ux-heuristics.' license: MIT metadata: author: wondelai version: "1.5.1" --- # Refactoring UI Design System A practical, opinionated approach to UI design. Apply these principles when generating frontend code, reviewing designs, or advising on visual improvements. ## Core Principle **Design in grayscale first. Add color last.** This forces proper hierarchy through spacing, contrast, and typography before relying on color as a crutch. **The foundation:** Great UI isn't about talent — it's about systems. Constrained scales for spacing, type, color, and shadows produce consistently professional results. Start with too much white space and remove; leave details (icons, shadows, micro-interactions) until layout and hierarchy work. ## Scoring **Goal: 10/10.** Score by counting satisfied rows in the [Quick Diagnostic](#quick-diagnostic) (8 yes/no checks): `score = round(satisfied / 8 × 10)`. Bands follow directly: **10** = all 8 pass (hierarchy reads blurred and in grayscale, every value on a scale); **9** = exactly 1 gap (usually weak hierarchy or thin white space); **6-8** = 2-3 gaps; **<=5** = 4+ gaps (arbitrary spacing, color doing the work hierarchy should, or failing contrast). Always state the current score and the specific diagnostic rows to fix to reach 10/10. ## The Refactoring UI Framework Seven principles for building professional interfaces without a designer: ### 1. Visual Hierarchy **Core concept:** Not everything can be important. Create hierarchy through three levers: size, weight, and color. **Why it works:** When every element competes for attention, nothing stands out; deliberately de-emphasizing secondary content makes primary content powerful by contrast. **Key insights:** - Combine levers, don't multiply — primary text = large OR bold OR dark, not all three; save "all three" for the single most important element - Labels are secondary — form labels, table headers, and metadata support the data, not compete with it; make them smaller, lighter, or uppercase-small - Semantic color ≠ visual weight — a muted secondary button often beats screaming red for routine destructive actions **Product applications:** | Context | Hierarchy Technique | Example | |---------|---------------------|---------| | **Form fields** | De-emphasize labels, emphasize values | Small uppercase label above large value | | **Dashboards** | Key metric large, context small | "$42,300" large, "vs last month" small | | **Tables** | De-emphasize headers, emphasize data | Headers uppercase small gray, data normal | **Design patterns:** - Three-level hierarchy: Size (large/base/small), Weight (bold/medium/normal), Color (dark/medium/light gray) - Button hierarchy: primary (filled), secondary (outlined or muted), tertiary (text only) **Ethical boundary:** Don't use hierarchy tricks to hide important information like pricing, terms, or cancellation options. See [references/advanced-patterns.md](references/advanced-patterns.md) when designing components beyond static layout — interaction/hover/focus states, form design, empty states, border-radius systems, text truncation, and responsive breakpoints. ### 2. Spacing & Sizing **Core concept:** Use a constrained spacing scale, not arbitrary values. Spacing defines relationships — closer elements read as more related. **Why it works:** Arbitrary spacing (padding: 13px) creates inconsistency; a fixed scale forces deliberate decisions and harmonious layouts. Generous spacing feels premium; dense feels overwhelming. **Key insights:** - Use the scale: 4, 8, 16, 24, 32, 48, 64px - Start with too much white space, then remove — you'll almost never remove enough - Spacing between groups must exceed spacing within groups - Constrain widths: text to 45-75 characters (`max-w-prose`), forms to 300-500px; full-width is almost never right **Product applications:** | Context | Spacing Strategy | Example | |---------|-----------------|---------| | **Icon + label** | Tight coupling (4px) | Small gap keeps them connected | | **Card sections** | Section separation (24px) | Title, content, footer blocks | | **Page sections** | Major sections (48-64px) | Hero, features, testimonials | **CSS patterns:** - `p-1`(4px) `p-2`(8px) `p-4`(16px) `p-6`(24px) `p-8`(32px) `p-12`(48px) `p-16`(64px) - `max-w-prose`(65ch) `max-w-md`(28rem) `max-w-lg`(32rem) `max-w-xl`(36rem) - `gap-2` for related items, `gap-6` for section separation ### 3. Typography **Core concept:** Use a modular type scale, constrain line heights by context, and limit to two font families maximum. **Why it works:** A modular scale (steps growing ~1.2× each) creates natural visual rhythm; tight line heights on headings and relaxed on body text improve readability in each context. **Key insights:** - Scale: 12, 14, 16, 18, 20, 24, 30, 36px (~1.2 modular, hand-tuned) - Headings: tight line height (1.0-1.25); body: relaxed (1.5-1.75); wider text needs more line height - Avoid weights below 400 for body text; use bold (600-700) for emphasis, not everything - Two fonts max: one for headings, one for body (or one family with weight variation) **Product applications:** | Context | Typography Rule | Example | |---------|----------------|---------| | **Hero headline** | 36px, line-height 1.1, bold | Large impactful statement | | **Body text** | 16px, line-height 1.75, normal | Comfortable reading | | **Captions/labels** | 12-14px, line-height 1.5, medium gray | Secondary information | **CSS patterns:** - `text-xs`(12px) `text-sm`(14px) `text-base`(16px) `text-lg`(18px) `text-xl`(20px) - `font-normal`(400) `font-medium`(500) `font-semibold`(600) `font-bold`(700) - `leading-tight`(1.25) `leading-normal`(1.5) `leading-relaxed`(1.75) ### 4. Color **Core concept:** Build a systematic palette with 5-9 shades per color, add subtle saturation to grays, and design in grayscale first. **Why it works:** Random colors clash; a predefined shade system ensures consistency, and HSL adjustments create natural-feeling lighter and darker variants. **Key insights:** - Each color needs 5-9 shades from near-white to near-black (50-900); darkest is not pure black — use `#111827`, not `#000000` - Pure grays look lifeless — tint them (cool UI: blue like `#64748b`; warm UI: yellow/brown like `#78716c`) - HSL: lighter = raise lightness, lower saturation, hue toward 60°; darker = the reverse, hue toward 0°/240° - Contrast minimums: 4.5:1 body text, 3:1 large text (18px+); use `#374151` (gray-700) on white, not lighter grays **Product applications:** | Context | Color Strategy | Example | |---------|---------------|---------| | **Primary palette** | 9 shades (50-900) of brand color | Blue-500 buttons, Blue-100 backgrounds | | **Semantic colors** | Success/warning/error with shade ranges | Green-500 success, Red-500 errors | | **Text colors** | Three levels: dark, medium, light | `text-gray-900`, `text-gray-600`, `text-gray-400` | **CSS patterns:** - `text-gray-900`(dark) `text-gray-600`(medium) `text-gray-400`(light) - `bg-blue-50` for subtle backgrounds, `bg-blue-500` for primary actions - `border-gray-200` for subtle borders, `border-gray-300` for stronger See [references/theming-dark-mode.md](references/theming-dark-mode.md) when building a dark theme — hex shade scales, why darkest is `#111827` not black (halation), and conveying elevation via lightness instead of shadow. See [references/accessibility-depth.md](references/accessibility-depth.md) when contrast, focus rings, keyboard nav, or screen-reader support is in scope — full WCAG 2.1 AA checklist and fixes. ### 5. Depth & Shadows **Core concept:** Use a shadow scale to convey elevation — small shadows for slightly raised elements, large shadows for floating ones. **Why it works:** The eye reads shadow size as height above the page; a consistent scale makes elevation legible, so users intuit what's interactive, floating, or background. **Key insights:** - Small shadows = raised slightly (buttons, cards); large = floating (modals, dropdowns) - Good shadows have two parts: a tight dark shadow for crispness plus a larger soft one for atmosphere - Depth without shadows: lighter top border + darker bottom border, subtle gradients, overlapping elements - Don't overuse — if everything floats, nothing has depth; shadow color is transparent dark, never opaque gray **Product applications:** | Context | Shadow Level | Example | |---------|-------------|---------| | **Buttons** | `shadow-sm` (subtle raise) | Slightly elevated above surface | | **Dropdowns** | `shadow-lg` (floating) | Menu clearly above content | | **Modals** | `shadow-xl` (highest) | Overlay detached from page | **CSS patterns:** - `shadow-sm`: `0 1px 2px rgba(0,0,0,0.05)` - `shadow-md`: `0 4px 6px rgba(0,0,0,0.1)` - `shadow-lg`: `0 10px 15px rgba(0,0,0,0.1)` - `shadow-xl`: `0 20px 25px rgba(0,0,0,0.15)` See [references/animation-microinteractions.md](references/animation-microinteractions.md) when adding motion to interactive elements — durations, easing curves, loading states, and the `prefers-reduced-motion` rule. ### 6. Images & Icons **Core concept:** Treat images as design elements, not afterthoughts. Size icons deliberately and use overlays to keep text readable on images. **Why it works:** Poorly sized icons look awkward and unstyled images break consistency; deliberate treatment (overlays, object-fit, radius) makes interfaces feel polished. **Key insights:** - Size icons relative to context; use sets with consistent stroke width and style - Never stretch or distort — use `object-fit: cover` with fixed aspect ratios and crop deliberately - Text over images needs an overlay (semi-transparent gradient) - Empty states are an opportunity — use illustrations plus a clear CTA, not just text **Product applications:** | Context | Image/Icon Technique | Example | |---------|---------------------|---------| | **Hero images** | Semi-transparent gradient overlay | Text readable over any photo | | **Avatars** | Consistent size, rounded, fallback initials | 40px circle, object-fit cover | | **Empty states** | Custom illustration + CTA | Friendly illustration with "Get started" | **CSS patterns:** - `object-fit: cover` with fixed `aspect-ratio` for consistent display - Icon sizing: `w-4 h-4` inline, `w-6 h-6` navigation, `w-8 h-8` feature icons - Overlay: `bg-gradient-to-t from-black/60 to-transparent` for text on images ### 7. Layout & Composition **Core concept:** Don't center everything. Use alignment, overlap, and emphasis variation to create engaging compositions. **Why it works:** A consistent left edge gives the eye a fixed return point per line, so it costs less to scan; centered multi-line text moves that edge every line and slows reading. **Key insights:** - Left-align by default; center only short headlines, heroes, single-action CTAs, and empty states - Cards don't need to contain everything — let images bleed to edges or overlap containers - Vary visual treatment in lists and feeds — feature some items, minimize others - Use alignment to create relationships between unrelated elements **Product applications:** | Context | Layout Strategy | Example | |---------|----------------|---------| | **Hero sections** | Centered text, generous spacing | Short headline + subtext + single CTA | | **Blog feeds** | Varied card sizes for emphasis | First post large, rest in 2-column grid | | **Content pages** | Constrained width, left-aligned | `max-w-prose` container with left text | **CSS patterns:** - `text-left` by default, `text-center` only for heroes and short headlines - `grid grid-cols-3 gap-6` for feature grids; `max-w-4xl mx-auto` for page containers - `overflow-hidden` on cards with `object-fit: cover` images that bleed to edges See [references/data-visualization.md](references/data-visualization.md) when laying out charts, tables, or dashboards — chart-type selection, color use in charts, table density, and dashboard composition. ## Common Mistakes | Mistake | Why It Fails | Fix | |---------|-------------|------| | **"Looks amateur"** | Insufficient white space, unconstrained widths | More white space, constrain content widths | | **"Feels flat"** | No depth differentiation | Subtle shadows, border-bottom on sections | | **"Text is hard to read"** | Poor line-height, too wide, low contrast | Increase line-height, constrain width, boost contrast | | **"Everything looks the same"** | No visual hierarchy | Vary size/weight/color between primary and secondary | | **"Feels cluttered"** | Equal spacing everywhere | Group related items, larger gaps between groups | | **"Colors clash"** | Random choices, no system | Reduce saturation, more grays, limit to palette | | **"Buttons don't pop"** | Low contrast with surroundings | Increase contrast, add shadow | | **Arbitrary values** | px values like 13, 17, 23 breed inconsistency | Stick to the spacing and type scales | ## Quick Diagnostic Audit any UI design: | Question | If No | Action | |----------|-------|--------| | Does hierarchy read when squinting (blur test)? | Elements competing | Increase primary/secondary contrast | | Does it work in grayscale? | Color is a crutch | Strengthen size/weight/spacing hierarchy | | Is there enough white space? | Probably not — most designs are too dense | Increase spacing, especially between groups | | Are labels de-emphasized vs. values? | Labels competing with data | Smaller, lighter, or uppercase-small labels | | Does spacing follow a consistent scale? | Arbitrary spacing = visual noise | Use 4/8/16/24/32/48/64 only | | Is text width constrained? | Long lines fatigue readers | Apply `max-w-prose` (~65ch) | | Do colors have sufficient contrast? | Accessibility failure | WCAG-check; use gray-700+ on white | | Are shadows appropriate for elevation? | Elements float at wrong level | Match shadow scale to element purpose | ## Further Reading For the complete system with visual before/after examples: - [*"Refactoring UI"*](https://refactoringui.com/) by Adam Wathan & Steve Schoger (the full book with hundreds of visual examples) - [*"The Design of Everyday Things"*](https://www.amazon.com/Design-Everyday-Things-Revised-Expanded/dp/0465050654?tag=wondelai00-20) by Don Norman (foundational design thinking and usability) - [*"Don't Make Me Think"*](https://www.amazon.com/Dont-Make-Think-Revisited-Usability/dp/0321965515?tag=wondelai00-20) by Steve Krug (web usability principles that complement Refactoring UI) - [Refactoring UI](https://www.refactoringui.com/) — Official site with resources and examples ## About the Authors **Adam Wathan**, creator of Tailwind CSS, and **Steve Schoger**, the visual designer behind its design language, wrote *Refactoring UI* to teach developers systematic, repeatable design techniques. Their approach replaces artistic talent with constrained systems — fixed scales for spacing, typography, color, and shadows — that produce professional results.
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.