Skip to content

Instantly share code, notes, and snippets.

@iinfin
Created January 20, 2026 09:33
Show Gist options
  • Select an option

  • Save iinfin/0c45586545358d996d111c776667d1b2 to your computer and use it in GitHub Desktop.

Select an option

Save iinfin/0c45586545358d996d111c776667d1b2 to your computer and use it in GitHub Desktop.

UI Agent Guidelines

Opinionated constraints for building accessible, fast, delightful interfaces. Use MUST/SHOULD/NEVER to guide decisions.

Stack

  • MUST use Tailwind CSS defaults unless custom values exist or are explicitly requested
  • MUST use cn utility (clsx + tailwind-merge) for class logic
  • MUST use motion/react when JavaScript animation is required
  • SHOULD use tw-animate-css for entrance and micro-animations
  • SHOULD prefer Rust-based or modern tooling where available

Components

  • MUST use accessible primitives for keyboard/focus behavior (Base UI, React Aria, Radix)
  • MUST use project's existing component primitives first
  • SHOULD prefer Base UI for new primitives if stack-compatible
  • NEVER mix primitive systems within the same interaction surface
  • NEVER rebuild keyboard or focus behavior by hand unless explicitly requested

Keyboard & Focus

  • MUST implement full keyboard support per WAI-ARIA APG
  • MUST show visible focus rings via :focus-visible; group with :focus-within
  • MUST manage focus (trap, move, return) per APG patterns
  • MUST make focusable elements in sequential lists navigable with ↑↓ keys
  • MUST make focusable elements in sequential lists deletable with ⌘+Backspace
  • SHOULD use box-shadow for focus rings (respects border-radius unlike outline)
  • NEVER use outline: none without visible focus replacement
  • NEVER use positive tabIndex values (disrupts natural tab order)

Targets & Input

  • MUST ensure hit targets ≥24px (mobile ≥44px); if visual <24px, expand hit area
  • MUST set mobile <input> font-size ≥16px to prevent iOS zoom on focus
  • MUST use touch-action: manipulation to prevent double-tap zoom
  • MUST disable touch-action for custom pan/zoom gestures to prevent native interference
  • SHOULD set -webkit-tap-highlight-color to match design; always provide alternative
  • SHOULD avoid autofocus on mobile (opens keyboard, covers screen)
  • NEVER disable browser zoom (user-scalable=no, maximum-scale=1)

Forms

  • MUST wrap inputs with <form> to enable Enter submission
  • MUST use appropriate type (password, email, etc.) and inputmode
  • MUST use meaningful name attributes with correct autocomplete values
  • MUST keep inputs hydration-safe (no lost focus/value); use defaultValue for uncontrolled
  • MUST allow incomplete form submission to surface validation errors
  • MUST show errors inline next to fields; on submit, focus first error
  • MUST keep submit enabled until request starts; then disable with spinner
  • MUST show spinner on loading buttons while preserving original label
  • MUST warn on unsaved changes before navigation
  • MUST ensure compatibility with password managers and 2FA; allow pasting codes
  • MUST trim values to handle trailing spaces from text expansion
  • MUST ensure no dead zones on checkboxes/radios; label+control share one hit target
  • MUST position prefix/suffix decorations absolutely over input with padding, triggering focus
  • SHOULD disable spellcheck for emails, codes, usernames
  • SHOULD end placeholders with and show example pattern
  • SHOULD prefer uncontrolled inputs; controlled inputs must be cheap per keystroke
  • NEVER block paste in <input> or <textarea>

State & Navigation

  • MUST reflect state in URL (deep-link filters, tabs, pagination, expanded panels)
  • MUST restore scroll position on Back/Forward
  • MUST use <a>/<Link> for navigation (supports Cmd/Ctrl/middle-click)
  • MUST perform auth redirects server-side before client loads to avoid janky URL changes
  • NEVER use <div onClick> for navigation

