# Compliance, Accessibility & Security UI Patterns Cross-platform reference for HIPAA compliance, WCAG 2.2 AA accessibility, and security UI patterns: Web (Bootstrap 5/Tabler + PHP) and Android (Jetpack Compose + Material 3). --- ## 1. HIPAA UI Requirements ### 1.1 Protected Health Information (PHI) Handling **Never display PHI in:** - URLs or query parameters (`/patient?ssn=123-45-6789` is a violation) - Browser tab titles or `` tags (use "Patient Record" not "John Smith - DOB 1985") - Push notification text (use "You have a new message" not "Lab result: HIV positive") - Error messages or stack traces shown to users - Console logs (`console.log(patient)` must be stripped in production) - Autocomplete suggestions on PHI fields **Mask sensitive identifiers:** | Data Type | Display Format | Full Access | |-----------|---------------|-------------| | SSN | `***-**-6789` | Behind re-auth click | | DOB | `**/**/1985` or age only | Role-dependent | | MRN | Full (operational need) | Always visible to clinical | | Phone | `(***) ***-4321` | Role-dependent | | Address | City, State only | Role-dependent | **Auto-lock and session timeout:** - Lock screen after 15 minutes inactivity (configurable per facility) - Show blurred overlay, not blank screen (preserves context awareness) - Session timeout with state preservation: save draft to server, restore on re-login - Countdown warning at 2 minutes before timeout **Minimum Necessary Rule:** Only show PHI needed for the user's current role and task. A receptionist sees demographics, not diagnoses. A lab tech sees orders, not billing. ### 1.2 Audit Trail Requirements Log every interaction with patient data: | Event | Fields Logged | |-------|---------------| | Record access | user_id, patient_id, timestamp, IP, device, section_viewed | | Data modification | user_id, field, old_value, new_value, reason, timestamp | | Export/Print/Download | user_id, patient_id, format, fields_included, timestamp | | Failed access attempt | user_id, patient_id, reason_denied, timestamp | **UI requirements:** - Display "Last accessed by [Name] on [Date]" on sensitive records - Provide audit log viewer for compliance officers (filterable, exportable) - Show access history tab on patient profiles (visible to admin roles) ### 1.3 Break-the-Glass Access Emergency override for restricted patient records: 1. User clicks "Emergency Access" on restricted record 2. Modal requires: documented reason (dropdown + free text), acknowledgment checkbox 3. Access granted for limited window (default 4 hours, configurable) 4. Auto-notify privacy officer immediately 5. Access logged with elevated audit detail 6. Banner persists on record: "Emergency access active - expires [time]" ### 1.4 Platform-Specific HIPAA Controls **Web (PHP):** ```php // Secure session configuration ini_set('session.cookie_httponly', 1); ini_set('session.cookie_secure', 1); ini_set('session.cookie_samesite', 'Strict'); ini_set('session.gc_maxlifetime', 900); // 15 min header('Content-Security-Policy: default-src \'self\'; script-src \'self\''); header('X-Content-Type-Options: nosniff'); header('X-Frame-Options: DENY'); header('Referrer-Policy: no-referrer'); // Prevent PHI leaking in referrer ``` **Android (Kotlin):** ```kotlin // Sensitive screen protection class PatientRecordActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) window.setFlags( WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE ) } } // Encrypted storage for cached PHI val prefs = EncryptedSharedPreferences.create( "phi_cache", MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC), context, EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM ) ``` --- ## 2. WCAG 2.2 AA Compliance ### 2.1 Color Contrast Requirements | Element | Minimum Ratio | Test Tool | |---------|---------------|-----------| | Normal text (<18px) | 4.5:1 | WebAIM Contrast Checker | | Large text (18px+ or 14px+ bold) | 3:1 | Browser DevTools | | Non-text (icons, borders, focus rings) | 3:1 | axe DevTools | | Clinical status colors vs background | 4.5:1 | Test each status color | **Critical:** Test ALL clinical status colors (critical red, warning amber, success green, info blue) against both light and dark card backgrounds. ### 2.2 Keyboard Navigation Every healthcare screen must be fully keyboard-operable: - **Tab** reaches every interactive element in logical order (top-to-bottom, left-to-right) - **Focus indicators** are always visible: `outline: 2px solid var(--clinical-primary); outline-offset: 2px;` - **Skip-to-content** link on every page (first focusable element) - **Escape** closes all modals, dropdowns, and overlays - **Enter/Space** activates buttons and toggles - **Arrow keys** navigate within components (tabs, menus, date pickers) **Web implementation:** ```html <!-- Skip link — first element in <body> --> <a href="#main-content" class="visually-hidden-focusable"> Skip to main content </a> <!-- Focus indicator — never use outline: none --> <style> :focus-visible { outline: 2px solid var(--clinical-primary); outline-offset: 2px; border-radius: var(--radius-sm); } </style> ``` ### 2.3 Screen Reader Support **Web ARIA patterns:** ```html <!-- Vital signs with live updates --> <div role="region" aria-label="Vital Signs Monitor" aria-live="polite"> <dl> <dt>Heart Rate</dt> <dd aria-label="Heart rate 72 beats per minute, normal"> <span class="vital-value">72</span> <span class="vital-unit">bpm</span> <span class="badge badge-success">Normal</span> </dd> </dl> </div> <!-- Critical alert — assertive announcement --> <div role="alert" aria-live="assertive"> Critical: Blood pressure reading 180/120 — immediate attention required </div> <!-- Table with proper scope headers --> <table aria-label="Patient Medications"> <thead> <tr><th scope="col">Medication</th><th scope="col">Dosage</th><th scope="col">Frequency</th><th scope="col">Status</th></tr> </thead> <tbody> <tr><th scope="row">Metformin</th><td>500mg</td><td>Twice daily</td><td><span aria-label="Active">Active</span></td></tr> </tbody> </table> <!-- ARIA landmarks --> <header role="banner">...</header> <nav role="navigation" aria-label="Main">...</nav> <main role="main" id="main-content">...</main> <aside role="complementary" aria-label="Patient summary">...</aside> ``` **Android Compose semantics:** ```kotlin @Composable fun VitalSignCard(label: String, value: String, unit: String, status: VitalStatus) { Card( modifier = Modifier.semantics(mergeDescendants = true) { contentDescription = "$label $value $unit, ${status.label}" stateDescription = status.label if (status == VitalStatus.CRITICAL) liveRegion = LiveRegionMode.Assertive } ) { Text(text = label, style = MaterialTheme.typography.labelMedium) Text(text = "$value $unit", style = MaterialTheme.typography.headlineMedium) StatusBadge(status = status) } } @Composable fun CriticalAlert(message: String) { Card( colors = CardDefaults.cardColors(containerColor = ClinicalColors.CriticalLight), modifier = Modifier.semantics { liveRegion = LiveRegionMode.Assertive contentDescription = "Critical alert: $message" } ) { Text(text = message, color = ClinicalColors.Critical) } } ``` ### 2.4 Touch Target & Form Accessibility **Touch targets:** | Context | Web Minimum | Android Minimum | Healthcare Recommended | |---------|-------------|-----------------|----------------------| | Standard interactive | 44x44px | 48x48dp | 48px / 48dp | | Primary clinical action | 48x48px | 56x56dp | 56px / 56dp | | Gap between targets | 8px | 8dp | 8px / 8dp | **Forms -- mandatory patterns:** ```html <!-- ALWAYS visible labels, NEVER placeholder-only --> <div class="mb-3"> <label for="allergies" class="form-label"> Known Allergies <span class="text-danger" aria-hidden="true">*</span> <span class="visually-hidden">(required)</span> </label> <textarea id="allergies" class="form-control" required aria-describedby="allergies-help allergies-error"></textarea> <div id="allergies-help" class="form-text">List all known drug and food allergies</div> <div id="allergies-error" class="invalid-feedback" role="alert">Required for patient safety</div> </div> <!-- Group related fields with fieldset/legend --> <fieldset> <legend>Emergency Contact</legend> <label for="ec-name" class="form-label">Full Name</label> <input type="text" id="ec-name" class="form-control mb-3"> <label for="ec-phone" class="form-label">Phone Number</label> <input type="tel" id="ec-phone" class="form-control"> </fieldset> ``` --- ## 3. Color-Blind Safe Design **Rule:** Never use color alone to convey clinical meaning. Every color indicator must be paired with at least one of: icon, text label, pattern, or shape. ### 3.1 Status Indicator Pattern | Status | Color | Icon | Text Label | All Three Together | |--------|-------|------|------------|-------------------| | Critical | `#DC2626` | Warning triangle | "Critical" | Red triangle + "Critical" text | | Warning | `#D97706` | Exclamation circle | "Warning" | Amber circle + "Warning" text | | Normal | `#059669` | Checkmark | "Normal" | Green check + "Normal" text | | Info | `#0284C7` | Info circle | "Pending" | Blue info + "Pending" text | ### 3.2 Chart Accessibility For clinical charts (vitals over time, lab trends): - Use different line patterns: solid, dashed, dotted, dash-dot - Add distinct markers: circle, square, triangle, diamond - Include data labels at key points - Provide tabular data alternative ### 3.3 Safe Color Pairs Instead of red/green (indistinguishable for 8% of males): | Use Case | Recommended Pair | Avoid | |----------|-----------------|-------| | Good/Bad | Blue `#2563EB` + Orange `#D97706` | Red + Green | | High contrast | Dark blue `#1E40AF` + Yellow `#CA8A04` | Red + Green | | Status range | Blue/Amber/Dark gray | Red/Yellow/Green alone | Test all palettes with Deuteranopia, Protanopia, and Tritanopia simulators (Chrome DevTools > Rendering > Emulate vision deficiencies). --- ## 4. Role-Based Access Control UI ### 4.1 UI Visibility Rules | Strategy | When to Use | |----------|-------------| | **Hide** nav items/menu entries | User's role has zero access to the feature | | **Disable** (gray out) buttons/actions | User can see but needs higher permission to act | | **Show explanation** on unauthorized attempt | User clicks disabled action or navigates via URL | Never rely on UI hiding alone for security. Server-side permission checks are mandatory. ### 4.2 Healthcare Role Hierarchy | Role | Scope | Typical Access | |------|-------|----------------| | Super Admin | System-wide | Full platform configuration | | Facility Admin | Facility | User management, facility settings, reports | | Physician | Assigned patients | Records, orders, prescriptions, referrals | | Nurse | Assigned ward/unit | Vitals, medication admin, notes, assessments | | Lab Technician | Lab module | Lab orders, results entry, specimen tracking | | Pharmacist | Pharmacy module | Medication orders, dispensing, interactions | | Receptionist | Front desk | Scheduling, check-in, demographics, billing | | Patient | Own records | View records, appointments, messages, payments | ### 4.3 Platform Integration **Web:** Use `dual-auth-rbac` skill for server-side checks + UI filtering: ```php // Server-side: always check before rendering if ($auth->hasPermission('view_patient_records')) { include 'partials/patient-records-nav.php'; } // Client-side disabled state for visible but restricted actions <button class="btn btn-primary" <?= !$auth->hasPermission('prescribe') ? 'disabled aria-disabled="true" title="Requires physician role"' : '' ?>> Write Prescription </button> ``` **Android:** Use `mobile-rbac` skill PermissionGate composable: ```kotlin PermissionGate(permission = "view_patient_records") { PatientRecordsScreen() } // Disabled button for visible but restricted actions PermissionButton( permission = "prescribe", onClick = { navController.navigate("prescribe/$patientId") }, disabledMessage = "Requires physician role" ) { Text("Write Prescription") } ``` --- ## 5. Data Privacy UI Patterns ### 5.1 Consent Management Provide granular, revocable consent toggles: ``` Consent Settings ------------------------------------------------- [ON] Share records with my care team [OFF] Share anonymized data with research [OFF] Share records with insurance provider [ON] Receive appointment reminders via SMS [OFF] Allow third-party health app access ------------------------------------------------- Last updated: Feb 15, 2026 | Version 3 [View full consent agreement] [Download my consent history] ``` **Requirements:** - Each toggle has a plain-language explanation (not legal jargon) - Revocable at any time from patient settings - All consent changes are version-tracked with timestamps - Consent state is checked server-side before any data sharing ### 5.2 Data Portability - **Export my health data** -- FHIR R4 format (JSON) or human-readable PDF - **Request data deletion** -- with clear notice of legal retention periods (e.g., "Records retained 7 years per state law") - **Data sharing dashboard** -- shows who has access to what, with revoke option ### 5.3 Privacy Notices - Layered notice: short summary visible, full policy one click away (plain language, 6th grade reading level) - Web: cookie/tracking consent banner (GDPR/state law) | Android: privacy policy in settings, consent at onboarding --- ## 6. Security UI Patterns ### 6.1 Authentication - **MFA required** for all clinical users (password + OTP or biometric) - **Session indicator:** lock icon + "Secure Session" in header - **Failed login handling:** lockout after 5 consecutive failures, CAPTCHA after 3 - **Password strength:** minimum 12 characters, complexity indicator, healthcare-grade policy ### 6.2 Sensitive Action Verification Re-authenticate before high-risk actions: | Action | Verification Method | |--------|-------------------| | Prescribing medication | Password or biometric re-entry | | Accessing restricted record | Break-the-glass flow (Section 1.3) | | Exporting patient data | Password + reason documentation | | Overriding a block | Supervisor PIN entry | | Digital signature | Credentials + displayed signer identity | ### 6.3 Copy/Paste and Screen Capture **Web:** ```html <!-- Warn on copy of PHI fields --> <input type="text" id="ssn-field" class="phi-field" oncopy="logPhiCopy('ssn', userId); showCopyWarning();" style="user-select: none;" readonly> <script> function showCopyWarning() { Swal.fire({ icon: 'warning', title: 'PHI Copy Detected', text: 'Copying protected health information has been logged.', confirmButtonColor: 'var(--clinical-primary)' }); } </script> ``` **Android:** ```kotlin // Prevent screenshots on sensitive screens window.setFlags( WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE ) // Disable copy on PHI text fields TextField( value = maskedSsn, onValueChange = {}, readOnly = true, modifier = Modifier.semantics { // Announce that copy is restricted contentDescription = "Social Security Number, last four digits, copy restricted" }, visualTransformation = SsnMaskTransformation() ) ``` --- ## 7. Per-Screen Compliance Checklist Use this checklist for **every** healthcare screen built: ### Accessibility - [ ] Color contrast verified: 4.5:1 for text, 3:1 for non-text elements - [ ] All color indicators paired with text labels and/or icons - [ ] Keyboard navigation works completely (Tab, Enter, Escape, arrows) - [ ] Visible focus indicators on all interactive elements - [ ] Screen reader announces all content correctly (test with NVDA/TalkBack) - [ ] Touch targets meet minimum 48dp (Android) / 44px (web) - [ ] Forms have visible labels, not placeholder-only - [ ] Error messages programmatically associated with fields (aria-describedby) - [ ] Loading states announced via `aria-live` regions - [ ] Skip-to-content link present (web) ### HIPAA & Privacy - [ ] PHI not exposed in URLs, page titles, or browser tabs - [ ] PHI not logged to console or included in error messages - [ ] Sensitive identifiers masked (SSN, DOB per role) - [ ] Session timeout implemented with state preservation - [ ] Audit logging for all data access and modifications - [ ] "Last accessed by" displayed on sensitive records - [ ] Minimum Necessary Rule applied (role-scoped data) ### Security - [ ] Role-based visibility applied (hide/disable per permission) - [ ] Server-side permission check backs up every UI gate - [ ] Re-authentication required for sensitive actions - [ ] FLAG_SECURE set on sensitive Android screens - [ ] CSP headers and secure cookies configured (web) - [ ] Copy/paste restrictions on PHI fields with logging - [ ] Break-the-glass flow available for emergency access --- **Cross-references:** `dual-auth-rbac` for auth patterns | `mobile-rbac` for Android permissions | `vibe-security-skill` for general web security | `design-tokens.md` for clinical color values