--- name: ui-design-patterns description: Practical UI design patterns and principles for creating polished, professional interfaces. Based on proven techniques from Refactoring UI and Practical UI. version: 1.1.0 typo3_compatibility: "13.0 - 14.x" triggers: - ui - design - layout - spacing - typography - color - hierarchy - styling - visual - accessibility - usability - contrast --- # UI Design Patterns Practical guidelines for creating polished, professional user interfaces without relying on graphic design talent. These patterns work for any web project, including TYPO3 frontend development. ## Acknowledgements These patterns are adapted from two excellent resources: - **Refactoring UI** by Adam Wathan & Steve Schoger — The definitive guide to practical UI design for developers - **Practical UI** (2nd Edition) by Adham Dannaway — Quick and practical UI design guidelines for intuitive, accessible, and beautiful interfaces We highly recommend both works for deepening your UI design knowledge. --- ## 0. Core Principles (from Practical UI) Before diving into specific patterns, internalize these foundational principles: ### Minimize Usability Risks Base design decisions on risk assessment—the risk that someone could have difficulty using an interface: - Light grey text may look sleek but risks readability issues - Icons without labels risk confusion about meaning - Colored heading text risks being mistaken for links **Always consider:** people with poor eyesight, low computer literacy, reduced dexterity, and cognitive differences. ### Have a Logical Reason for Every Design Detail Every UI element should have a rationale. "That looks nice" is not constructive feedback. Be able to articulate *why* each design decision was made. | Element | Logical Reason | |---------|----------------| | Left-aligned text | Creates neat edge, improves readability | | Descriptive headings | Scannable, works with screen readers | | Blue underlined links | Indicates interactivity, accessible for color blind | | Grouped spacing | Related items closer together reduce cognitive load | ### Minimize Interaction Cost Interaction cost = physical + mental effort to complete a task. Reduce it by: 1. **Keep related actions close** (Fitts's Law—closer/larger targets are faster to click) 2. **Reduce distractions** (avoid attention-grabbing elements that pull focus) 3. **Use progressive disclosure** (reveal complexity only when needed) ### Minimize Cognitive Load Cognitive load is the mental effort required to use an interface. Reduce it by: - Breaking information into smaller, digestible chunks - Using familiar patterns people already understand - Removing unnecessary elements and decisions - Grouping related items visually ### Design System First Create a system of reusable guidelines before designing: - Color palette with usage rules - Typography scale - Spacing system - Component patterns - Interaction states This ensures consistency and speeds up decision-making. ### Accessibility is Non-Negotiable Meet **WCAG 2.1 Level AA** at minimum: | Requirement | Minimum Ratio | |-------------|---------------| | Small text (≤18px) | 4.5:1 contrast | | Large text (>18px bold or >24px) | 3:1 contrast | | UI elements (borders, icons) | 3:1 contrast | **Never rely on color alone**—always pair with icons, patterns, or text for color blind users. ### Use Common Design Patterns Per Jakob's Law, stick with patterns people already know: - Conventional form fields (not custom/unfamiliar styles) - Standard navigation patterns - Expected icon meanings - Familiar button behaviors Save creativity for your product's unique value proposition, not basic UI conventions. ### The 80/20 Rule Roughly 80% of users use 20% of features. Prioritize the common paths: - Optimize for frequent tasks, not edge cases - Focus design effort where it has the largest impact - Don't over-engineer rarely-used features --- ## 1. Starting from Scratch ### Start with a Feature, Not a Layout Don't begin by designing the shell (navigation, sidebar, footer). Start with actual functionality. **Wrong approach:** - "Should it have a top nav or sidebar?" - "Where should the logo go?" **Right approach:** - Design the core feature first (search form, product card, user profile) - The navigation will reveal itself as you design features ### Detail Comes Later In early stages, ignore typefaces, shadows, and icons. Use thick markers or low-fidelity wireframes to explore layouts quickly. ### Hold the Color Design in grayscale first. This forces you to use spacing, contrast, and size to create hierarchy. Color comes later as enhancement. ### Work in Cycles 1. Design a simple version of the next feature 2. Build it 3. Iterate on the working design 4. Move to the next feature ### Be a Pessimist Design the smallest useful version first. Don't design features you can't build yet—ship what works. --- ## 2. Hierarchy is Everything ### Not All Elements Are Equal Visual hierarchy makes interfaces feel "designed". When everything competes for attention, nothing stands out. **The key:** Deliberately de-emphasize secondary and tertiary information while highlighting what matters most. ### Size Isn't Everything Don't rely solely on font size for hierarchy. Use: | Technique | Effect | |-----------|--------| | **Font weight** | 600-700 for emphasis, 400-500 for normal | | **Color contrast** | Dark for primary, grey for secondary, light grey for tertiary | | **Spacing** | More space around important elements | **Color guidelines for text:** - Primary content: Dark color (e.g., `slate-900`) - Secondary content: Medium grey (e.g., `slate-600`) - Tertiary content: Light grey (e.g., `slate-400`) ### Don't Use Grey Text on Colored Backgrounds Grey text on colored backgrounds looks washed out. Instead, pick a color with the same hue as the background, adjusting saturation and lightness. ```css /* Bad: Grey on blue background */ background: hsl(220, 80%, 50%); color: #888888; /* Looks dull */ /* Good: Tinted text matching background hue */ background: hsl(220, 80%, 50%); color: hsl(220, 60%, 85%); /* Harmonious and readable */ ``` ### Emphasize by De-emphasizing If a primary element doesn't stand out, don't make it louder—make competing elements quieter. ```css /* Instead of making active nav item bolder... */ /* ...make inactive items softer */ .nav-item { color: var(--slate-400); } .nav-item.active { color: var(--slate-900); } ``` ### Labels Are a Last Resort Context often eliminates the need for labels: | Instead of | Use | |------------|-----| | "Email: john@example.com" | john@example.com (format is obvious) | | "In stock: 12" | "12 left in stock" | | "Bedrooms: 3" | "3 bedrooms" | When labels are necessary, de-emphasize them—the data is what matters. ### Balance Weight and Contrast Heavy elements (icons, bold text) can be de-emphasized with softer colors. Light elements (thin borders) can be emphasized with increased weight. ```css /* Icon feels too heavy? Reduce contrast */ .icon { color: var(--slate-400); } /* Instead of slate-900 */ /* Border too subtle? Increase width */ border: 2px solid hsl(210, 23%, 95%); /* Instead of 1px darker */ ``` ### Button Hierarchy Design buttons based on hierarchy, not just semantics: | Type | Style | Use for | |------|-------|---------| | **Primary** | Solid, high contrast | Main action on page | | **Secondary** | Outline or lower contrast | Less important actions | | **Tertiary** | Link style | Seldom-used actions | **Destructive actions** aren't automatically red and bold. If "Delete" isn't the primary action, style it as secondary or tertiary, then use bold red styling in the confirmation modal. --- ## 3. Layout and Spacing ### Start with Too Much White Space Begin with excessive space, then remove until satisfied. This ensures elements breathe properly. ### Establish a Spacing System Use a constrained scale with meaningful jumps (~25% between values): ``` 4px, 8px, 12px, 16px, 24px, 32px, 48px, 64px, 96px, 128px ``` **Base on 16px** (default browser font size, divides nicely). ### You Don't Have to Fill the Screen If content only needs 600px, don't stretch it to 1200px. Extra space around edges never hurts. ### Shrink the Canvas Designing for mobile first often reveals better solutions. Start with ~400px width, then expand. ### Grids Are Overrated Not all elements should be fluid. Sidebars, icons, and avatars often work better with fixed sizes while main content flexes. ```css /* Better than percentage-based sidebar */ .sidebar { width: 280px; flex-shrink: 0; } .main { flex: 1; min-width: 0; } ``` ### Relative Sizing Doesn't Scale Headlines shouldn't stay proportional to body text across screen sizes. Large elements should shrink faster than small ones on mobile. ```css /* Desktop: 45px headline, 18px body (2.5x ratio) */ /* Mobile: 24px headline, 14px body (1.7x ratio) */ ``` ### Avoid Ambiguous Spacing When elements are grouped without visible separators, the spacing between groups must be greater than spacing within groups. ```css /* Form labels should be closer to their inputs than to previous inputs */ .form-group { margin-bottom: 24px; } .form-label { margin-bottom: 8px; } ``` --- ## 4. Designing Text ### Establish a Type Scale Hand-pick sizes rather than using mathematical ratios: ``` 12px, 14px, 16px, 18px, 20px, 24px, 30px, 36px, 48px, 60px, 72px ``` Use `px` or `rem`, not `em` (to avoid compounding issues with nesting). ### Use Good Fonts **Safe choices:** - System font stack for familiarity - Fonts with 5+ weights indicate quality craftsmanship - High x-height fonts for UI text (better legibility at small sizes) **Filter by weight count** on Google Fonts to find quality options. ### Keep Line Length in Check Optimal: **45-75 characters per line** (20-35em width). ```css .prose { max-width: 65ch; } /* Character-based width */ ``` ### Baseline, Not Center When mixing font sizes on one line, align by baseline, not vertical center. ```css .header-row { align-items: baseline; } /* Not center */ ``` ### Line Height Is Proportional | Font Size | Line Height | |-----------|-------------| | Small text (14px) | 1.5-1.75 | | Body (16-18px) | 1.5-1.65 | | Headlines (24px+) | 1.1-1.25 | | Large headlines (36px+) | 1.0-1.1 | Wider paragraphs need taller line heights. ### Not Every Link Needs a Color In link-heavy interfaces, use subtle differentiation (font weight, darker color) instead of blue underlines everywhere. Reserve bold link styling for important navigation. ### Align with Readability in Mind - **Left-align** most text - **Center** only short, independent blocks (headings, CTAs) - **Right-align** numbers in tables for easy comparison - **Hyphenate** justified text ### Use Letter-Spacing Effectively - **Tighten** headlines slightly: `letter-spacing: -0.02em;` - **Widen** all-caps text: `letter-spacing: 0.05em;` - Leave body text alone --- ## 5. Working with Color ### Ditch Hex for HSL HSL (Hue, Saturation, Lightness) makes color relationships intuitive: ```css /* HSL is easier to reason about */ --primary-500: hsl(220, 80%, 50%); --primary-600: hsl(220, 80%, 40%); /* Just darken lightness */ --primary-400: hsl(220, 80%, 60%); /* Just lighten */ ``` ### You Need More Colors Than You Think A complete palette includes: | Category | Shades Needed | |----------|---------------| | **Greys** | 8-10 shades (true black looks unnatural) | | **Primary** | 5-10 shades | | **Accent colors** | 5-10 shades each (red, yellow, green, etc.) | ### Define Shades Up Front Don't use `lighten()` or `darken()` functions. Pre-define all shades: 1. Pick your **base** color (good for button backgrounds) 2. Pick your **darkest** shade (for text on light backgrounds) 3. Pick your **lightest** shade (for tinted backgrounds) 4. Fill in 6-7 shades between them ### Don't Let Lightness Kill Saturation As lightness approaches 0% or 100%, increase saturation to maintain vibrancy. ### Perceived Brightness Varies by Hue Yellow appears brighter than blue at the same lightness. To make a color lighter without washing it out, rotate hue toward yellow, cyan, or magenta. To darken, rotate toward red, green, or blue. ### Greys Don't Have to Be Grey Saturate greys slightly for personality: - **Cool greys**: Add blue (hue ~210) - **Warm greys**: Add yellow/orange (hue ~40) ```css --grey-500: hsl(210, 10%, 50%); /* Cool grey */ --grey-500-warm: hsl(40, 10%, 50%); /* Warm grey */ ``` ### Accessible Contrast | Text Type | Minimum Ratio | |-----------|---------------| | Body text (<18px) | 4.5:1 | | Large text (18px+ bold or 24px+) | 3:1 | **Flip the contrast** when colored backgrounds make white text too dark—use dark colored text on light colored backgrounds instead. ### Don't Rely on Color Alone Always pair color with another indicator (icons, patterns, text) for colorblind users. ### System Colors for Status Use traffic light colors with familiar meanings: | Color | Usage | When to Use | |-------|-------|-------------| | **Red** | Error | Negative messages, failures requiring attention | | **Amber** | Warning | Caution, potentially risky actions | | **Green** | Success | Positive messages, completed actions | Always pair with icons for color blind accessibility. ### APCA: The Future of Contrast Measurement WCAG 3 introduces the **Accessible Perceptual Contrast Algorithm (APCA)**—a more accurate contrast measurement: | APCA Value | Use For | |------------|---------| | ≥90 | Preferred for body text (14px+) | | ≥75 | Minimum for body text (18px+) | | ≥60 | Other text (24px or 16px bold+) | | ≥45 | Large text (36px or 24px bold+), UI elements | | ≥30 | Placeholder text, disabled buttons | | ≥15 | Non-text decorative elements | **Key difference:** APCA handles dark backgrounds better than WCAG 2, and swapping text/background colors affects the score. ### Transparent Colors for Flexibility Use transparent colors (with alpha values) for: - Hover states that work on any background - Overlays that adapt to underlying content - Subtle backgrounds that maintain harmony ```css /* Transparent overlays that work on any background */ --hover-overlay: hsla(0, 0%, 0%, 0.05); --active-overlay: hsla(0, 0%, 0%, 0.1); --disabled-overlay: hsla(0, 0%, 100%, 0.5); ``` --- ## 6. Creating Depth ### Emulate a Light Source Light comes from above. Apply this consistently: - **Raised elements**: Lighter top edge, shadow below - **Inset elements**: Shadow at top, lighter bottom edge ```css /* Raised button */ .button { box-shadow: inset 0 1px 0 hsl(220, 80%, 70%), /* Light top edge */ 0 1px 3px hsla(0, 0%, 0%, 0.2); /* Shadow below */ } /* Inset input */ .input { box-shadow: inset 0 2px 4px hsla(0, 0%, 0%, 0.1); } ``` ### Use Shadows to Convey Elevation | Elevation | Shadow | Use for | |-----------|--------|---------| | Low | `0 1px 3px rgba(0,0,0,0.12)` | Buttons, cards | | Medium | `0 4px 6px rgba(0,0,0,0.1)` | Dropdowns, popovers | | High | `0 15px 35px rgba(0,0,0,0.15)` | Modals, dialogs | Define 5 shadow levels and stick to them. ### Shadows Can Have Two Parts Combine a large soft shadow (direct light) with a small tight shadow (ambient occlusion): ```css box-shadow: 0 4px 6px rgba(0, 0, 0, 0.07), /* Large, soft */ 0 1px 3px rgba(0, 0, 0, 0.1); /* Small, tight */ ``` The tight shadow fades at higher elevations. ### Flat Designs Can Have Depth Without shadows: - Use **lighter colors** for raised elements - Use **darker colors** for inset elements - Use **solid offset shadows** (no blur) for flat aesthetic with depth ### Overlap Elements to Create Layers Let cards cross background boundaries. Overlap images with invisible borders to prevent clashing. --- ## 7. Working with Images ### Use Good Photos Bad photos ruin designs. Hire professionals or use quality stock (Unsplash, etc.). Don't use smartphone placeholders. ### Text on Images Needs Consistent Contrast When placing text over images: 1. **Add overlay**: Semi-transparent black (for light text) or white (for dark text) 2. **Lower contrast**: Reduce image contrast, adjust brightness 3. **Colorize**: Desaturate + multiply blend with brand color 4. **Text shadow**: Large blur radius, no offset (glow effect) ### Everything Has an Intended Size - **Don't scale up icons** designed for 16-24px—they look chunky - **Don't scale down screenshots**—details become illegible - **Redraw logos** for small sizes (favicons) Wrap small icons in colored shapes to fill larger spaces: ```html