Skip to main content

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:

ApplicationModuleLocation
Anshin OrchestrateTour booking platformC:\projects\anshin-orchestrate\orchestrate-frontend\
Accelerate — Fax AutomationFax processingC:\projects\accelerate\modules\fax-automation\app\web-fax-dashboard\
Accelerate — Referral AutomationReferral managementC:\projects\accelerate\modules\referral-automation\web-outreach-dashboard\
Accelerate — Patient OutreachPatient communicationC:\projects\accelerate\modules\patient-outreach\ (future)
All future Accelerate modulesAny new moduleFollow these standards from day one

Technology Stack (Non-Negotiable)

LayerTechnologyVersion
FrameworkNext.js (App Router)14/15
LanguageTypeScript5.x
UI Primitivesshadcn/ui (Radix UI)latest
StylingTailwind CSS3.x
IconsLucide Reactlatest
TablesTanStack React Tablev8
State / Data FetchingTanStack React Queryv5
FormsReact Hook Form + Zodlatest
State (client)Zustand or React Contextlatest

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 TokenUse forLight valueDark value
bg-backgroundPage/app backgroundwhitedark
bg-cardCard/panel backgroundwhitedark card
bg-mutedSecondary areas, inactive itemsgray-100gray-800
bg-primaryBrand action colorbrand greenbrand green
bg-primary/10Active/selected status backgroundlight green
bg-destructiveHard error backgroundsred
bg-destructive/10Soft error/warning backgroundslight red
bg-accentHover states on non-interactiveaccent
bg-accent/50Hover on nav items
bg-popoverDropdown menus, popoverswhite
text-foregroundPrimary body textnear-blacknear-white
text-muted-foregroundSecondary/caption textgray-500gray-400
text-primaryBrand text, active linkbrand greenbrand green
text-primary-foregroundText ON a primary bgwhitewhite
text-destructiveError messagesred
border-borderDefault bordersgray-200gray-700
border-primaryActive/focused bordersbrand green
border-primary/20Subtle active borderslight green
ring-ringFocus ringsbrand
sidebar-accentSidebar hover background
sidebar-primarySidebar active item
sidebar-foregroundSidebar 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.

Neededshadcn componentImport path
ButtonButton@/components/ui/button
Input / textareaInput, Textarea@/components/ui/input
Select dropdownSelect@/components/ui/select
Modal/overlayDialog@/components/ui/dialog
Slide-out panelSheet@/components/ui/sheet
TooltipTooltip@/components/ui/tooltip
Badge/pillBadge@/components/ui/badge
CheckboxCheckbox@/components/ui/checkbox
RadioRadioGroup@/components/ui/radio-group
Switch/toggleSwitch@/components/ui/switch
TableTable@/components/ui/table
TabsTabs@/components/ui/tabs
Dropdown menuDropdownMenu@/components/ui/dropdown-menu
PopoverPopover@/components/ui/popover
ToastToaster + useToast@/components/ui/toaster
CardCard@/components/ui/card
SeparatorSeparator@/components/ui/separator
AvatarAvatar@/components/ui/avatar
SkeletonSkeleton@/components/ui/skeleton
AlertAlert@/components/ui/alert
SidebarSidebarProvider, 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

BreakpointBehavior
< lg (< 1024px)Sidebar collapses to hamburger Sheet (mobile)
lg+Full sidebar visible. Content area uses p-6 padding
TablesHorizontal scroll wrapper, never truncate columns without overflow-x-auto
ModalsMax width max-w-md (sm), max-w-2xl (md), max-w-4xl (lg). Full screen on mobile
FormsSingle column on mobile, 2-col grid on md+

RULE 7 — Accessibility (WCAG 2.1 AA Minimum)

  • All interactive elements must have aria-label or 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)
  • role attributes 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

#DocumentCovers
00This fileFoundational rules, CSS vars, tech stack
0101-LAYOUT-SHELL.mdFull page layout: shell, zones, spacing
0202-HEADER.mdSticky header: three zones, icons, page title
0303-SIDEBAR-NAVIGATION.mdNav sidebar: groups, items, collapse, user card
0404-PAGE-TITLE-AND-SUBTITLE.mdTitle/subtitle pattern, registry, override context
0505-TAB-NAVIGATION.mdState-based vs route-based tabs, active styles
0606-STATS-SUMMARY-CARDS.mdStat cards: layout, color variants, grid rules
0707-UNIVERSAL-DATA-TABLE.mdUDT: columns, sorting, density, selection, pagination
0808-UDT-TOOLBAR.mdAll toolbar buttons: icons, tooltips, permissions
0909-COLUMN-FILTER-POPUP.mdColumn filter panel: sort, pin, hide, filter builder
1010-IMPORT-WIZARD-MODAL.md6-step import wizard: each step, progress, CTAs
1111-MODALS-AND-DIALOGS.mdModal sizes, header/body/footer, CTA placement
1212-POPOUT-AND-SLIDE-OVER-WINDOWS.mdPopup windows, Sheet slide-overs
1313-FOOTER.mdFooter spec, height, content, policy links
1414-COMPONENT-QUICK-REFERENCE.mdMaster lookup: UI element → component → file path
07B07B-UDT-CELL-SELECTION-AND-COPY.mdCell range selection, right-click context menu, clipboard copy
07C07C-UDT-COLUMN-VISIBILITY-AND-DEFAULTS.mdDefault visible/hidden columns, audit field standards, column selector
08B08B-EXPORT-DIALOG.mdExport Data dialog: records/columns options, format reference
08C08C-TOOLBAR-BUTTON-POPUP-REFERENCE.mdPer-button popup type, behavior, selection requirements
09B09B-ADVANCED-FILTER-POPUP.mdAdvanced 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/*)