481 lines
18 KiB
Markdown
481 lines
18 KiB
Markdown
# Healthcare Design Tokens
|
|
|
|
Cross-platform token system for clinical-grade UI: Web (Bootstrap 5/Tabler + PHP) and Android (Jetpack Compose + Material 3).
|
|
|
|
## Quick-Reference Token Table
|
|
|
|
| Token Category | Web (CSS Custom Property) | Android (Compose) | Value |
|
|
|----------------|---------------------------|---------------------|-------|
|
|
| **Primary** | `--clinical-primary` | `ClinicalPrimary` | `#2563EB` |
|
|
| **Secondary** | `--clinical-secondary` | `ClinicalSecondary` | `#0F766E` |
|
|
| **Surface** | `--clinical-surface` | `ClinicalSurface` | `#F8FAFC` |
|
|
| **Background** | `--clinical-bg` | `ClinicalBackground` | `#F1F5F9` |
|
|
| **Critical** | `--clinical-critical` | `ClinicalCritical` | `#DC2626` |
|
|
| **Warning** | `--clinical-warning` | `ClinicalWarning` | `#D97706` |
|
|
| **Success** | `--clinical-success` | `ClinicalSuccess` | `#059669` |
|
|
| **Info** | `--clinical-info` | `ClinicalInfo` | `#0284C7` |
|
|
| **Text Primary** | `--clinical-text` | `ClinicalText` | `#0F172A` |
|
|
| **Text Secondary** | `--clinical-text-secondary` | `ClinicalTextSecondary` | `#334155` |
|
|
| **Text Muted** | `--clinical-text-muted` | `ClinicalTextMuted` | `#64748B` |
|
|
| **Medication** | `--clinical-medication` | `ClinicalMedication` | `#7C3AED` |
|
|
| **Spacing Base** | `--space-base` | `SpaceBase` | `4px / 4dp` |
|
|
| **Radius md** | `--radius-md` | `RadiusMd` | `8px / 8dp` |
|
|
| **Body Font Size** | `--font-body` | `bodyLarge` | `0.9375rem / 16sp` |
|
|
|
|
---
|
|
|
|
## 1. Color System
|
|
|
|
### 1.1 Core Semantic Palette
|
|
|
|
```css
|
|
/* Web — add to your root stylesheet or Tabler custom override */
|
|
:root {
|
|
/* Primary palette */
|
|
--clinical-primary: #2563EB; /* Trust blue — headers, primary buttons, nav */
|
|
--clinical-primary-hover: #1D4ED8;
|
|
--clinical-primary-light: #DBEAFE; /* Badges, light backgrounds */
|
|
--clinical-secondary: #0F766E; /* Clinical teal — secondary actions */
|
|
--clinical-secondary-hover: #0D6963;
|
|
--clinical-secondary-light: #CCFBF1;
|
|
|
|
/* Surfaces */
|
|
--clinical-surface: #F8FAFC; /* Clean white — card backgrounds */
|
|
--clinical-bg: #F1F5F9; /* Soft gray — page background */
|
|
--clinical-border: #E2E8F0; /* Default borders */
|
|
|
|
/* Feedback */
|
|
--clinical-critical: #DC2626; /* Danger, critical vitals, allergies */
|
|
--clinical-critical-light: #FEF2F2;
|
|
--clinical-warning: #D97706; /* Approaching thresholds */
|
|
--clinical-warning-light: #FFFBEB;
|
|
--clinical-success: #059669; /* Normal ranges, confirmations */
|
|
--clinical-success-light: #ECFDF5;
|
|
--clinical-info: #0284C7; /* Informational, pending states */
|
|
--clinical-info-light: #F0F9FF;
|
|
|
|
/* Text hierarchy */
|
|
--clinical-text: #0F172A; /* Primary body text */
|
|
--clinical-text-secondary: #334155;/* Secondary labels */
|
|
--clinical-text-muted: #64748B; /* Timestamps, metadata */
|
|
--clinical-text-inverse: #FFFFFF; /* Text on dark backgrounds */
|
|
}
|
|
```
|
|
|
|
```kotlin
|
|
// Android — HealthcareColors.kt
|
|
package com.app.ui.theme
|
|
|
|
import androidx.compose.ui.graphics.Color
|
|
|
|
object ClinicalColors {
|
|
// Primary palette
|
|
val Primary = Color(0xFF2563EB)
|
|
val PrimaryHover = Color(0xFF1D4ED8)
|
|
val PrimaryLight = Color(0xFFDBEAFE)
|
|
val Secondary = Color(0xFF0F766E)
|
|
val SecondaryHover = Color(0xFF0D6963)
|
|
val SecondaryLight = Color(0xFFCCFBF1)
|
|
|
|
// Surfaces
|
|
val Surface = Color(0xFFF8FAFC)
|
|
val Background = Color(0xFFF1F5F9)
|
|
val Border = Color(0xFFE2E8F0)
|
|
|
|
// Feedback
|
|
val Critical = Color(0xFFDC2626)
|
|
val CriticalLight = Color(0xFFFEF2F2)
|
|
val Warning = Color(0xFFD97706)
|
|
val WarningLight = Color(0xFFFFFBEB)
|
|
val Success = Color(0xFF059669)
|
|
val SuccessLight = Color(0xFFECFDF5)
|
|
val Info = Color(0xFF0284C7)
|
|
val InfoLight = Color(0xFFF0F9FF)
|
|
|
|
// Text
|
|
val Text = Color(0xFF0F172A)
|
|
val TextSecondary = Color(0xFF334155)
|
|
val TextMuted = Color(0xFF64748B)
|
|
val TextInverse = Color(0xFFFFFFFF)
|
|
}
|
|
```
|
|
|
|
### 1.2 Clinical Status Colors
|
|
|
|
| Status | Foreground | Background | Usage |
|
|
|--------|-----------|------------|-------|
|
|
| Vital Normal | `#059669` | `#ECFDF5` | Within normal range |
|
|
| Vital Warning | `#D97706` | `#FFFBEB` | Approaching threshold |
|
|
| Vital Critical | `#DC2626` | `#FEF2F2` | Outside safe range |
|
|
| Allergy Alert | `#DC2626` | `#FEF2F2` | Allergy banners, badges |
|
|
| Medication | `#7C3AED` | `#F5F3FF` | Pharma context, prescriptions |
|
|
| Lab Pending | `#0284C7` | `#F0F9FF` | Awaiting results |
|
|
| Lab Complete | `#059669` | `#ECFDF5` | Results available |
|
|
| Discharged | `#64748B` | `#F1F5F9` | Inactive/historical records |
|
|
|
|
### 1.3 Accessibility Compliance
|
|
|
|
All color pairs MUST meet WCAG 2.2 AA minimum contrast ratios:
|
|
|
|
- **Normal text (< 18pt):** 4.5:1 contrast ratio minimum
|
|
- **Large text (>= 18pt or 14pt bold):** 3:1 contrast ratio minimum
|
|
- **UI components / graphical objects:** 3:1 against adjacent colors
|
|
|
|
**Verified pairings:**
|
|
|
|
| Text Color | Background | Ratio | Pass |
|
|
|------------|-----------|-------|------|
|
|
| `#0F172A` on `#F8FAFC` | Surface | 15.4:1 | AA/AAA |
|
|
| `#334155` on `#F8FAFC` | Surface | 8.5:1 | AA/AAA |
|
|
| `#FFFFFF` on `#2563EB` | Primary | 4.6:1 | AA |
|
|
| `#FFFFFF` on `#DC2626` | Critical | 4.6:1 | AA |
|
|
| `#FFFFFF` on `#059669` | Success | 4.5:1 | AA |
|
|
| `#92400E` on `#FFFBEB` | Warning bg | 7.1:1 | AA/AAA |
|
|
|
|
### 1.4 Color-Blind Safety
|
|
|
|
Never rely on red/green alone to distinguish clinical states. ALWAYS pair color with:
|
|
|
|
- **Text labels:** "Normal", "Warning", "Critical" alongside color indicators
|
|
- **Icons:** Check-circle, alert-triangle, x-circle
|
|
- **Patterns:** Solid fill (critical), hatched (warning), outline (normal)
|
|
- **Shape:** Circle (normal), triangle (warning), octagon (critical)
|
|
|
|
---
|
|
|
|
## 2. Typography Scale
|
|
|
|
### 2.1 Web (Bootstrap 5 / Tabler)
|
|
|
|
```css
|
|
:root {
|
|
--font-system: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
|
"Helvetica Neue", Arial, sans-serif;
|
|
--font-mono: "SF Mono", "Cascadia Code", "Fira Code", "Consolas", monospace;
|
|
|
|
/* Page hierarchy */
|
|
--font-h1: 1.75rem; /* 28px — page titles */
|
|
--font-h1-weight: 700;
|
|
--font-h2: 1.25rem; /* 20px — section headings */
|
|
--font-h2-weight: 600;
|
|
--font-h3: 1.1rem; /* 17.6px — subsection headings */
|
|
--font-h3-weight: 600;
|
|
|
|
/* Body text */
|
|
--font-body: 0.9375rem; /* 15px — primary body */
|
|
--font-body-weight: 400;
|
|
--font-small: 0.8125rem;/* 13px — secondary, captions */
|
|
--font-small-weight: 400;
|
|
--font-caption: 0.75rem;/* 12px — timestamps, metadata */
|
|
--font-caption-weight: 400;
|
|
|
|
/* Clinical specialties */
|
|
--font-vital-value: 1.5rem; /* 24px — vital sign readings */
|
|
--font-vital-weight: 700;
|
|
--font-patient-name: 1.125rem; /* 18px — patient display name */
|
|
--font-patient-weight: 600;
|
|
--font-mrn: 0.875rem; /* 14px — MRN, IDs */
|
|
--font-mrn-weight: 500;
|
|
--font-mrn-spacing: 0.05em; /* Wider tracking for readability */
|
|
}
|
|
|
|
/* Clinical value class */
|
|
.vital-value {
|
|
font-family: var(--font-mono);
|
|
font-size: var(--font-vital-value);
|
|
font-weight: var(--font-vital-weight);
|
|
line-height: 1.2;
|
|
}
|
|
|
|
.patient-name { font-size: var(--font-patient-name); font-weight: var(--font-patient-weight); }
|
|
.mrn-display { font-family: var(--font-mono); font-size: var(--font-mrn); font-weight: var(--font-mrn-weight); letter-spacing: var(--font-mrn-spacing); }
|
|
```
|
|
|
|
### 2.2 Android (Jetpack Compose — Material 3 Overrides)
|
|
|
|
```kotlin
|
|
// HealthcareTypography.kt
|
|
import androidx.compose.material3.Typography
|
|
import androidx.compose.ui.text.TextStyle
|
|
import androidx.compose.ui.text.font.FontFamily
|
|
import androidx.compose.ui.text.font.FontWeight
|
|
import androidx.compose.ui.unit.sp
|
|
|
|
val HealthcareTypography = Typography(
|
|
headlineLarge = TextStyle(fontSize = 28.sp, fontWeight = FontWeight.W400, lineHeight = 36.sp),
|
|
headlineMedium = TextStyle(fontSize = 24.sp, fontWeight = FontWeight.W400, lineHeight = 32.sp),
|
|
titleLarge = TextStyle(fontSize = 22.sp, fontWeight = FontWeight.W400, lineHeight = 28.sp),
|
|
titleMedium = TextStyle(fontSize = 16.sp, fontWeight = FontWeight.W500, lineHeight = 24.sp),
|
|
bodyLarge = TextStyle(fontSize = 16.sp, fontWeight = FontWeight.W400, lineHeight = 24.sp),
|
|
bodyMedium = TextStyle(fontSize = 14.sp, fontWeight = FontWeight.W400, lineHeight = 20.sp),
|
|
labelLarge = TextStyle(fontSize = 14.sp, fontWeight = FontWeight.W500, lineHeight = 20.sp),
|
|
labelSmall = TextStyle(fontSize = 11.sp, fontWeight = FontWeight.W500, lineHeight = 16.sp),
|
|
)
|
|
|
|
// Clinical-specific text styles (extend beyond M3 type scale)
|
|
object ClinicalTextStyles {
|
|
val VitalValue = TextStyle(
|
|
fontFamily = FontFamily.Monospace,
|
|
fontSize = 24.sp,
|
|
fontWeight = FontWeight.W700,
|
|
lineHeight = 28.sp
|
|
)
|
|
val PatientName = TextStyle(
|
|
fontSize = 18.sp,
|
|
fontWeight = FontWeight.W600,
|
|
lineHeight = 24.sp
|
|
)
|
|
val MrnDisplay = TextStyle(
|
|
fontFamily = FontFamily.Monospace,
|
|
fontSize = 14.sp,
|
|
fontWeight = FontWeight.W500,
|
|
letterSpacing = 0.8.sp
|
|
)
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 3. Spacing System
|
|
|
|
Base unit: **4px** (web) / **4dp** (Android). All spacing values are multiples of 4.
|
|
|
|
| Token | Value | CSS Variable | Compose | Use Case |
|
|
|-------|-------|-------------|---------|----------|
|
|
| `xs` | 4 | `--space-xs: 4px` | `4.dp` | Icon-to-text gap, tight inline spacing |
|
|
| `sm` | 8 | `--space-sm: 8px` | `8.dp` | Related element gap, badge padding |
|
|
| `md` | 12 | `--space-md: 12px` | `12.dp` | List item vertical padding, form field gap |
|
|
| `lg` | 16 | `--space-lg: 16px` | `16.dp` | Card internal padding, section padding |
|
|
| `xl` | 24 | `--space-xl: 24px` | `24.dp` | Between card groups, column gutters |
|
|
| `2xl` | 32 | `--space-2xl: 32px` | `32.dp` | Major section separation |
|
|
| `3xl` | 48 | `--space-3xl: 48px` | `48.dp` | Page-level vertical margins |
|
|
|
|
### Component-Specific Spacing
|
|
|
|
| Component | Padding | Gap |
|
|
|-----------|---------|-----|
|
|
| Card (web/Android) | 16px / 16dp all sides | 24px / 24dp between cards |
|
|
| List item | 12px vertical, 16px horizontal | 1px divider or 8px gap |
|
|
| Form fields | 0 internal (handled by component) | 12px between fields |
|
|
| Section groups | 0 (cards handle own padding) | 32px between sections |
|
|
| Button group | 8px between buttons | N/A |
|
|
| Alert banners | 12px vertical, 16px horizontal | 8px icon-to-text |
|
|
|
|
### Touch Target Minimums
|
|
|
|
| Platform | Minimum | Preferred (clinical) | Note |
|
|
|----------|---------|---------------------|------|
|
|
| **Web** | 44 x 44px | 48 x 48px | WCAG 2.2 Level AA target size |
|
|
| **Android** | 48 x 48dp | 56 x 56dp | Preferred for bedside/clinical use |
|
|
|
|
Clinical environments use gloved hands and stressed motor control — always prefer the larger target size.
|
|
|
|
---
|
|
|
|
## 4. Elevation and Shadows
|
|
|
|
### 4.1 Web (CSS Shadows)
|
|
|
|
```css
|
|
:root {
|
|
--shadow-0: none; /* Level 0: inline content */
|
|
--shadow-1: 0 1px 2px 0 rgba(0, 0, 0, 0.05); /* Level 1: cards, tiles */
|
|
--shadow-2: 0 4px 6px -1px rgba(0, 0, 0, 0.07),
|
|
0 2px 4px -2px rgba(0, 0, 0, 0.05); /* Level 2: dropdowns, popovers */
|
|
--shadow-3: 0 10px 15px -3px rgba(0, 0, 0, 0.1),
|
|
0 4px 6px -4px rgba(0, 0, 0, 0.05); /* Level 3: modals, dialogs */
|
|
}
|
|
```
|
|
|
|
| Level | Shadow Token | Use Case |
|
|
|-------|-------------|----------|
|
|
| 0 | `--shadow-0` | Table rows, inline content, list items |
|
|
| 1 | `--shadow-1` | Cards, panels, stat tiles |
|
|
| 2 | `--shadow-2` | Dropdown menus, popovers, tooltips |
|
|
| 3 | `--shadow-3` | Modals, confirmation dialogs, medication checks |
|
|
|
|
### 4.2 Android (Material 3 Elevation)
|
|
|
|
| Level | Elevation | Compose | Use Case |
|
|
|-------|-----------|---------|----------|
|
|
| 0 | 0dp | `elevation = 0.dp` | Flat surfaces, list items |
|
|
| 1 | 1dp | `elevation = 1.dp` | Cards, ElevatedCard |
|
|
| 2 | 3dp | `elevation = 3.dp` | Navigation bars, bottom sheets |
|
|
| 3 | 6dp | `elevation = 6.dp` | FAB, extended FAB |
|
|
| 4 | 8dp | `elevation = 8.dp` | Dialogs, modal bottom sheets |
|
|
|
|
---
|
|
|
|
## 5. Border Radius
|
|
|
|
| Token | Web | Android | Use Case |
|
|
|-------|-----|---------|----------|
|
|
| `sm` | `4px` | `4.dp` | Input fields, text areas, inline badges |
|
|
| `md` | `8px` | `8.dp` | Cards, buttons, dropdown menus |
|
|
| `lg` | `12px` | `12.dp` | Large cards, image containers |
|
|
| `xl` | `16px` | `16.dp` | Modals, bottom sheets |
|
|
| `pill` | `9999px` | `CircleShape` | Status badges, vital indicators, tags |
|
|
|
|
```css
|
|
:root {
|
|
--radius-sm: 4px;
|
|
--radius-md: 8px;
|
|
--radius-lg: 12px;
|
|
--radius-xl: 16px;
|
|
--radius-pill: 9999px;
|
|
}
|
|
```
|
|
|
|
```kotlin
|
|
object ClinicalShapes {
|
|
val Small = RoundedCornerShape(4.dp)
|
|
val Medium = RoundedCornerShape(8.dp)
|
|
val Large = RoundedCornerShape(12.dp)
|
|
val ExtraLarge = RoundedCornerShape(16.dp)
|
|
val Pill = CircleShape
|
|
}
|
|
```
|
|
|
|
**Mapping:** Cards use `md`. Buttons use `md`. Input fields use `sm`. Clinical status badges use `pill`. Modals use `xl`.
|
|
|
|
---
|
|
|
|
## 6. Iconography
|
|
|
|
### Healthcare-Specific Icons
|
|
|
|
| Category | Icons | Web Source | Android Source |
|
|
|----------|-------|-----------|----------------|
|
|
| **Clinical** | Stethoscope, heartbeat, pill, syringe, thermometer, lungs, brain, bone, eye, tooth | Tabler Icons / Bootstrap Icons | Material Symbols (Outlined) |
|
|
| **Status** | Check-circle, x-circle, alert-triangle, info, clock, shield | Tabler Icons | Material Symbols |
|
|
| **Actions** | Plus, edit, trash, search, filter, sort, download, print, share | Tabler Icons | Material Symbols |
|
|
|
|
### Sizing
|
|
|
|
| Context | Web | Android | When |
|
|
|---------|-----|---------|------|
|
|
| Inline/compact | 16px | 20dp | Inside buttons, badges, table cells |
|
|
| Default | 20px | 24dp | Navigation, list leading icons |
|
|
| Emphasis | 24px | 28dp | Card headers, empty states |
|
|
| Hero | 48px | 48dp | Dashboard KPI icons, onboarding |
|
|
|
|
**Rules:** Icons must have `aria-hidden="true"` when decorative, or a descriptive `aria-label` when functional. On Android, use `contentDescription` for accessibility. Never use icons alone for critical clinical actions — always pair with a text label.
|
|
|
|
---
|
|
|
|
## 7. Animation and Motion
|
|
|
|
### 7.1 Web Transitions
|
|
|
|
```css
|
|
:root {
|
|
--duration-micro: 150ms; /* Button hover, focus rings, toggles */
|
|
--duration-standard: 300ms; /* Panel open/close, state changes */
|
|
--duration-page: 400ms; /* Page transitions, route changes */
|
|
--easing-standard: cubic-bezier(0.4, 0, 0.2, 1);
|
|
--easing-decelerate: cubic-bezier(0, 0, 0.2, 1); /* Elements entering */
|
|
--easing-accelerate: cubic-bezier(0.4, 0, 1, 1); /* Elements exiting */
|
|
}
|
|
|
|
/* Usage */
|
|
.card { transition: box-shadow var(--duration-micro) var(--easing-standard); }
|
|
.panel-expand { transition: max-height var(--duration-standard) var(--easing-decelerate); }
|
|
```
|
|
|
|
### 7.2 Android Motion (Material 3 Spring-Based)
|
|
|
|
```kotlin
|
|
object ClinicalMotion {
|
|
// Enters — elements appearing on screen
|
|
val EmphasizedDecelerate = tween<Float>(
|
|
durationMillis = 400,
|
|
easing = CubicBezierEasing(0.05f, 0.7f, 0.1f, 1.0f)
|
|
)
|
|
// Exits — elements leaving the screen
|
|
val EmphasizedAccelerate = tween<Float>(
|
|
durationMillis = 200,
|
|
easing = CubicBezierEasing(0.3f, 0.0f, 0.8f, 0.15f)
|
|
)
|
|
// State changes — color, size, elevation shifts
|
|
val Standard = tween<Float>(
|
|
durationMillis = 300,
|
|
easing = CubicBezierEasing(0.2f, 0.0f, 0.0f, 1.0f)
|
|
)
|
|
}
|
|
```
|
|
|
|
### 7.3 Clinical Motion Rules
|
|
|
|
- **Critical alerts:** NO animation delay. Appear instantly (`duration: 0ms`). Patient safety cannot wait for a fade-in.
|
|
- **Loading/skeleton states:** Use a pulse animation (opacity oscillation 0.4 to 1.0, 1.5s loop).
|
|
- **Vital sign updates:** Subtle color transition (300ms) when value changes category (normal to warning).
|
|
- **Never animate** medication confirmation dialogs — they must be immediately readable.
|
|
- **Reduce motion:** Respect `prefers-reduced-motion` (web) and accessibility motion settings (Android). Replace animations with instant state changes.
|
|
|
|
```css
|
|
@media (prefers-reduced-motion: reduce) {
|
|
*, *::before, *::after {
|
|
animation-duration: 0.01ms !important;
|
|
transition-duration: 0.01ms !important;
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 8. Breakpoints
|
|
|
|
### 8.1 Web (Bootstrap 5 Standard)
|
|
|
|
| Breakpoint | Min Width | Grid Columns | Clinical Layout |
|
|
|------------|----------|-------------|-----------------|
|
|
| `xs` | 0 | 1 | Stacked cards, single-column patient list |
|
|
| `sm` | 576px | 1-2 | Patient list with minimal detail |
|
|
| `md` | 768px | 2 | Sidebar nav + content, tablet portrait |
|
|
| `lg` | 992px | 2-3 | Full clinical detail views, list + detail |
|
|
| `xl` | 1200px | 3 | Three-panel: nav + list + detail |
|
|
| `xxl` | 1400px | 3-4 | Dashboard with multi-widget grid |
|
|
|
|
**Optimization targets:**
|
|
|
|
- Patient lists usable at ALL breakpoints (xs through xxl)
|
|
- Clinical detail views (vitals, medication charts) optimized for >= 992px (tablet landscape and above)
|
|
- Admin dashboards designed for >= 1200px, gracefully collapse below
|
|
|
|
### 8.2 Android (WindowSizeClass)
|
|
|
|
| Class | Width | Columns | Layout Strategy |
|
|
|-------|-------|---------|-----------------|
|
|
| **Compact** | < 600dp | 1 | Single-column patient view, bottom nav, full-screen forms |
|
|
| **Medium** | 600-840dp | 2 | List-detail split (40/60), rail nav, side-by-side vitals |
|
|
| **Expanded** | > 840dp | 3 | Three-panel: nav rail + patient list + detail pane |
|
|
|
|
```kotlin
|
|
// Usage in Compose
|
|
@Composable
|
|
fun PatientScreen(windowSizeClass: WindowSizeClass) {
|
|
when (windowSizeClass.widthSizeClass) {
|
|
WindowWidthSizeClass.Compact -> PatientSingleColumn()
|
|
WindowWidthSizeClass.Medium -> PatientListDetail()
|
|
WindowWidthSizeClass.Expanded -> PatientThreePanel()
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Token Application Checklist
|
|
|
|
When building a new healthcare screen, verify:
|
|
|
|
1. All colors reference token variables (no hardcoded hex in components)
|
|
2. Typography uses defined scale only (no arbitrary font sizes)
|
|
3. Spacing uses 4px/4dp multiples exclusively
|
|
4. Touch targets meet 44px/48dp minimums
|
|
5. Color pairs pass WCAG 2.2 AA contrast check
|
|
6. Color-blind safe: every color meaning has a text/icon backup
|
|
7. Critical alerts have zero animation delay
|
|
8. `prefers-reduced-motion` / Android accessibility settings are respected
|
|
9. Breakpoints handle all clinical layouts from mobile to desktop
|
|
10. Clinical values (vitals, MRN) use monospace font tokens
|