# HoffDesk Dashboard β€” Accessibility Compliance Review **Reviewer:** Wadsworth πŸ“‹ (routed on behalf of Daedalus's domain) **Date:** 2026-04-20 **Scope:** Sprint 1 dashboard UI (`dashboard/templates/index.html`, `dashboard/static/style.css`, `prototype/index.html`) **Standard:** WCAG 2.1 AA --- ## Executive Summary The dashboard is early-stage but has a solid semantic foundation. There are **3 critical issues**, **5 serious issues**, and **8 moderate issues**. Several quick wins can be knocked out in a single pass. The biggest structural gap is **no focus indicators anywhere** and **color contrast failures on secondary/tertiary text**. --- ## 1. WCAG 2.1 AA β€” Color Contrast ### ❌ CRITICAL: Tertiary text fails 4.5:1 ratio | Token | Hex | Use | Contrast on `#0f1117` | Pass? | |-------|-----|-----|------------------------|-------| | `text-primary` | `#e8e9ed` | Headlines | 13.3:1 | βœ… | | `text-secondary` | `#9ca0b0` | Timestamps, labels | 5.3:1 | βœ… | | `text-tertiary` | `#6b7084` | Muted info | 3.5:1 | ❌ FAIL | | `text-inverse` | `#0f1117` | On accent bg | 1.1:1 | ⚠️ Only works on light bg | - **`text-tertiary` (`#6b7084`)** on `bg-primary` (`#0f1117`) = **3.5:1** β€” fails AA for normal text (requires 4.5:1). Used for: skeleton placeholders, location text, forecast labels, footer text, health latency labels, disk usage labels. - **Fix:** Lighten `text-tertiary` to at least `#7f8396` (4.5:1) or `#828698` (4.54:1). Recommended: `#8b8fa3` (~5:1 for comfortable reading). ### ⚠️ Warning: Prototype uses different palette The prototype (`prototype/index.html`) uses a different color system (Midnight Teal, Forest Emerald, Morning Peach, etc.) with `color: #ECB392` on `background-color: #043C5C`. This passes at **4.78:1** β€” barely. But `sage-mist` (`#CAD0AD`) on `#043C5C` = **5.6:1** βœ… and `text-[10px]` size classes throughout the prototype are particularly risky (see Touch Targets below). ### ⚠️ Warning: Status colors on dark bg Status pills use colored text on semi-transparent tinted backgrounds: - `status-healthy` (`#22c55e`) on `status-healthy-bg` (`rgba(34,197,94,0.12)` β‰ˆ `#161f1c`): β‰ˆ **7.3:1** βœ… - `status-degraded` (`#f59e0b`) on `status-degraded-bg`: β‰ˆ **6.1:1** βœ… - `status-critical` (`#ef4444`) on `status-critical-bg`: β‰ˆ **5.2:1** βœ… These are fine. βœ… --- ## 2. WCAG 2.1 AA β€” Focus Indicators ### ❌ CRITICAL: No visible focus indicators The CSS has **zero `:focus-visible` or `:focus` styles**. There is no custom focus ring on any interactive element. The default browser focus ring on dark backgrounds is often invisible. **Affected elements:** - HTMX-powered `
` cards (they have `hx-get` attributes β€” are they focusable?) - The "Add Event" button in the prototype - Category toggle buttons in the prototype - Any links or buttons injected by JS renderers **Fix:** Add a visible `:focus-visible` outline: ```css :focus-visible { outline: 2px solid var(--accent-primary); outline-offset: 2px; } ``` ### ⚠️ SERIOUS: HTMX sections may not be keyboard-focusable The `
` elements with `hx-get` attributes are not inherently focusable. HTMX will swap their innerHTML on load and poll, but if these are meant to be interactive (e.g., clicking into a calendar event), they need `tabindex="0"` and keyboard event handlers. If they're purely passive displays, they're fine as-is. --- ## 3. Screen Reader Compatibility ### ❌ CRITICAL: Dynamic content has no ARIA live regions HTMX swaps content via `hx-swap="innerHTML"` into `#calendar-content`, `#weather-content`, `#health-content`. When these update every 30 seconds, **screen readers will not announce the changes**. **Fix:** Add `aria-live="polite"` to each content container: ```html
``` ### ⚠️ SERIOUS: Prototype "Add Event" form has no accessible name The collapsible form section has no `aria-expanded` or `aria-controls`: ```html
``` ### ⚠️ SERIOUS: Loading indicators need ARIA The `.htmx-indicator` elements (loading dots) should have `aria-hidden="true"` since they're purely decorative, and the content area should have `aria-busy="true"` during loading. ### ⚠️ MODERATE: Landmark regions are good The dashboard correctly uses `
`, `
`, and `