UX Standards — 00: Overview & Foundational CSS Rules
This is the master governing document. Every other UX Standards file inherits these rules. All Claude Code instances MUST read this document before building any frontend page.
Purpose
These standards define the single authoritative UI specification for all Anshin Health Solutions frontend applications. They apply universally to:
| Application | Module | Location |
|---|---|---|
| Anshin Orchestrate | Tour booking platform | C:\projects\anshin-orchestrate\orchestrate-frontend\ |
| Accelerate — Fax Automation | Fax processing | C:\projects\accelerate\modules\fax-automation\app\web-fax-dashboard\ |
| Accelerate — Referral Automation | Referral management | C:\projects\accelerate\modules\referral-automation\web-outreach-dashboard\ |
| Accelerate — Patient Outreach | Patient communication | C:\projects\accelerate\modules\patient-outreach\ (future) |
| All future Accelerate modules | Any new module | Follow these standards from day one |
Technology Stack (Non-Negotiable)
| Layer | Technology | Version |
|---|---|---|
| Framework | Next.js (App Router) | 14/15 |
| Language | TypeScript | 5.x |
| UI Primitives | shadcn/ui (Radix UI) | latest |
| Styling | Tailwind CSS | 3.x |
| Icons | Lucide React | latest |
| Tables | TanStack React Table | v8 |
| State / Data Fetching | TanStack React Query | v5 |
| Forms | React Hook Form + Zod | latest |
| State (client) | Zustand or React Context | latest |
Never substitute these with other libraries without explicit approval.
RULE 1 — THE CSS VARIABLES MANDATE (Most Enforced Rule)
❌ NEVER use hardcoded Tailwind color classes
// ❌ WRONG — NEVER DO THIS
<div className="bg-green-50 text-green-700 border-green-200">Active</div>
<div className="bg-red-100 text-red-600">Error</div>
<div className="bg-gray-100 text-gray-500">Inactive</div>
<div className="bg-blue-500 text-white">Primary</div>
<span className="text-yellow-500">Warning</span>
✅ ALWAYS use CSS variable-based semantic tokens
// ✅ CORRECT — Always use semantic tokens
<div className="bg-primary/10 text-primary border-primary/20">Active</div>
<div className="bg-destructive/10 text-destructive">Error</div>
<div className="bg-muted text-muted-foreground">Inactive</div>
<div className="bg-primary text-primary-foreground">Primary</div>
<span className="text-yellow-500">⚠️ STILL WRONG — use text-warning or text-muted-foreground</span>
The Semantic Token Map
| Semantic Token | Use for | Light value | Dark value |
|---|---|---|---|
bg-background | Page/app background | white | dark |
bg-card | Card/panel background | white | dark card |
bg-muted | Secondary areas, inactive items | gray-100 | gray-800 |
bg-primary | Brand action color | brand green | brand green |
bg-primary/10 | Active/selected status background | light green | — |
bg-destructive | Hard error backgrounds | red | — |
bg-destructive/10 | Soft error/warning backgrounds | light red | — |
bg-accent | Hover states on non-interactive | accent | — |
bg-accent/50 | Hover on nav items | — | — |
bg-popover | Dropdown menus, popovers | white | — |
text-foreground | Primary body text | near-black | near-white |
text-muted-foreground | Secondary/caption text | gray-500 | gray-400 |
text-primary | Brand text, active link | brand green | brand green |
text-primary-foreground | Text ON a primary bg | white | white |
text-destructive | Error messages | red | — |
border-border | Default borders | gray-200 | gray-700 |
border-primary | Active/focused borders | brand green | — |
border-primary/20 | Subtle active borders | light green | — |
ring-ring | Focus rings | brand | — |
sidebar-accent | Sidebar hover background | — | — |
sidebar-primary | Sidebar active item | — | — |
sidebar-foreground | Sidebar default text | — | — |
Status State Pattern (Mandatory for all status indicators)
// Active / Success
className="bg-primary/10 border border-primary/20 text-primary"
// Inactive / Neutral
className="bg-muted border border-border text-muted-foreground"
// Error / Failed
className="bg-destructive/10 border border-destructive/20 text-destructive"
// Warning (use amber palette reference only via CSS var if defined, else muted)
className="bg-muted border border-border text-muted-foreground"
// Pending / In-progress
className="bg-muted border border-border text-muted-foreground animate-pulse"
RULE 2 — shadcn/ui Components Are Always First Choice
Before building any UI element from scratch, check if shadcn/ui already provides it.
| Needed | shadcn component | Import path |
|---|---|---|
| Button | Button | @/components/ui/button |
| Input / textarea | Input, Textarea | @/components/ui/input |
| Select dropdown | Select | @/components/ui/select |
| Modal/overlay | Dialog | @/components/ui/dialog |
| Slide-out panel | Sheet | @/components/ui/sheet |
| Tooltip | Tooltip | @/components/ui/tooltip |
| Badge/pill | Badge | @/components/ui/badge |
| Checkbox | Checkbox | @/components/ui/checkbox |
| Radio | RadioGroup | @/components/ui/radio-group |
| Switch/toggle | Switch | @/components/ui/switch |
| Table | Table | @/components/ui/table |
| Tabs | Tabs | @/components/ui/tabs |
| Dropdown menu | DropdownMenu | @/components/ui/dropdown-menu |
| Popover | Popover | @/components/ui/popover |
| Toast | Toaster + useToast | @/components/ui/toaster |
| Card | Card | @/components/ui/card |
| Separator | Separator | @/components/ui/separator |
| Avatar | Avatar | @/components/ui/avatar |
| Skeleton | Skeleton | @/components/ui/skeleton |
| Alert | Alert | @/components/ui/alert |
| Sidebar | SidebarProvider, Sidebar | @/components/ui/sidebar |
Never build a custom button, modal, input, or dropdown if shadcn provides one.
RULE 3 — Lucide Icons Only
// ✅ CORRECT
import { Plus, Pencil, Trash2, RefreshCw, Filter, Download } from 'lucide-react';
<Plus className="h-4 w-4" />
// ❌ WRONG
import { FaPlus } from 'react-icons/fa'; // No FontAwesome
import PlusIcon from '@heroicons/react/...' // No Heroicons
// Inline SVGs only as last resort for brand icons not in Lucide
Standard icon sizes:
- Toolbar buttons:
h-4 w-4 - Nav items:
h-4 w-4 - Stat cards:
h-5 w-5 - Empty state:
h-12 w-12 text-muted-foreground - Loading spinner:
h-8 w-8 animate-spin text-muted-foreground
RULE 4 — No Inline Styles for Colors or Spacing
// ❌ WRONG
<div style={{ color: '#22c55e', padding: '12px' }}>...</div>
<div style={{ backgroundColor: 'rgba(34, 197, 94, 0.1)' }}>...</div>
// ✅ CORRECT — className only
<div className="text-primary p-3">...</div>
<div className="bg-primary/10">...</div>
Exception: style is only permitted for:
- Dynamic widths/heights from calculations (e.g.,
style={{ width: columnWidth + 'px' }}) - Canvas/animation transforms
RULE 5 — Dark Mode Always Supported
Every page and component must work in both light and dark mode because next-themes is configured with the class strategy. Test both modes. Never assume white background.
// ✅ Correct — uses tokens that adapt
<div className="bg-background text-foreground border border-border">
// ❌ Wrong — breaks in dark mode
<div className="bg-white text-black border border-gray-200">
RULE 6 — Responsive Layout Rules
| Breakpoint | Behavior |
|---|---|
< lg (< 1024px) | Sidebar collapses to hamburger Sheet (mobile) |
lg+ | Full sidebar visible. Content area uses p-6 padding |
| Tables | Horizontal scroll wrapper, never truncate columns without overflow-x-auto |
| Modals | Max width max-w-md (sm), max-w-2xl (md), max-w-4xl (lg). Full screen on mobile |
| Forms | Single column on mobile, 2-col grid on md+ |
RULE 7 — Accessibility (WCAG 2.1 AA Minimum)
- All interactive elements must have
aria-labelor visible text - Keyboard navigation must work (focus rings via
ring-ring ring-offset-background focus-visible:ring-2) - Color alone cannot convey meaning (always combine color + icon or color + text)
roleattributes on nav (role="navigation"), tables (role="table"), and tab panels (role="tabpanel")- Screen reader text for icon-only buttons:
<span className="sr-only">Description</span>
RULE 8 — No git add -A, Specific File Staging
When committing UI standards implementations:
git add "modules/shared/components/layout/app-layout.tsx"
# Never: git add -A or git add .
Document Index
| # | Document | Covers |
|---|---|---|
| 00 | This file | Foundational rules, CSS vars, tech stack |
| 01 | 01-LAYOUT-SHELL.md | Full page layout: shell, zones, spacing |
| 02 | 02-HEADER.md | Sticky header: three zones, icons, page title |
| 03 | 03-SIDEBAR-NAVIGATION.md | Nav sidebar: groups, items, collapse, user card |
| 04 | 04-PAGE-TITLE-AND-SUBTITLE.md | Title/subtitle pattern, registry, override context |
| 05 | 05-TAB-NAVIGATION.md | State-based vs route-based tabs, active styles |
| 06 | 06-STATS-SUMMARY-CARDS.md | Stat cards: layout, color variants, grid rules |
| 07 | 07-UNIVERSAL-DATA-TABLE.md | UDT: columns, sorting, density, selection, pagination |
| 08 | 08-UDT-TOOLBAR.md | All toolbar buttons: icons, tooltips, permissions |
| 09 | 09-COLUMN-FILTER-POPUP.md | Column filter panel: sort, pin, hide, filter builder |
| 10 | 10-IMPORT-WIZARD-MODAL.md | 6-step import wizard: each step, progress, CTAs |
| 11 | 11-MODALS-AND-DIALOGS.md | Modal sizes, header/body/footer, CTA placement |
| 12 | 12-POPOUT-AND-SLIDE-OVER-WINDOWS.md | Popup windows, Sheet slide-overs |
| 13 | 13-FOOTER.md | Footer spec, height, content, policy links |
| 14 | 14-COMPONENT-QUICK-REFERENCE.md | Master lookup: UI element → component → file path |
| 07B | 07B-UDT-CELL-SELECTION-AND-COPY.md | Cell range selection, right-click context menu, clipboard copy |
| 07C | 07C-UDT-COLUMN-VISIBILITY-AND-DEFAULTS.md | Default visible/hidden columns, audit field standards, column selector |
| 08B | 08B-EXPORT-DIALOG.md | Export Data dialog: records/columns options, format reference |
| 08C | 08C-TOOLBAR-BUTTON-POPUP-REFERENCE.md | Per-button popup type, behavior, selection requirements |
| 09B | 09B-ADVANCED-FILTER-POPUP.md | Advanced Filter: draggable/resizable floating panel, saved queries, shared queries |
Pre-Commit Checklist (Run Before Every Frontend Commit)
# 1. Zero hardcoded color classes
grep -r "bg-green-\|bg-red-\|bg-gray-\|bg-blue-\|text-green-\|text-red-\|text-gray-\|text-blue-" \
src/components/ --include="*.tsx"
# Expected: 0 matches (except intentional status-dot indicators)
# 2. No inline color styles
grep -r 'style=.*color\|style=.*background' src/components/ --include="*.tsx"
# Expected: 0 matches
# 3. Lucide icons only
grep -r "from 'react-icons\|from '@heroicons" src/ --include="*.tsx"
# Expected: 0 matches
# 4. shadcn imports are from @/components/ui (not external)
grep -r "from 'shadcn\|import.*shadcn" src/ --include="*.tsx"
# Expected: 0 matches (should all be @/components/ui/*)