Feedback

  • MUST use AlertDialog for destructive or irreversible actions; or provide Undo window
  • MUST use polite aria-live for toasts and inline validation
  • MUST display feedback relative to trigger (inline checkmark on copy, highlight input on error)
  • SHOULD use optimistic UI; reconcile on response; rollback on failure or offer Undo
  • SHOULD use ellipsis () for options opening follow-ups ("Rename…") and loading states ("Loading…")

Touch & Drag

  • MUST use generous targets with clear affordances; avoid finicky interactions
  • MUST use overscroll-behavior: contain in modals/drawers
  • MUST disable text selection during drag; set inert on dragged elements
  • MUST delay first tooltip; subsequent peers show instantly
  • MUST apply muted and playsinline to <video> for iOS autoplay
  • SHOULD use @media (hover: hover) to prevent hover states flashing on touch press

Animation

  • MUST honor prefers-reduced-motion (provide reduced variant or disable)
  • MUST animate only compositor-friendly props (transform, opacity)
  • MUST ensure animations are interruptible and input-driven (no autoplay)
  • MUST set correct transform-origin (motion starts where it physically should)
  • MUST pause looping animations when off-screen (offload CPU/GPU)
  • MUST use SVG transforms on <g> wrapper with transform-box: fill-box
  • SHOULD prefer CSS > Web Animations API > JS libraries
  • SHOULD use ease-out on entrance
  • SHOULD choose easing to match the change (size, distance, trigger)
  • SHOULD animate only to clarify cause/effect or add deliberate delight
  • SHOULD use scroll-behavior: smooth for in-page anchors with appropriate offset
  • NEVER add animation unless explicitly requested
  • NEVER animate layout props (top, left, width, height, margin, padding)
  • NEVER exceed 200ms for interaction feedback
  • NEVER use transition: all—list properties explicitly
  • NEVER introduce custom easing curves unless explicitly requested
  • NEVER animate large images, full-screen surfaces, or large blur()/backdrop-filter

Layout

  • MUST use deliberate alignment to grid/baseline/edges—no accidental placement
  • MUST verify layouts on mobile, laptop, and ultra-wide (simulate at 50% zoom)
  • MUST respect safe areas via env(safe-area-inset-*) for fixed elements
  • MUST avoid unwanted scrollbars; fix overflows
  • MUST use a fixed z-index scale (no arbitrary z-* values)
  • SHOULD use optical alignment; adjust ±1px when perception beats geometry
  • SHOULD balance icon/text lockups (weight, size, spacing, color)
  • SHOULD use flex/grid over JS measurement for layout
  • SHOULD use size-* for square elements instead of w-* + h-*
  • NEVER use h-screen; use h-dvh

Typography

  • MUST apply -webkit-font-smoothing: antialiased for legibility
  • MUST apply text-rendering: optimizeLegibility
  • MUST use font-variant-numeric: tabular-nums for number comparisons, tables, timers
  • MUST prevent iOS landscape text resizing with -webkit-text-size-adjust: 100%
  • MUST subset fonts based on content, alphabet, or relevant languages
  • MUST use text-balance for headings; text-pretty for body/paragraphs
  • SHOULD use truncate or line-clamp-* for dense UI
  • SHOULD use fluid sizing via clamp() for responsive headings
  • SHOULD use medium headings with font-weight 500–600
  • NEVER use font weights below 400
  • NEVER change font weight on hover/selected state (causes layout shift)
  • NEVER modify letter-spacing (tracking-*) unless explicitly requested

