Skip to main content
Version: 1.6.1 Last Updated: 2026-04-18 Status: Active Target Audience: Developers and AI agents (GitHub Copilot, Cursor, general AI assistants)
Constitution Reference: See constitution.md (current: see docs/VERSIONS.md) §6.7 [UI/UX Guardrails] for non-negotiable requirements. Quick Reference: See root AGENTS.md (current: see docs/VERSIONS.md) §[Quick Reference] for condensed guidelines. Color Utilities: See docs/development/SEMANTIC_COLORS.md (current: see docs/VERSIONS.md) for color mapping patterns. Spacing Patterns: See SPACING_AND_UX_STANDARDS.md (current: see docs/VERSIONS.md) for detailed spacing patterns and responsive design guidelines. Tab Patterns: See .cursor/rules/tab-patterns.md for the canonical tab variant decision table, codemod recipes, and accessibility rules.

AI agents: Use semantic tokens, skeleton loaders (never return null), responsive spacing, and @/shared/ui components. Full pattern list: AGENTS.md, .cursor/rules/quick-reference.mdc.

Design Token Governance

The canonical token source for app/runtime parity is governed by: This policy defines:
  • source-of-truth files and ownership,
  • hard-locked vs app-extensible token families,
  • backward-compat alias expectations,
  • required visual and validation checks before merge.
All token changes in src/index.css must follow this policy.

Quick Reference

Common Patterns

Semantic Color Tokens:
Component Variants:
  • success - Positive states (active, approved, complete)
  • warning - Caution states (pending, at-risk, expiring)
  • destructive - Error/danger states (rejected, error, critical)
  • info - Informational states (in progress, new)
  • secondary - Neutral/inactive states
Loading States:
Spacing: Use responsive spacing; see SPACING_AND_UX_STANDARDS.md for patterns and breakpoints.

Token Usage

Colors: Always use semantic tokens (success, warning, destructive, primary, muted)
Spacing: Use responsive spacing (space-y-4 md:space-y-6)
Typography: Use font utilities (font-sans, font-serif, font-mono)
Components: Use variant props (variant="success", variant="destructive")

Empty States

For list or content areas with no data (e.g. hub tab content, pipeline stages, credential renewals), use the shared EmptyState component from @/shared/components/EmptyState with a title, optional description, and primary CTA (e.g. “Add credential”, “Create job posting”). Do not leave content areas blank; every empty list or empty tab view must show an explicit empty state. Avoid custom empty layouts (custom Card + icon + text + button); use a thin wrapper around EmptyState when you need domain-specific copy or branching. See constitution §6.7 (widgets handle empty states).

Overlay naming

Use *Dialog or *Sheet in component names for overlay surfaces (e.g. ClaimGenerateDialog, MobileMenuSheet). Do not use Modal in names; the implementation should use Dialog or Sheet from @/shared/ui.

N/A and Zero States

Where a KPI or count is not applicable (e.g. no data yet, metric does not apply), display ”—” or “N/A” with an optional tooltip explaining why, instead of “0” or a blank value, to avoid implying incorrect data.

Hub Entry Copy (Optional)

Hub pages MAY include a one-line “How to use” under the hub title (e.g. “Use the tabs below to switch between Report, Renewals, and My Credentials”) for first-time users. If used, keep the pattern consistent across hubs. For detailed patterns and examples, see sections below.

Table of Contents

  1. Design System
  2. Button Usage
  3. Tabs
  4. Page Templates
  5. Navigation & Information Architecture
  6. Mobile-First Requirements
  7. Performance UX
  8. Accessibility
  9. Related Documentation

Design System

Token System

The platform uses a token-based design system with HSL CSS variables mapped to Tailwind utilities using Tailwind CSS v4 CSS-first configuration. All colors, spacing, typography, and other design tokens are defined in src/index.css. Implementation References:
  • CSS Variables + token mappings: src/index.css (@theme inline, :root, .dark)

Colors

Semantic Colors

Required: Always use semantic color tokens, never hardcoded Tailwind color classes. Available Semantic Colors:
  • primary / primary-foreground - Primary brand color (deep navy chrome accent; not the main CTA — use accent)
  • secondary / secondary-foreground - Secondary actions (note: bg-secondary is aliased to --card-elevated)
  • accent / accent-foreground - Brand CTA color (teal); pair with Button variant="accent" for the main action
  • destructive / destructive-foreground - Errors, destructive actions
  • success / success-foreground - Success states, positive feedback
  • warning / warning-foreground - Warnings, caution states
  • info / info-foreground - Informational states (a distinct blue, not a primary tint); pair bg-info with text-info-foreground for proper dark-mode contrast
  • muted / muted-foreground - Muted text, disabled states
  • card / card-foreground - Card backgrounds
  • popover / popover-foreground - Popover/dropdown backgrounds
Full token catalog: See SEMANTIC_COLORS.md § Token Catalog Reference for the complete inventory, including subtle variants, hover tokens, sidebar slot, special tokens (overlay-scrim, ai-accent, glass, shimmer-highlight), and the Encore brand reserved set.
Usage Examples:
Anti-Patterns:
  • ❌ Using text-green-500, bg-red-100, etc. (hardcoded Tailwind colors)
  • ❌ Using text-[#FF0000] (arbitrary color values)
  • ✅ Use semantic tokens: text-success, bg-destructive/10, etc.

Status Colors

Status indicators should use semantic color variants:
  • Success: success token (green) - Completed, approved, published
  • Warning: warning token (yellow/orange) - Pending, needs attention
  • Error: destructive token (red) - Failed, rejected, errors
  • Info: info token (blue) - Informational messages, in-progress states. A distinct semantic state — not a primary tint. Pair bg-info with text-info-foreground (a dedicated, mode-aware token); never with text-primary-foreground (which fails WCAG in dark mode).
  • Neutral: muted token (gray) - Default, inactive states

Module Identity Colors

Each domain core has a dedicated identity color (--module-{short-code} plus -hover and -subtle variants). Use them for navigation accents, dashboard widget headers, and module badges — never for status/state.
See SEMANTIC_COLORS.md § Module Identity Tokens for the full short-code list (12 active + 4 reserved + 4 deprecated aliases) and recommended usage patterns.

Chart Palette

Chart configs MUST use hsl(var(--chart-N)) (N = 1–8) for series colors — never hex literals or hardcoded HSL strings — so charts adapt to dark mode and respect tenant rebranding.
See SEMANTIC_COLORS.md § Chart Palette for the full 8-color palette, the colorblind-safety note, and ChartContainer integration guidance.

Typography

Font Families:
  • Display/Headings: Space Grotesk - font-heading
  • Body/Sans: Inter (primary UI font) - font-sans
  • Serif alias: Inter fallback contract (matches body token) - font-serif
  • Mono: DM Mono (for code, data) - font-mono
Font Loading:
  • Fonts are loaded asynchronously in index.html (non-blocking); do not add @import for fonts in CSS.
  • Critical inline faces preload Inter and Space Grotesk for first paint parity with design system.
  • System font fallbacks are defined in CSS variables.
Usage:
  • Default: font-sans (Inter) for all body/UI text.
  • Headings: font-heading (Space Grotesk) for page titles and heading hierarchy.
  • Mono: font-mono (DM Mono) for code blocks, data tables, and technical strings.
  • Tenant overrides:
    • --tenant-font-family overrides body typography,
    • --tenant-font-heading overrides heading typography. If unset, the canonical Inter + Space Grotesk contract applies.

Spacing

Spacing scale, container padding, and responsive patterns are defined in SPACING_AND_UX_STANDARDS.md. Use that doc for breakpoints, card padding, and form layout.

Button Usage

Use the shared Button from @/shared/ui/button with semantic variants (default, accent, destructive, outline, secondary, ghost, link, success, warning, soft, glass, ai) and sizes (default, sm, xs, lg, icon). Primary CTA (important): The default variant is a neutral elevated control (card surface + border), not the brand primary. For the main action on a page, form, or dialog footer, use variant="accent" (teal / brand accent) unless a domain pattern explicitly calls for success / destructive / warning. Icon-only buttons must have aria-label. Prefer the loading prop for async actions. Dialog footers: Cancel left, primary right. Full guidance: BUTTON_USAGE.md.

Icons

Source: Use lucide-react only. Do not use heroicons, react-icons, or custom SVG icon sets. Size scale: Use the design token scale for consistency. CSS variables are in src/index.css (--icon-xs--icon-lg); use the equivalent Tailwind classes when applying to icons: Decorative only: For large hero or empty-state icons use h-8 w-8 (32px) or h-12 w-12 (48px). These are not part of the standard token scale and should only be used for decorative/empty-state contexts. Icon-only buttons: Use size="icon-touch" on Button for icon-only actions so the tap target is at least 44×44px (accessibility). Use size="icon" only when the button is not the primary touch target (e.g. inside a larger clickable area). Nav and lists: Standardize nav item icons to one size (e.g. icon-md / h-5 w-5). Use icon-sm for compact lists and data tables. Color: Use semantic tokens for icon color (e.g. text-muted-foreground, text-primary, text-success). See SEMANTIC_COLORS.md. AppIcon wrapper (available now, preferred for shared code): Use AppIcon from @/shared/ui/icon for shared/reusable components. It bakes in the platform stroke width (1.75), the size scale (xs/sm/md/lg), and the decorative/aria-label accessibility contract:
For accent tones: use tone="accent" on subtle accent surfaces (bg-accent/10, bg-accent/20) and reserve tone="onAccent" for solid accent chips (bg-accent). Tailwind size classes (see table above) remain acceptable for trivial one-off icons inside leaf components. See ICON_GUIDE.md § AppIcon wrapper for the full prop table. For which icon to use, sizing, import patterns, and accessibility, see ICON_GUIDE.md.

Component Variants

Badge Variants

Available Variants:
  • default - Primary actions, highlights
  • secondary - Secondary information
  • destructive - Errors, deletions
  • outline - Subtle indicators
  • success - Success states
  • warning - Warning states
  • info - Informational states (in progress, new)
  • Light variants: success-light, warning-light, destructive-light, primary-light
  • Specialty: accentGlow, glass, ai (non-status use cases)
Usage:
See SEMANTIC_COLORS.md for complete variant reference and status mapping patterns.

Button Variants

Available Variants:
  • default - Neutral elevated (card-like surface); use for secondary emphasis, not the single main CTA
  • accent - Brand primary CTA (accent teal); default choice for “Save”, “Submit”, “Continue” when one action dominates
  • destructive - Destructive actions
  • outline - Secondary actions, toolbars
  • secondary - Tertiary / muted actions
  • ghost - Subtle actions
  • link - Link-style buttons
  • success, warning, soft, glass, ai - See JSDoc on src/shared/ui/button.tsx
Sizes:
  • default - h-10 px-4 py-2
  • sm - h-9 px-3
  • lg - h-11 px-8
  • icon / icon-touch - h-11 w-11 min 44×44px (square icon buttons; use icon-touch for icon-only actions to meet touch target size)
Note (2026-04-18 audit): The Button size tokens shipped in code currently differ slightly from the table above (default h-9, sm h-8, lg h-10, icon size-11, plus an undocumented xs h-7). Reconciliation is tracked in reports/ux/ux-implementation-plan-20260418.md § Phase 1.2. Until that PR lands, prefer Button size="default" and trust the variant defaults rather than asserting on the height.

Tabs

Canonical rule file: .cursor/rules/tab-patterns.md. Source primitives: @/shared/ui/tabs (Tabs, TabsList, TabsTrigger, TabsContent, TabBadge), @/shared/components/ScrollableTabsList, @/platform/tabs/useTabUrlState, @/platform/settings/components/SettingsTabs.

Variant decision table

What AI must NEVER do (tabs)

  • <TabsList className="grid w-full grid-cols-N"> — equal-width cells break at narrow viewports, no scroll affordance. Wrap with ScrollableTabsList tabsListClassName="sm:grid sm:grid-cols-N".
  • <TabsList className="flex-wrap h-auto"> — wrapping creates uneven heights and breaks the page’s vertical rhythm. Use ScrollableTabsList and let it scroll horizontally instead.
  • ❌ Inline h-N overrides on <TabsList> (h-8, h-10, h-11). Use size="compact" only when you need denser controls (e.g. inside a card).
  • ❌ Inline (${count}) in trigger labels. Use <TabBadge count={n} /> for the canonical pill style and the aria-label="${count} items" accessibility contract.
  • <TabsList> without aria-label (or aria-labelledby). Pass an explicit label when there is no visible heading.
  • ❌ Mobile collapse to icon-only that drops the visible label. At <sm, tabs MUST scroll horizontally OR collapse into a Sheet picker.

Quick recipes

URL-synced hub (deep-link friendly, refresh-stable):
Settings page — use SettingsTabs:
Tab badges — never inline (${count}):
Migration guidance: see reports/ux/tabs-consistency-audit-20260418.md (full inventory + before/after recipes) and reports/ux/ux-implementation-plan-20260418.md § Phase 3 for the rollout plan and codemods.

Page Templates

Required Page States

Every page MUST implement these states:

1. Loading State

Requirement: Use skeleton loaders, never return null.
Skeleton Patterns by Page Type:
  • List Pages: Grid of skeleton cards/rows
  • Detail Pages: Skeleton header + content sections
  • Dashboards: Skeleton stat cards + chart placeholders
  • Forms: Skeleton form fields
Implementation:
  • Route-level: RouteLoadingSkeleton (already implemented)
  • Component-level: Use Skeleton component from @/shared/ui/skeleton

2. Error State

Requirement: Show user-friendly error messages with recovery actions.
Error State Components:
  • Use Alert component with destructive variant
  • Include retry/refresh action when applicable
  • Show specific error message (not generic “Something went wrong”)

3. Empty State

Requirement: Show helpful empty states for lists and detail pages.
Empty State Guidelines:
  • Include icon, title, description, and primary action
  • Use friendly, helpful copy
  • Provide clear next steps

Page Template Primitives

Available Components:
  • PageContainer - Responsive page wrapper with padding (src/shared/components/PageContainer.tsx)
  • OverviewPageWrapper - Standardized wrapper for module overview pages with pull-to-refresh (src/shared/components/OverviewPageWrapper.tsx)
  • ResponsiveFormLayout - Form field layout with responsive columns (src/shared/components/ResponsiveFormLayout.tsx)
  • MobileTableWrapper - Responsive table wrapper for mobile (src/shared/components/MobileTableWrapper.tsx)
Page Structure:

Overview Pages

Use OverviewPageWrapper for all module overview/landing pages:
Features:
  • Automatic mobile pull-to-refresh with query invalidation
  • Consistent responsive padding and spacing
  • Standardized page structure
When to Use Which Template:
  • Overview Pages: OverviewPageWrapper for module landing pages
  • List Pages: PageContainer + Card for filters + table/list
  • Detail Pages: PageContainer + Card sections for different data groups
  • Dashboards: PageContainer + grid of stat cards + widgets
  • Forms: PageContainer + ResponsiveFormLayout for form fields

Route Taxonomy

Current Standard: Flat route structure with nested sub-modules where appropriate. Route Patterns:
  • Module Overview: /{module}/dashboard (e.g., /hr/dashboard)
  • Module List: /{module}/{entity} (e.g., /hr/employees)
  • Module Detail: /{module}/{entity}/:id (e.g., /hr/employees/123)
  • Sub-Module: /{module}/{sub-module}/{entity} (e.g., /hr/ats/jobs)
Route Naming:
  • Use kebab-case: /hr/employee-directory not /hr/employeeDirectory
  • Use plural nouns for list routes: /hr/employees not /hr/employee
  • Use singular for detail routes when appropriate: /hr/employees/:id
When to Show:
  • Detail pages (3+ route segments)
  • Nested sub-module pages
  • Settings pages with hierarchy
When NOT to Show:
  • Module overview pages (/hr/dashboard)
  • Top-level list pages (/hr/employees)
  • Home/dashboard (/)
Breadcrumb Standards:
  • Desktop: Full breadcrumb trail in header (via Breadcrumbs.tsx)
  • Mobile: Truncated to last 2 segments (via MobileBreadcrumbs.tsx)
  • Depth Limit: Maximum 5 segments (truncate if deeper)
Implementation:
  • Auto-generated from route (via route-labels.ts)
  • Explicit breadcrumbs via PageContainer breadcrumbs prop for dynamic entity names

Module/Sub-Module Navigation

Navigation Modes:
  1. All Modules: Shows all available modules (home, module switcher)
  2. In Module: Shows module-specific navigation (sidebar with nav items)
  3. In Sub-Module: Shows sub-module navigation with “Back to Parent” button
Sub-Module Pattern:
  • Sub-modules have overviewRoute (e.g., /hr/ats)
  • Sub-modules have navGroups or navItems for sub-navigation
  • Sub-modules show context indicator in mobile header
Permission Filtering:
  • Navigation items filtered by minRole (V1) or permission (V2)
  • Use useFilteredNavItems hook for permission-aware filtering
Related Documentation:
  • docs/architecture/standards/NAVIGATION_STANDARD.md - Navigation patterns and policies
  • docs/development/mobile-navigation-guide.md - Mobile navigation patterns
  • docs/development/breadcrumb-implementation-guide.md - Breadcrumb implementation
  • src/platform/navigation/README.md - Navigation component documentation

Mobile-First Requirements

Touch Targets

Requirement: All interactive elements MUST meet minimum touch target size. Minimum Size: 44x44px (iOS HIG, Material Design) Implementation:
Common Patterns:
  • Navigation items: min-h-[44px]
  • Icon buttons: h-10 w-10 (40px + padding = 44px+)
  • Bottom nav items: min-w-[56px] min-h-[56px] (generous for thumb reach)

Safe Areas

Requirement: Fixed elements (header, bottom nav) MUST respect safe area insets. Implementation:
CSS Variables:
  • --safe-area-inset-top - iOS notch, Android status bar
  • --safe-area-inset-bottom - iOS home indicator, Android nav bar
  • --safe-area-inset-left / --safe-area-inset-right - Landscape safe areas
Utility Classes:
  • .safe-area-top - Adds top padding
  • .safe-area-bottom - Adds bottom padding
  • .safe-area-left / .safe-area-right - Adds side padding

Mobile Header Density

Requirement: Minimize vertical space consumed by mobile header chrome. Current Structure:
  1. Main header (64px) - Logo, menu, actions
  2. Org switcher (48px) - Organization/site selector
  3. Sub-module indicator (~40px, conditional) - Current sub-module context
  4. Breadcrumbs (~40px) - Navigation path
Guidelines:
  • Collapse When Possible: Combine org switcher into header row when space allows
  • Conditional Display: Show breadcrumbs only on 3+ depth routes
  • Sub-Module Indicator: Show only when in sub-module context
Target: Reduce total header height to < 120px on most pages.

Bottom Navigation

Requirement: Bottom nav provides quick access to key modules. Implementation:
  • Fixed bottom position with safe area inset
  • 4-5 primary shortcuts (configurable per user/role)
  • Long-press for module menu (if implemented)
  • Active state indicator
Touch Targets:
  • Minimum 56x56px per nav item (generous for thumb reach)
  • Adequate spacing between items
Related Documentation:
  • docs/development/mobile-navigation-guide.md - Complete mobile navigation guide

Performance UX

Skeleton Loaders

Requirement: All loading states MUST use skeleton loaders, never return null. Skeleton Standards by Page Type:

List Pages

Detail Pages

Dashboards

Route Code Splitting

Requirement: All route components MUST use React.lazy() for code splitting. Implementation:
Suspense Boundaries:

QueryClient Configuration

Requirement: QueryClient MUST have default staleTime and gcTime configured. Configuration:

Performance Targets

Lighthouse Scores:
  • Performance: 85+ (target: 90+)
  • Accessibility: 95+ (target: 100)
  • Best Practices: 90+ (target: 95+)
  • SEO: 90+ (target: 95+)
Core Web Vitals:
  • First Contentful Paint (FCP): < 2s
  • Largest Contentful Paint (LCP): < 2.5s
  • Cumulative Layout Shift (CLS): < 0.1
  • Time to Interactive (TTI): < 3.5s (on 3G)
Related Documentation:
  • constitution.md §[Performance Requirements] - Non-negotiable performance rules

Form UX Standards

Form Layout

Requirement: Use ResponsiveFormLayout for all multi-field forms. Import:
Usage:
Column Guidelines: Spacing:

Shared Field Pilot

Use Field, FieldGroup, FieldLabel, FieldDescription, and FieldError from @/shared/ui/field for simple controlled-field layouts that otherwise repeat label/helper/error markup outside React Hook Form.
Keep FormField / FormItem for React Hook Form validation flows. The field pilot is additive and should not replace mature form abstractions unless it reduces duplicated composition without changing behavior.

Help Text (FormDescription)

Always use for:
  • Sensitive data fields (account numbers, passwords)
  • Masked/transformed displays (“Will be masked as XXX-7890”)
  • Optional fields with non-obvious purpose
  • Fields requiring specific formats
Skip for:
  • Self-explanatory labels (Name, Email, Phone)
  • Required fields with obvious purpose
Example:

Error Presentation

Standard: Inline errors only via FormMessage component. Guidelines:
  • Errors appear immediately below the field
  • Use FormMessage component (styled with text-destructive)
  • No error summary at form top (too noisy for most forms)
  • For complex forms with many errors, consider scrolling to first error
Example:

Exception: Clinical (CL) forms

Clinical forms often have many required fields and accessibility requirements that benefit from a summary view. Use ClinicalFormErrorSummary at the top of CL forms to give keyboard and screen-reader users a single place to review and jump to invalid fields. Use when:
  • The form has more than a few required fields.
  • Clinicians submit it under time pressure and need to triage errors quickly.
  • You need WCAG 2.1 AA-compliant focus management after submission.
Behavior:
  • Renders nothing when there are no errors.
  • Announces error count via an aria-live="polite" alert.
  • Each entry is a focus link that calls setFocus from react-hook-form to move the caret to the invalid field.
Example:
Props:

Progressive Disclosure

Use when: Form has advanced/optional sections that most users don’t need. Pattern: Accordion or Collapsible for “Advanced Options”
Default state: Collapsed (don’t overwhelm users with options)

Form Actions

Standard layout:
Guidelines:
  • Right-aligned buttons (LTR layouts)
  • Cancel on left, Submit on right
  • Show loading state during submission
  • Disable buttons during submission
  • Use descriptive labels: “Create Account” vs “Submit”

Accessibility

Tabbed Hub Accessibility Checklist

For pages that use tabbed hubs (e.g. HR Recruiting, Credentialing, Workforce Analytics, Clinical Oversight):
  • Tab panels have correct ARIA: role="tabpanel" and aria-labelledby linking to the corresponding trigger (Radix/shadcn Tabs provide these by default).
  • Focus moves to the active panel on tab change when appropriate (Radix handles this).
  • Keyboard navigation: arrow keys move between tabs (Radix/shadcn support this).
  • Touch targets: tab triggers meet 44px minimum height (see src/shared/ui/tabs.tsx).
  • Hub routes are in scope for accessibility audits (e.g. axe-core); see ACCESSIBILITY_TESTING.md.

Keyboard Navigation

Requirement: All interactive elements MUST be keyboard accessible. Implementation:
  • Use semantic HTML (<button>, <a>, <input>, etc.)
  • Maintain logical tab order
  • Provide skip links for main content
  • Support arrow key navigation for lists/menus
Focus Management:
  • Visible focus indicators on all interactive elements
  • Focus trap in modals/dialogs
  • Return focus to trigger after closing modal

Focus Indicators

Requirement: All interactive elements MUST have visible focus indicators. Implementation:
Standards:
  • 2px solid outline
  • Use --ring color token
  • 2px offset from element
  • Only visible on keyboard focus (:focus-visible)

ARIA Usage

Requirement: Use ARIA attributes when semantic HTML is insufficient. Common Patterns: Labels:
Roles:
Live Regions:
  • aria-live="polite" - Non-urgent updates
  • aria-live="assertive" - Urgent updates
  • aria-atomic="true" - Announce entire region

Contrast Requirements

Requirement: All text MUST meet WCAG AA contrast ratios. Minimum Contrast:
  • Normal Text: 4.5:1 against background
  • Large Text (18pt+): 3:1 against background
  • UI Components: 3:1 for non-text elements (icons, borders)
Testing:
  • Use browser DevTools contrast checker
  • Use tools like WebAIM Contrast Checker
  • Test in both light and dark themes
Related Documentation:

Deep Dives

Implementation References

  • Design Tokens: src/index.css - CSS variable definitions
  • Tailwind token mapping: src/index.css (@theme inline) - Token mappings
  • Shared Components: src/shared/components/ - Page templates and layouts
  • UI Components: src/shared/ui/ - Base UI component library
  • Navigation Components: src/platform/navigation/ - Navigation system

Constitution & Guidelines

  • Constitution: constitution.md - Non-negotiable engineering guardrails
  • AI Guide: AI_GUIDE.md - AI contribution guidelines
  • Quick Reference: AGENTS.md - Condensed cross-platform reference

Shared Component Primitives

Location: src/shared/components/PageHeader.tsx Purpose: Standardized page header with title, description, and actions. Usage:
Props:
  • title (required) - Page title (h1)
  • description (optional) - Page subtitle
  • actions (optional) - Action buttons/components
  • breadcrumbs (optional) - Custom breadcrumb items

StatCard

Location: src/shared/components/StatCard.tsx Purpose: Standardized metric/stat card for dashboards. Usage:
Props:
  • title (required) - Metric label
  • value (required) - Metric value (number or string)
  • icon (optional) - Lucide icon component
  • trend (optional) - Trend indicator with value and direction
  • description (optional) - Additional context

EmptyState

Location: src/shared/components/EmptyState.tsx Purpose: Standardized empty state with icon, message, and action. Usage:
MUST use for all empty lists and states. Never create ad-hoc empty state UIs.

CardActionsMenu

Location: src/shared/components/CardActionsMenu.tsx Purpose: Touch-safe action menu for cards with consistent patterns. Usage:
Requirements:
  • Minimum 44x44px touch target
  • Always visible (not hover-only)
  • Uses DropdownMenu for mobile-friendly interaction

Color Mapping Utilities

Reference: See docs/development/SEMANTIC_COLORS.md for complete documentation.

Import Pattern

Status Color Mapping

Map business statuses to semantic colors:

Anti-Patterns

❌ WRONG: Hardcoded color maps
✅ CORRECT: Semantic color mapping

Touch-Safe Action Patterns

Requirements

  1. Always visible actions - Never hide actions behind hover-only states
  2. 44x44px minimum - All touch targets must meet minimum size
  3. Menu for multiple actions - Use CardActionsMenu or DropdownMenu for 2+ actions
  4. Single primary action visible - Show most common action directly, others in menu

Hover-Only Anti-Pattern

❌ WRONG: Hover-only actions
✅ CORRECT: Always visible with proper touch targets

Mobile Considerations

  • Use DropdownMenu instead of hover menus
  • Ensure action buttons are at least 44x44px
  • Place actions in consistent locations (top-right corner)
  • Use swipe gestures sparingly and always provide alternatives

Dialog vs Native Browser Dialogs

CRITICAL: Never Use Native Dialogs

Prohibited: window.prompt(), window.confirm(), window.alert() Native browser dialogs are:
  • Not accessible
  • Not stylable
  • Inconsistent across browsers
  • Not mobile-friendly
  • Cannot be canceled properly

Use Custom Dialogs Instead

For confirmations:
For text input:
For notifications/alerts:

Mobile Gestures

Spec Reference: PF-37 - Mobile Swipe Gestures All mobile interfaces should leverage gesture patterns for improved UX:

Required Gesture Patterns

Implementation

Accessibility Requirements

  • All gestures MUST have keyboard alternatives
  • Escape closes sheets/dialogs
  • Delete triggers destructive actions
  • Tap/click fallback always available
Full Documentation: See Mobile Gesture Guide

Troubleshooting Common UI/UX Issues

Colors Not Applying Correctly

Problem: Semantic color tokens not working as expected. Solution:
Check:
  1. Verify token exists in src/index.css under @theme inline and/or root CSS variables
  2. Check CSS variables in src/index.css
  3. Ensure proper variant usage for components

Loading States Showing Blank Screen

Problem: Page shows blank screen during loading. Solution:

Components Not Responsive

Problem: Components don’t adapt to mobile screens. Solution:
  1. Check for fixed widths: Remove w-{fixed} classes
  2. Use responsive utilities: md:, lg: breakpoints
  3. Verify mobile-first approach: Start with mobile, enhance for desktop
  4. Test on actual devices: Use browser dev tools mobile emulation

Accessibility Issues

Problem: Components not accessible to screen readers. Solution:

Performance Issues

Problem: Page loads slowly or feels sluggish. Solution:
  1. Check lazy loading: Ensure routes use React.lazy()
  2. Verify code splitting: Check bundle sizes
  3. Optimize images: Use appropriate formats and sizes
  4. Check query optimization: Review TanStack Query configuration
  5. See Performance Optimization Guide for detailed guidance

Design System Tokens Not Found

Problem: TypeScript errors for design system tokens. Solution:
  1. Verify token exists in src/index.css under @theme inline and/or root CSS variables
  2. Check TypeScript types: Ensure Tailwind types are generated
  3. Restart TypeScript server: Reload VS Code window
  4. Check import paths: Verify @/ alias configuration

Mobile Touch Targets Too Small

Problem: Buttons/links hard to tap on mobile. Solution:

Inconsistent Component Variants

Problem: Same component looks different in different places. Solution:
  1. Use standard variants: variant="success", variant="destructive"
  2. Avoid custom styling: Don’t override component styles
  3. Check design system: Verify variant exists in component definition
  4. Use semantic tokens: Don’t hardcode colors

Version: 1.6.1 Maintained By: Platform Foundation Team