From craft-workspace-webconsulting-skills
Provides practical UI design patterns and principles from Refactoring UI and Practical UI for layout, spacing, typography, color, hierarchy, and styling in web projects. Use for UI, design tasks.
npx claudepluginhub dirnbauer/webconsulting-skillsThis skill uses the workspace's default tool permissions.
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.
Provides UI/UX design guidance for unique, accessible web interfaces covering colors, typography, layouts. Always asks before decisions; activates for building web components, pages, apps.
Designs consistent UIs with color palettes, typography scales, spacing systems, visual hierarchy, and accessibility. Use for design systems, color/font choices, dark mode, or visual direction.
Generates distinctive, production-grade frontend code for web components, pages, and apps with bold, creative UX designs in styles like brutalist or retro-futuristic, avoiding generic AI aesthetics.
Share bugs, ideas, or general feedback.
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.
These patterns are adapted from two excellent resources:
We highly recommend both works for deepening your UI design knowledge.
Before diving into specific patterns, internalize these foundational principles:
Base design decisions on risk assessment—the risk that someone could have difficulty using an interface:
Always consider: people with poor eyesight, low computer literacy, reduced dexterity, and cognitive differences.
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 |
Interaction cost = physical + mental effort to complete a task. Reduce it by:
Cognitive load is the mental effort required to use an interface. Reduce it by:
Create a system of reusable guidelines before designing:
This ensures consistency and speeds up decision-making.
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.
Per Jakob's Law, stick with patterns people already know:
Save creativity for your product's unique value proposition, not basic UI conventions.
Roughly 80% of users use 20% of features. Prioritize the common paths:
Don't begin by designing the shell (navigation, sidebar, footer). Start with actual functionality.
Wrong approach:
Right approach:
In early stages, ignore typefaces, shadows, and icons. Use thick markers or low-fidelity wireframes to explore layouts quickly.
Design in grayscale first. This forces you to use spacing, contrast, and size to create hierarchy. Color comes later as enhancement.
Design the smallest useful version first. Don't design features you can't build yet—ship what works.
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.
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:
slate-900)slate-600)slate-400)Grey text on colored backgrounds looks washed out. Instead, pick a color with the same hue as the background, adjusting saturation and lightness.
/* 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 */
If a primary element doesn't stand out, don't make it louder—make competing elements quieter.
/* Instead of making active nav item bolder... */
/* ...make inactive items softer */
.nav-item { color: var(--slate-400); }
.nav-item.active { color: var(--slate-900); }
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.
Heavy elements (icons, bold text) can be de-emphasized with softer colors. Light elements (thin borders) can be emphasized with increased weight.
/* 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 */
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.
Begin with excessive space, then remove until satisfied. This ensures elements breathe properly.
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).
If content only needs 600px, don't stretch it to 1200px. Extra space around edges never hurts.
Designing for mobile first often reveals better solutions. Start with ~400px width, then expand.
Not all elements should be fluid. Sidebars, icons, and avatars often work better with fixed sizes while main content flexes.
/* Better than percentage-based sidebar */
.sidebar { width: 280px; flex-shrink: 0; }
.main { flex: 1; min-width: 0; }
Headlines shouldn't stay proportional to body text across screen sizes. Large elements should shrink faster than small ones on mobile.
/* Desktop: 45px headline, 18px body (2.5x ratio) */
/* Mobile: 24px headline, 14px body (1.7x ratio) */
When elements are grouped without visible separators, the spacing between groups must be greater than spacing within groups.
/* Form labels should be closer to their inputs than to previous inputs */
.form-group { margin-bottom: 24px; }
.form-label { margin-bottom: 8px; }
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).
Safe choices:
Filter by weight count on Google Fonts to find quality options.
Optimal: 45-75 characters per line (20-35em width).
.prose { max-width: 65ch; } /* Character-based width */
When mixing font sizes on one line, align by baseline, not vertical center.
.header-row { align-items: baseline; } /* Not center */
| 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.
In link-heavy interfaces, use subtle differentiation (font weight, darker color) instead of blue underlines everywhere. Reserve bold link styling for important navigation.
letter-spacing: -0.02em;letter-spacing: 0.05em;HSL (Hue, Saturation, Lightness) makes color relationships intuitive:
/* 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 */
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.) |
Don't use lighten() or darken() functions. Pre-define all shades:
As lightness approaches 0% or 100%, increase saturation to maintain vibrancy.
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.
Saturate greys slightly for personality:
--grey-500: hsl(210, 10%, 50%); /* Cool grey */
--grey-500-warm: hsl(40, 10%, 50%); /* Warm grey */
| 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.
Always pair color with another indicator (icons, patterns, text) for colorblind users.
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.
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.
Use transparent colors (with alpha values) for:
/* 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);
Light comes from above. Apply this consistently:
/* 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);
}
| 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.
Combine a large soft shadow (direct light) with a small tight shadow (ambient occlusion):
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.
Without shadows:
Let cards cross background boundaries. Overlap images with invisible borders to prevent clashing.
Bad photos ruin designs. Hire professionals or use quality stock (Unsplash, etc.). Don't use smartphone placeholders.
When placing text over images:
Wrap small icons in colored shapes to fill larger spaces:
<div class="w-12 h-12 bg-blue-100 rounded-lg flex items-center justify-center">
<svg class="w-6 h-6 text-blue-600"><!-- Icon --></svg>
</div>
object-fit: cover.user-image {
object-fit: cover;
aspect-ratio: 16/9;
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1);
}
Clear interface text is as important as visual design. Poor copy creates confusion and increases cognitive load.
Remove unnecessary words. Every word should earn its place.
| Verbose | Concise |
|---|---|
| "Click here to submit your form" | "Submit" |
| "In order to continue, please..." | "To continue..." |
| "Are you sure you want to delete?" | "Delete this item?" |
Sentence case is easier to read than Title Case or ALL CAPS:
Put the most important information first. Users scan—don't bury key info.
| Back-loaded | Front-loaded |
|---|---|
| "To reset your password, click here" | "Reset password" |
| "If you need help, contact support" | "Contact support for help" |
Write at an 8th-grade reading level. Avoid jargon and technical terms.
| Complex | Simple |
|---|---|
| "Authenticate your credentials" | "Sign in" |
| "Terminate session" | "Log out" |
| "Insufficient permissions" | "You don't have access" |
Good error messages:
| Bad | Good |
|---|---|
| "Error 403" | "You don't have permission to view this page" |
| "Invalid input" | "Please enter a valid email address" |
| "Request failed" | "Couldn't save changes. Check your connection and try again" |
Use the same words for the same concepts throughout:
Use action verbs that describe what happens:
| Vague | Specific |
|---|---|
| "OK" | "Save changes" |
| "Submit" | "Create account" |
| "Yes" | "Delete message" |
Define three distinct button styles based on importance:
| Weight | Style | Use For |
|---|---|---|
| Primary | Solid, high contrast, brand color | Main action (one per screen) |
| Secondary | Outline or muted fill | Alternative actions |
| Tertiary | Text-only, link style | Least important actions |
Disabled buttons create confusion. Instead:
/* If you must use disabled buttons */
.button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
| Default | Upgrade |
|---|---|
| Bullet points | Custom icons (checkmarks, locks, stars) |
| Quote marks | Large, colored quote symbols |
| Links | Bold, custom underline overlapping text |
| Checkboxes | Brand-colored custom controls |
A 4px colored border adds polish without design skills:
/* Top of cards */
.card { border-top: 4px solid var(--primary-500); }
/* Side of alerts */
.alert { border-left: 4px solid var(--warning-500); }
/* Under headlines */
.headline::after {
content: '';
display: block;
width: 60px;
height: 4px;
background: var(--primary-500);
margin-top: 12px;
}
Break monotony with:
Empty states are first impressions. Include:
Instead of borders for separation:
| Alternative | When to Use |
|---|---|
| Box shadows | Outline elements on same-color backgrounds |
| Different background colors | Adjacent sections |
| Extra spacing | Group separation |
Challenge assumptions about component design:
Study designs you admire. Notice unconventional choices:
Recreate designs from scratch without inspecting code. Discovering why your version differs teaches lasting lessons.
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-6: 24px;
--space-8: 32px;
--space-12: 48px;
--space-16: 64px;
--space-24: 96px;
--space-32: 128px;
--text-xs: 12px;
--text-sm: 14px;
--text-base: 16px;
--text-lg: 18px;
--text-xl: 20px;
--text-2xl: 24px;
--text-3xl: 30px;
--text-4xl: 36px;
--text-5xl: 48px;
--text-6xl: 60px;
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
--shadow-md: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.06);
--shadow-lg: 0 4px 6px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.06);
--shadow-xl: 0 10px 15px rgba(0, 0, 0, 0.1), 0 4px 6px rgba(0, 0, 0, 0.05);
--shadow-2xl: 0 20px 25px rgba(0, 0, 0, 0.15), 0 10px 10px rgba(0, 0, 0, 0.04);
--radius-sm: 2px;
--radius-md: 4px;
--radius-lg: 8px;
--radius-xl: 12px;
--radius-2xl: 16px;
--radius-full: 9999px;
Every interactive element needs clear state feedback:
/* Button states example */
.button {
background: var(--primary-500);
transition: all 0.15s ease;
}
.button:hover {
background: var(--primary-600);
}
.button:active {
background: var(--primary-700);
transform: translateY(1px);
}
.button:focus-visible {
outline: 2px solid var(--primary-500);
outline-offset: 2px;
}
.button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
| Requirement | Target |
|---|---|
| Text contrast (small) | 4.5:1 minimum |
| Text contrast (large) | 3:1 minimum |
| UI component contrast | 3:1 minimum |
| Focus indicators | Visible, 3:1 contrast |
| Touch targets | 44×44px minimum |
| Color independence | Never color alone |
| Text resize | Works at 200% zoom |
For deeper learning, study the source materials: Refactoring UI and Practical UI