Content & Accessibility

  • MUST ensure <title> matches current context
  • MUST add scroll-margin-top on headings; include "Skip to content" link; use hierarchical <h1><h6>
  • MUST provide accessible names even when visuals omit labels
  • MUST add aria-label to icon-only buttons and interactive elements
  • MUST use accurate aria-label; mark decorative elements with aria-hidden
  • MUST use redundant status cues (not color-only); icons should have text labels
  • MUST use character (not ...)
  • MUST use non-breaking spaces: 10&nbsp;MB, ⌘&nbsp;K, brand names
  • MUST render images with <img> for screen readers and right-click copy
  • MUST design resilient layouts for user-generated content (short, average, very long)
  • MUST use locale-aware formatting (Intl.DateTimeFormat, Intl.NumberFormat)
  • MUST prefer native semantics (button, a, label, table) before ARIA
  • MUST design empty, sparse, dense, and error states—no dead ends
  • MUST match skeletons to final content layout to avoid CLS
  • SHOULD use inline help first; tooltips as last resort
  • SHOULD use curly quotes (" "); avoid widows/orphans with text-wrap: balance
  • SHOULD give illustrations built with HTML an explicit aria-label
  • SHOULD unset gradient on ::selection for gradient text

Content Handling

  • MUST handle long content in text containers (truncate, line-clamp-*, break-words)
  • MUST add min-w-0 to flex children to allow truncation
  • MUST handle empty states gracefully—no broken UI for empty strings/arrays

Performance

  • MUST track and minimize re-renders (React DevTools, React Scan)
  • MUST profile with CPU/network throttling
  • MUST batch layout reads/writes; avoid reflows/repaints
  • MUST target <500ms for mutations (POST/PATCH/DELETE)
  • MUST virtualize large lists (>50 items)
  • MUST preload above-fold images; lazy-load the rest
  • MUST prevent CLS with explicit image dimensions
  • MUST measure reliably (disable extensions that skew runtime)
  • MUST pause or unmount off-screen auto-playing videos on iOS
  • SHOULD test on iOS Low Power Mode and macOS Safari
  • SHOULD use <link rel="preconnect"> for CDN domains
  • SHOULD preload critical fonts with <link rel="preload" as="font"> and font-display: swap
  • SHOULD bypass React render lifecycle with refs for real-time DOM updates
  • SHOULD adapt to user's hardware/network capabilities
  • NEVER apply will-change outside an active animation
  • NEVER use useEffect for anything expressible as render logic

Dark Mode & Theming

  • MUST set color-scheme: dark on <html> for dark themes
  • MUST set explicit background-color and color on native <select> (Windows fix)
  • MUST prevent theme switching from triggering unintended transitions
  • SHOULD set <meta name="theme-color"> to match page background
  • SHOULD use SVG favicon with prefers-color-scheme style tag

Hydration

  • MUST pair inputs with value with onChange (or use defaultValue)
  • SHOULD guard date/time rendering against hydration mismatch

Design

  • MUST style document selection with ::selection
  • MUST use accessible chart palettes (color-blind-friendly)
  • MUST meet contrast—prefer APCA over WCAG 2
  • MUST increase contrast on :hover/:active/:focus
  • MUST ensure toggles take immediate effect without confirmation
  • MUST disable user-select on inner content of interactive elements
  • MUST disable pointer-events on decorative elements (glows, gradients)
  • MUST ensure interactive elements in lists have no dead areas—increase padding
  • MUST ensure anything that looks clickable is clickable
  • MUST give empty states one clear next action with optional templates
  • SHOULD use optimistic updates; roll back on server error with feedback
  • SHOULD use layered shadows (ambient + direct)
  • SHOULD use semi-transparent borders + shadows for crisp edges
  • SHOULD ensure nested radii: child ≤ parent; concentric
  • SHOULD tint borders/shadows/text toward background hue for consistency
  • SHOULD match browser UI to background
  • SHOULD limit accent color to one per view
  • SHOULD use existing theme or Tailwind color tokens before introducing new ones
  • SHOULD use nested menus with "prediction cone" to prevent accidental closing
  • NEVER use gradients unless explicitly requested
  • NEVER use purple or multicolor gradients
  • NEVER use glow effects as primary affordances
  • NEVER use dark gradient colors that cause banding (use background images instead)

References

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment