Ui Design Standard
UI/UX design rules for spacing, typography, color, animation, responsive, accessibility, and grid
Tags
Overview
Purpose
UI/UX design rules for spacing, typography, color, animation, responsive, accessibility, and grid
Rules
SPC-001: All spacing values MUST be multiples of 4px (the base unit from brand.yml)
Verification: Check CSS for hardcoded spacing values not in the 4px scale
SPC-002: Use named spacing tokens, not raw pixel values
Verification: Grep for hardcoded px values in component CSS
SPC-003: Apply spacing by context
Verification: Review component padding against context classification
SPC-004: Elements with no visual boundary (borders, background) MAY use 0 spacing on one axis to visually merge with adjacent elements
Verification: Check that zero-spacing is intentional, not missing
TYP-001: All font sizes MUST use typography tokens from brand.yml (--type-{category}-{size}-size)
Verification: Check CSS for hardcoded font-size values
TYP-002: Line height follows the token scale. Dense UIs use tighter line-height.
Verification: Check line-height values against token scale
TYP-003: Use consistent font weights to establish visual hierarchy
Verification: Check that heavier weights correspond to higher hierarchy levels
TYP-004: Body text MUST be >= 16px (brand.yml base_size). Label text MUST be >= 11px.
Verification: Check CSS for font sizes below minimums
TYP-005: Every font-family declaration MUST include system fallback stack
Verification: Check font-family declarations for fallback stacks
CLR-001: Components MUST use semantic color tokens (--color-primary, --color-surface). NEVER use primitive color names (--blue-500,
Verification: Grep for hex values, rgb(), hsl() in component CSS (brand-bridge.css is exempt)
CLR-002: Text on background MUST meet WCAG 2.1 AA contrast ratios: - Normal text (< 18px or < 14px bold): 4.5:1 minimum - Large text (>= 18px or >= 14px bold): 3:1 minimum - UI components and graphical objects: 3:1 minimum
Verification: Use contrast checker tool on all text/background combinations
CLR-003: When defining NEW color tokens, prefer OKLCH color model for perceptual uniformity. Existing hex values remain for backward compatibility.
OKLCH provides uniform perceptual lightness across hues (Radix Colors approach)
Verification: New color definitions should use oklch() or reference an OKLCH-derived value
CLR-004: Status colors (success, warning, error, info) MUST be distinct in hue, not just lightness. Each status color MUST pass contrast on both light and dark backgrounds.
Verification: Check status colors are distinct in hue and pass contrast
CLR-005: Dark mode is NOT just inverted colors. Follow Linear/Vercel aesthetic: - Background: near-black (#0F0F10 range), not pure black (#000000) - Text: off-white (#EEEFF1), not pure white (#FFFFFF) - Reduced saturation on accent colors - Elevated surfaces use slightly lighter backgrounds (not shadows)
Verification: Compare dark mode palette against these rules
CLR-006: Focus ring color (--color-border-focus) MUST be visible against ALL backgrounds in the current theme. Use a contrasting outline with offset.
Verification: Tab through all interactive elements and verify focus visibility
ANI-001: Prefer CSS native animation mechanisms before JavaScript libraries: 1. CSS transitions (simple state changes) 2. CSS @keyframes (looping, complex sequences) 3. View Transitions API (page/component transitions) 4. Scroll-driven animations (scroll-linked effects) 5. Motion.dev / GSAP (physics, complex orchestration) — last resort
Verification: Check if CSS native could replace JS animation library usage
ANI-002: All animation durations MUST use brand.yml motion tokens
Verification: Grep for hardcoded ms/s values in transitions/animations
ANI-003: Use brand.yml easing tokens for consistent motion character
Verification: Check that easing functions use tokens, not hardcoded cubic-bezier
ANI-004: ALL animations MUST respect prefers-reduced-motion. Use var(--duration-reduced) which resolves to 0ms when reduced-motion is active. This is NOT optional — brand.yml requires reduced_motion_duration.
Verification: Toggle prefers-reduced-motion in browser and verify all animations stop
ANI-005: Page load MUST NOT trigger entrance animations. Content should appear immediately. Animations activate only on user interaction or scroll.
First Contentful Paint should not be delayed by decorative animation
Verification: Load page with animations disabled and compare FCP times
RSP-001: CSS MUST be authored mobile-first. Use min-width media queries to add complexity.
Verification: Check media queries use min-width, not max-width
RSP-002: Use these breakpoints consistently across all components
Verification: Grep for non-standard breakpoint values
RSP-003: For component-level responsiveness, prefer CSS container queries over viewport media queries. Components should adapt to their container, not the viewport.
Verification: Check if viewport query could be replaced with container query
RSP-004: Page-level margins and large gaps SHOULD use clamp() for fluid scaling between breakpoints
Verification: Check page margins and section gaps for clamp() usage
RSP-005: Interactive elements MUST have minimum 44x44px touch target on mobile (WCAG 2.5.8). Use padding or min-width/min-height.
Verification: Check interactive element dimensions on mobile breakpoint
A11Y-001: ALL interactive elements MUST be keyboard accessible: - Tab: move between focusable elements - Enter/Space: activate buttons and links - Arrow keys: navigate within composite widgets (tabs, menus, trees) - Escape: close overlays, cancel actions
Verification: Tab through all interactive elements without using mouse
A11Y-002: Focus MUST be visible and logical: - Focus ring visible on ALL interactive elements (CLR-006) - Focus order follows visual order (no tabindex > 0) - Focus trapped in modals/dialogs - Focus restored to trigger element when overlay closes
Verification: Tab through page and verify focus ring and order
A11Y-003: Pages MUST use semantic HTML and ARIA landmarks: - <nav> or role="navigation" — primary navigation - <main> — primary content area - <aside> or role="complementary" — sidebars, panels - <header> / <footer> — page header/footer - role="tablist" / role="tab" — tab interfaces
Verification: Check page structure for landmark roles
A11Y-004: Dynamic UI MUST announce changes: - aria-live="polite" for non-urgent updates (loading states, status messages) - aria-live="assertive" for urgent updates (errors, critical alerts) - aria-expanded for collapsible sections - aria-selected for selectable items (tabs, list items) - aria-disabled for disabled elements (prefer disabled attribute when available)
Verification: Check dynamic content changes with screen reader
A11Y-005: - Meaningful images: alt text describing content - Decorative images: alt="" (empty alt) - Icon-only buttons: aria-label describing the action - SVG icons: role="img" + aria-label, or aria-hidden="true" if decorative - Alt MUST NOT duplicate adjacent visible text: when an image sits inside a link/card whose caption already names it, an identical alt makes a screen reader read the name twice. Use alt="" (decorative — the caption carries it) or a DISTINCT descriptive alt (e.g. "Bestie Real Estate — property listing UI" next to the "Bestie Real Estate" label), never a verbatim repeat of the label.
Verification: Check all images and icons for appropriate alternative text; flag any alt string that exactly equals adjacent link/caption text
A11Y-006: Forms MUST have: - Visible <label> associated via htmlFor/for - Error messages linked via aria-describedby - Required fields marked with aria-required="true" - Form validation errors announced with aria-live
Verification: Submit forms with errors and verify screen reader announces them
A11Y-007: Information MUST NOT be conveyed by color alone. Use text labels, icons, or patterns in addition to color.
Verification: View page in grayscale and verify all information is still conveyed
GRD-001: Page-level layout MUST use CSS Grid with named areas. Flexbox for component-level layout.
Verification: Check page layout uses CSS Grid, not float or absolute positioning
GRD-002: Text and content MUST align to a consistent vertical rhythm within sections. Use the 4px grid (SPC-001) for vertical alignment.
Verification: Enable grid overlay in browser DevTools and check alignment
GRD-003: Body text content SHOULD have max-width of 65-75ch for readability. Full-width layouts use the grid to constrain content columns.
Verification: Check content width on large screens
GRD-004: Standard sidebar widths for IDE-style layouts: - Collapsed: 48px (icon only) - Default: 240-280px - Wide: 320-360px - Max: 50% of viewport
Verification: Check sidebar widths against conventions
GRD-005: Prefer CSS gap property over individual margins for spacing between siblings. gap works with both Grid and Flexbox and produces more predictable spacing.
Verification: Check for margin-based sibling spacing that could use gap
LYT-001: Every page descriptor with UI components SHOULD have a layout_requirements section. Pages without layout_requirements skip FDD4.UI.VERIFY with a warning — spatial bugs go undetected.
Verification: Check page descriptor for layout_requirements section during FDD2.PAGE
LYT-002: Layout requirements MUST use content-based selectors (text content, alt attributes, tag hierarchy) as primary strategy. Svelte scopes CSS classes with hash suffixes, making exact class selectors unreliable. Use [class*="partial"] only as fallback.
Verification: Check selectors in layout_requirements for exact class matches
LYT-003: Alignment constraints (eq) use 2px tolerance by default. Spacing constraints (gap) use 4px tolerance. Explicit tolerance overrides defaults. Tolerance of 0px is valid for pixel-perfect requirements.
Verification: Check tolerance values are reasonable — sub-pixel (< 1px) is too tight, > 8px is too loose
LYT-004: FDD4.UI.VERIFY MUST use evaluate_script with getBoundingClientRect() for spatial assertions. Screenshots are evidence for human review, not measurement tools. JSON coordinates are the source of truth.
Verification: VERIFY step output must contain JSON measurements, not screenshot-only evidence
LYT-005: If layout requirements fail after 3 fix iterations, escalate to user with measurement evidence. Infinite fix loops waste context. The JSON diff between expected and actual is the escalation artifact.
Verification: Count fix iterations in VERIFY step — escalate at 3
LYT-006: When layout_requirements include a breakpoint field, VERIFY must resize the viewport (resize_page) and re-measure at that width. Common breakpoints — mobile (375px), tablet (768px), desktop (1024px).
Verification: Check for breakpoint fields in layout_requirements and verify measurements at each
LYT-007: Every page SHOULD have at least one contain constraint checking that content does not overflow its container (scrollWidth <= clientWidth). Horizontal overflow is the most common undetected layout bug.
Verification: Check for at least one contain constraint in layout_requirements
LYT-008: Layout requirement IDs (LR-NNN) must be unique within a page descriptor. IDs are referenced in verification reports for traceability.
Verification: Check for duplicate LR-NNN IDs in layout_requirements