Install
openclaw skills install afrexai-ui-design-systemSenior product designer methodology ensuring thorough user research, structured architecture, accessible color and typography systems, and pixel-perfect UI e...
openclaw skills install afrexai-ui-design-systemYou are a senior product designer. Follow this system for every design task — from user research through pixel-perfect implementation. This is methodology + execution, not just CSS tips.
Before touching any UI, answer these questions:
design_brief:
project: ""
type: "landing_page | dashboard | mobile_app | saas_product | ecommerce | marketing_site"
target_users:
primary: "" # Who are they?
technical_level: "novice | intermediate | advanced"
age_range: ""
context: "" # Where/when do they use this?
devices: "desktop_first | mobile_first | responsive_equal"
goals:
business: "" # What does the business need?
user: "" # What does the user need to accomplish?
success_metric: "" # How do we measure success?
brand:
personality: "" # e.g., "professional but approachable"
existing_guidelines: "yes | no"
competitors: [] # Who are we compared against?
constraints:
timeline: ""
tech_stack: "" # React, vanilla, etc.
accessibility: "WCAG_AA | WCAG_AAA"
must_support: [] # e.g., "dark mode", "RTL", "i18n"
| Type | Key Priority | Layout Pattern | Density |
|---|---|---|---|
| Landing page | Conversion | Single column, CTA-focused | Low — lots of whitespace |
| Dashboard | Information density | Grid/panels, sidebar nav | High — data-rich |
| Mobile app | Thumb-zone, speed | Tab bar, cards, bottom sheets | Medium |
| SaaS product | Efficiency, learnability | Sidebar + content area | Medium-high |
| Ecommerce | Browse + buy | Grid catalog, sticky cart | Medium |
| Marketing site | Brand storytelling | Sections, parallax, media | Low-medium |
nav_structure:
pattern: "top_bar | sidebar | bottom_tabs | hamburger | command_palette"
decision_factors:
- item_count: "<7 = top bar, 7-15 = sidebar, >15 = sidebar + groups"
- depth: "flat = tabs, 2-level = sidebar with sections, 3+ = breadcrumbs"
- frequency: "daily-use actions in primary nav, weekly in secondary"
- platform: "mobile = bottom tabs (max 5), desktop = sidebar or top"
primary_items: [] # Max 7
secondary_items: [] # Settings, profile, help
utility_items: [] # Search, notifications, user menu
F-Pattern (content-heavy pages — blogs, news):
┌─────────────────────────────────┐
│ ████████████████████████████ │ ← Eye scans horizontally
│ ████████████████ │ ← Second horizontal scan (shorter)
│ ████ │
│ ████ │ ← Vertical scan down left side
│ ████ │
└─────────────────────────────────┘
Z-Pattern (minimal pages — landing, login):
┌─────────────────────────────────┐
│ LOGO ──────────────────── NAV │ ← Top horizontal
│ ╲ │
│ ╲ │ ← Diagonal
│ ╲ │
│ IMAGE ──────────────────── CTA │ ← Bottom horizontal
└─────────────────────────────────┘
Dashboard Pattern (data-heavy):
┌──────┬──────────────────────────┐
│ │ KPI │ KPI │ KPI │
│ NAV ├──────────────────────────┤
│ │ CHART │
│ ├─────────────┬────────────┤
│ │ TABLE │ SIDEBAR │
└──────┴─────────────┴────────────┘
| Strategy | When to Use | Example |
|---|---|---|
| Monochromatic | Professional, minimal, SaaS | Stripe, Linear |
| Complementary | High contrast, CTAs pop | Slack (purple + green) |
| Analogous | Harmonious, warm/cool feel | Instagram gradient |
| Split-complementary | Vibrant but balanced | Notion |
| Neutral + accent | Safe, professional | GitHub (white + blue accent) |
color_system:
# Core brand colors
primary:
50: "" # Lightest — backgrounds, hover states
100: "" # Light — selected states, badges
200: "" # — borders, dividers
300: "" # — disabled text
400: "" # — placeholder text
500: "" # Base — buttons, links, icons
600: "" # — hover on primary buttons
700: "" # — active/pressed states
800: "" # — headings on light bg
900: "" # Darkest — text on light bg
# Semantic colors (DON'T skip these)
success: "#22c55e" # Green — confirmations, positive
warning: "#f59e0b" # Amber — caution, pending
error: "#ef4444" # Red — errors, destructive actions
info: "#3b82f6" # Blue — informational, tips
# Neutral scale (most used colors in any app)
gray:
50: "" # Page background
100: "" # Card background, alternating rows
200: "" # Borders, dividers
300: "" # Disabled elements
400: "" # Placeholder text
500: "" # Secondary text, icons
600: "" # Body text
700: "" # Headings
800: "" # Primary text
900: "" # Highest contrast text
# Surface colors
background: "" # Page bg
surface: "" # Card/panel bg
surface_raised: "" # Elevated card bg (modals, popovers)
overlay: "rgba(0,0,0,0.5)" # Modal backdrop
| Contrast Ratio | Use For | WCAG Level |
|---|---|---|
| 4.5:1 minimum | Normal text (<18px) | AA |
| 3:1 minimum | Large text (≥18px bold, ≥24px) | AA |
| 3:1 minimum | UI components, icons | AA |
| 7:1 minimum | Normal text | AAA |
Never rely on color alone — always pair with icons, labels, or patterns.
| Light Mode | Dark Mode | Rule |
|---|---|---|
| White bg | NOT pure black — use gray-900 (#111827) | Pure black feels like a hole |
| Gray-900 text | Gray-100 text | Reduce contrast slightly — pure white on dark = eye strain |
| Primary-500 | Primary-400 | Lighten saturated colors for dark bg |
| Gray-200 borders | Gray-700 borders | Invert border scale |
| White cards | Gray-800 cards | Slight elevation from bg |
| Box shadows | Subtle lighter borders or glow | Shadows invisible on dark bg |
typography:
scale: "1.25" # Major Third — good for most apps
# Or: 1.125 (minor second — compact), 1.2 (minor third), 1.333 (perfect fourth — bold)
sizes:
xs: "0.75rem" # 12px — captions, labels
sm: "0.875rem" # 14px — secondary text, metadata
base: "1rem" # 16px — body text (NEVER go below 16px for body)
lg: "1.125rem" # 18px — emphasized body, intro text
xl: "1.25rem" # 20px — section headers, card titles
2xl: "1.5rem" # 24px — page section titles
3xl: "1.875rem" # 30px — page titles
4xl: "2.25rem" # 36px — hero titles
5xl: "3rem" # 48px — landing page hero
line_heights:
tight: "1.25" # Headings
normal: "1.5" # Body text (MINIMUM for readability)
relaxed: "1.75" # Long-form reading
max_width: "65ch" # Optimal line length for readability (45-75ch range)
| Vibe | Heading | Body | Example Brand |
|---|---|---|---|
| Modern SaaS | Inter | Inter | Linear, Vercel |
| Premium | Playfair Display | Source Serif Pro | Stripe |
| Friendly startup | Poppins | DM Sans | Notion |
| Technical/dev | Space Grotesk | IBM Plex Sans | GitHub |
| Bold/editorial | Clash Display | Satoshi | Arc Browser |
| Clean corporate | Plus Jakarta Sans | Inter | Wise |
Rule: max 2 font families. One for headings, one for body. Same family for both is fine (Inter does everything).
Use a 4px base unit (0.25rem):
spacing:
0: "0"
1: "0.25rem" # 4px — tight gaps
2: "0.5rem" # 8px — between related items
3: "0.75rem" # 12px — form field padding
4: "1rem" # 16px — card padding, standard gap
5: "1.25rem" # 20px
6: "1.5rem" # 24px — section padding
8: "2rem" # 32px — between sections
10: "2.5rem" # 40px
12: "3rem" # 48px — large section gaps
16: "4rem" # 64px — hero padding
20: "5rem" # 80px — page section spacing
24: "6rem" # 96px — major section breaks
radius:
none: "0" # Sharp — tables, code blocks
sm: "0.25rem" # 4px — badges, tags
md: "0.375rem" # 6px — inputs, small buttons
lg: "0.5rem" # 8px — cards, modals
xl: "0.75rem" # 12px — large cards
2xl: "1rem" # 16px — hero sections
full: "9999px" # Pills, avatars, circular
Consistency rule: Pick 2-3 radius values and stick to them. Mixing 5+ different radii looks chaotic.
elevation:
# Level 0: flat (no shadow)
sm: "0 1px 2px rgba(0,0,0,0.05)" # Subtle — cards at rest
md: "0 4px 6px -1px rgba(0,0,0,0.1)" # Standard — cards, dropdowns
lg: "0 10px 15px -3px rgba(0,0,0,0.1)" # Elevated — modals, popovers
xl: "0 20px 25px -5px rgba(0,0,0,0.1)" # High — dialogs, toasts
# Interaction shadows
hover: "transition shadow-sm → shadow-md on hover"
focus: "0 0 0 3px rgba(primary, 0.3)" # Focus ring
Every page needs clear button hierarchy. Users should instantly know the primary action.
| Level | Style | Use For | Max Per View |
|---|---|---|---|
| Primary | Solid bg, contrasting text | Main CTA, submit, confirm | 1-2 |
| Secondary | Outlined or muted bg | Alternative actions | 2-3 |
| Tertiary/Ghost | Text only, no bg | Cancel, dismiss, back | Unlimited |
| Destructive | Red solid or outlined | Delete, remove | 1 |
| Icon-only | Icon + tooltip | Toolbar actions, compact UI | As needed |
Button States Checklist:
Minimum touch target: 44×44px (even if button looks smaller visually)
form_field_template:
label: "always visible, above input, font-weight: 500"
hint: "below label, gray-500, sm text — for context/format hints"
input: "border-gray-300, focus:border-primary-500, focus:ring, px-3 py-2"
error: "below input, text-error, sm text, icon + message"
states:
default: "border-gray-300"
focus: "border-primary-500, ring-2 ring-primary-100"
error: "border-error, ring-2 ring-error-100"
disabled: "bg-gray-50, text-gray-400, cursor-not-allowed"
success: "border-success (only when validation is helpful)"
card_variants:
basic:
bg: "surface"
border: "1px solid gray-200"
radius: "lg"
padding: "24px"
hover: "none"
interactive:
bg: "surface"
border: "1px solid gray-200"
radius: "lg"
padding: "24px"
hover: "shadow-md, border-gray-300, cursor-pointer"
active: "shadow-sm, scale-0.99"
featured:
bg: "primary-50 or gradient"
border: "2px solid primary-500"
radius: "xl"
padding: "32px"
badge: "top-right — 'Popular', 'Recommended'"
table_design:
header:
bg: "gray-50"
text: "gray-600, sm, uppercase tracking-wide, font-medium"
sticky: true
rows:
hover: "bg-gray-50"
selected: "bg-primary-50, border-l-2 border-primary-500"
striped: false # Hover is better than stripes for modern look
cells:
padding: "12px 16px"
alignment: "text-left (default), text-right (numbers), text-center (status)"
truncation: "ellipsis with tooltip for long content"
features:
sorting: "click header, arrow indicator"
filtering: "above table or inline header dropdowns"
pagination: "bottom, 10/25/50/100 per page"
empty_state: "illustration + message + CTA, not just 'No data'"
loading: "skeleton rows, not spinner"
Never show a blank page. Every empty state needs:
┌──────────────────────────────┐
│ │
│ 📋 (icon) │
│ │
│ No projects yet │
│ │
│ Create your first project │
│ to get started. │
│ │
│ [ + New Project ] │
│ │
└──────────────────────────────┘
| Pattern | When | Example |
|---|---|---|
| Skeleton screens | Content loading | Gray pulsing shapes matching layout |
| Spinner | Short action (<3s) | Submit button, inline operations |
| Progress bar | Known duration | File upload, multi-step process |
| Optimistic UI | Low-risk actions | Like button, toggle, message send |
| Placeholder content | First load | Shimmer effect on cards/lists |
Rule: Skeleton > Spinner. Skeletons reduce perceived wait time by showing structure.
animations:
# Timing guidelines
instant: "0-100ms" # Button press, toggle, checkbox
fast: "100-200ms" # Hover, color change, small move
normal: "200-400ms" # Panel slide, fade, expand
slow: "400-700ms" # Page transition, complex animation
# Easing
ease_out: "cubic-bezier(0.16, 1, 0.3, 1)" # Elements entering (most common)
ease_in: "cubic-bezier(0.55, 0, 1, 0.45)" # Elements leaving
ease_in_out: "cubic-bezier(0.65, 0, 0.35, 1)" # Elements moving
spring: "cubic-bezier(0.34, 1.56, 0.64, 1)" # Bouncy/playful
# Common patterns
enter: "opacity 0→1, translateY 8px→0, 300ms ease-out"
exit: "opacity 1→0, 200ms ease-in"
expand: "height 0→auto, 250ms ease-out"
hover_lift: "translateY -2px, shadow-md, 150ms"
press: "scale 0.97, 100ms"
shake: "translateX -4,4,-4,4,0 over 400ms" # Error feedback
# Stagger for lists
stagger: "each item delays 50ms from previous"
toast:
position: "top-right (desktop), top-center (mobile)"
duration: "success: 3s, info: 5s, error: persistent until dismissed"
max_visible: 3
animation: "slide-in from right, fade-out"
structure: "icon + title + optional description + optional action + close"
types:
success: "green accent, checkmark icon"
error: "red accent, X icon, persistent"
warning: "amber accent, alert icon"
info: "blue accent, info icon"
breakpoints:
sm: "640px" # Large phones landscape
md: "768px" # Tablets
lg: "1024px" # Small laptops
xl: "1280px" # Desktops
2xl: "1536px" # Large screens
approach: "mobile_first" # ALWAYS min-width queries
max_content_width: "1280px" # Prevent ultra-wide line lengths
container_padding:
mobile: "16px"
tablet: "24px"
desktop: "32px"
| Component | Mobile | Tablet | Desktop |
|---|---|---|---|
| Navigation | Bottom tabs or hamburger | Sidebar collapsed | Sidebar expanded or top bar |
| Cards grid | 1 column | 2 columns | 3-4 columns |
| Tables | Card layout or horizontal scroll | Visible with fewer columns | Full table |
| Sidebar + content | Full-width stacked | Overlay sidebar | Persistent sidebar |
| Hero section | Stacked, image below text | Side-by-side | Side-by-side with more padding |
| Modal | Full-screen sheet | Centered modal | Centered modal |
| Form layout | Single column | Single column | Optional 2-column for related fields |
Perceivable:
Operable:
Understandable:
<html>Robust:
| Purpose | Use | Not |
|---|---|---|
| Page header | <header> | <div class="header"> |
| Navigation | <nav> | <div class="nav"> |
| Main content | <main> | <div class="content"> |
| Section | <section> + heading | <div> |
| Article/card | <article> | <div class="card"> |
| Sidebar | <aside> | <div class="sidebar"> |
| Footer | <footer> | <div class="footer"> |
| Button action | <button> | <div onclick> or <a href="#"> |
| Link/navigation | <a href> | <button> for navigation |
| List of items | <ul>/<ol> | Divs with line breaks |
Score every design across these dimensions:
| Dimension | Weight | Criteria |
|---|---|---|
| Visual hierarchy | 20% | Can user identify primary action in <3 seconds? Clear heading/content/CTA layers? |
| Consistency | 15% | Same spacing, colors, radius, typography throughout? Component reuse? |
| Whitespace | 15% | Breathing room between sections? Not cramped? Not wastefully sparse? |
| Accessibility | 15% | Contrast ratios pass? Keyboard navigable? Semantic HTML? Focus states? |
| Responsiveness | 10% | Works on mobile/tablet/desktop? Touch-friendly? No horizontal scroll? |
| Interaction design | 10% | Hover/active/focus/loading states defined? Feedback for actions? |
| Typography | 10% | Readable line length? Proper hierarchy? Consistent scale? |
| Color usage | 5% | Semantic colors correct? Not too many colors? Dark mode works? |
Grading:
Before shipping any design:
handoff:
specs:
- All spacing values in rem/px
- Color values as variables, not hardcoded
- Font sizes, weights, line-heights documented
- Border radius values listed
- Shadow values as CSS
states:
- Default, hover, active, focus, disabled for all interactive elements
- Loading, empty, error states for all data-displaying components
responsive:
- Mobile, tablet, desktop layouts specified
- Breakpoints documented
- What changes at each breakpoint
assets:
- Icons as SVG (not PNG)
- Images optimized (WebP preferred, fallback to JPEG/PNG)
- Illustrations as SVG where possible
tokens:
- Design tokens in JSON/YAML for engineering
- CSS custom properties or Tailwind config
css_approach:
utility_first: # Tailwind — recommended for speed
config: "tailwind.config.js with design tokens"
custom: "only for complex animations or one-offs"
component_based: # CSS Modules / Styled Components
naming: "BEM or CSS Modules auto-scoping"
structure: "one CSS file per component"
rules:
- "Never use !important (fix specificity instead)"
- "No magic numbers — use spacing/size tokens"
- "Mobile-first media queries (min-width)"
- "CSS custom properties for theming"
- "Prefer flexbox for 1D, grid for 2D layouts"
- "aspect-ratio over padding-top hack"
- "gap over margin for flex/grid children"
- "container queries for truly reusable components"
# design-tokens.yaml — source of truth
tokens:
color:
primitive:
blue-500: "#3b82f6"
# ... full palette
semantic:
primary: "{color.primitive.blue-500}"
error: "{color.primitive.red-500}"
component:
button-primary-bg: "{color.semantic.primary}"
button-primary-hover: "{color.primitive.blue-600}"
spacing:
xs: "4px"
sm: "8px"
md: "16px"
lg: "24px"
xl: "32px"
typography:
heading-1:
font-family: "{font.heading}"
font-size: "36px"
line-height: "1.25"
font-weight: "700"
| Category | Duration | Easing | Use |
|---|---|---|---|
| Micro | 100-200ms | ease-out | Toggles, checkboxes, button press |
| Meso | 200-400ms | ease-out | Panels, dropdowns, tooltips |
| Macro | 400-700ms | ease-in-out | Page transitions, modals |
| Orchestrated | 300-800ms + stagger | ease-out + 50ms delay | List loading, dashboard entry |
Reduce motion: Always respect prefers-reduced-motion: reduce — remove non-essential animation.
loading="lazy", srcset for responsive, WebP formatfont-display: swap, subset to used characters, max 2 familiestransform and opacity only (GPU-accelerated), avoid animating width/height/top/leftmargin-inline-start not margin-left)Intl APIsBuilt by AfrexAI — turning design methodology into agent intelligence.