← Back to Index

Accessibility & CSS-Hygiene Audit

Blazor scaffold — alpa-components.css (2,539 lines) + Components/**/*.razor vs. the CLAUDE.md CSS rules & WCAG AA

ALPA Mobile · UI Refresh Epic AB#1821 · Updated: 2026-08-28 06:30 ET · Audited: 2026-08-28 06:30 ET

Capture-only audit. No live CSS/Razor changed. The mock UI is in flight, so this is a findings pass — every fix is classified SAFE (no visual change: move an inline style to an identical class, add :focus-visible, add C# state, heightmin-height that doesn't reflow at base size) or GATED (visual/contrast/sizing change needing design alignment — folded to the relevant DQ). Rules: CLAUDE.md "Blazor / CSS class rules"; WCAG 2.2 AA. Ties: DQ-18 (nav contrast) · DQ-21 (text scaling).
Summary. CSS hygiene is mostly clean — the flagged "duplicate selectors" are false positives (descendant selectors, grouped-then-specific pseudo blocks, and reduced-motion @media overrides); no real silent-override bug exists. The genuine hygiene items are inline styles (concentrated in the two skeleton components) and one un-wired interactive control (Remember-Me). Accessibility has one High-severity systemic gap: only 3 :focus rules in 2,539 lines (all on text inputs) — no visible keyboard focus on buttons, nav items, pills, tabs, toggles or cards. Counts: hygiene 4 findings · a11y 6 findings · 6 Safe / 4 Gated.
Status update (2026-08-28). Since this audit, three things landed and one prior finding turned out to be a defect rather than a capability gap:

Still open, unchanged: B2 (nav-active contrast, gated on DQ-18) and B4 (sub-minimum tap targets, gated on design sizing).

A · CSS Hygiene (CLAUDE.md rules)

FindingLocationSevRuleProposed fixClass
Static inline style= — ~26 in the skeleton components (widths, gaps, fixed heights) + 2 in ActionButton (display:block;width:100%;height:100%) Library/HomeSkeleton.razor, Library/ComponentSkeleton.razor, Library/ActionButton.razor:21,25 Med No inline style= Move to named classes / a small set of skeleton width-utilities in the Utility block. Keep only genuinely dynamic @-bound styles. SAFE
Legit dynamic inline styles (not violations) — --alpa-grid-cols:@Vm.Columns, width:@PercentClamped%, width:@MaxDPPercent%, skeleton width:@(45+i*7)% GridContainer:6, CumulativeBar:18, DutyPeriodCard:29, HomeSkeleton:45 (allowed — value is data-bound) No action. Bound values legitimately belong inline (or as a CSS custom property, already done for grid-cols). OK
Duplicate selectors — NO real bug. uniq -d flags were descendant selectors (.alpa-nav-item--active .alpa-nav-label vs … .alpa-nav-icon), grouped-then-specific pseudo blocks (::before,::after then ::after), and reduced-motion overrides. .alpa-sk/.alpa-home-content/.alpa-reveal dupes are inside @media (prefers-reduced-motion) (L513); .alpa-flight-card-line::after L1639/1650 is grouped+specific Info No duplicate silent-override None — verified clean. (Documented so the next audit doesn't re-flag the false positives.) OK
Interactive control without C# state — Remember-Me checkbox is a bare <input type="checkbox">, no @bind/@onclick/backing field Pages/LoginPage.razor:63 Med Interactive controls must have state Add a bool backing field + @bind (persist via SettingsService.RememberMe). Part of the login-scaffold wiring (see login data-gap §4). Note: the --active classes in KcmHomePage/MecHotelsPage are data-driven display states (e.g. "ACTIVE" airport badge), not tap-toggles — no violation. SAFE

B · Accessibility (WCAG 2.2 AA)

FindingLocationSevCriterionProposed fixClass
No visible keyboard focus on most controls — only 3 :focus rules in 2,539 lines, all on text inputs. Buttons, bottom-nav items, top-nav buttons, pills, tab-group tabs, toggles, action buttons and cards have no focus style. alpa-components.css — only L618 (login-input), L1447 (jsff-input), L2243 (ftdt-input) High 2.4.7 Focus Visible; 2.1.1 Keyboard Add a shared :focus-visible outline utility applied to all interactive controls. :focus-visible shows only on keyboard focus — no change to mouse/touch appearance, so visually safe. SAFE
Bottom-nav active contrast — active label/icon use var(--alpa-blue) #007bc2 on navy #05273e3.3:1 (fails AA for the small label). Design base is white label + light-blue #c7dff6 icon (passes). .alpa-nav-item--active L1513–1518 High 1.4.3 Contrast Adopt the design's light-blue base + a compliant active treatment. Gated on the active-state decision — DQ-18. GATED
Fixed heights block text reflow — 36 height:NNpx rules; the text-bearing ones clip when OS text scales (WebView ignores Dynamic Type entirely). e.g. cards/rows/labels at L192,344,350,573,860,1055,1338,1361 (icons/bars/dividers excluded) Med-High 1.4.4 Resize Text; 1.4.10 Reflow Safe: swap fixed heightmin-height on text containers (no base-size reflow). Gated: the WebView Dynamic-Type scale-bridge — DQ-21. SAFE / GATED
Sub-minimum tap targets — several interactive controls below the 24×24px AA floor (and 44×44 comfort): top-nav button 40px, --button-sm/inputs 36px, and smaller pills/rows (32/28/26/22/20px). e.g. .alpa-top-nav-btn L1121 (40), inputs L1457/2128 (36), L271 (32), L741 (28) Med 2.5.8 Target Size (Minimum) Ensure interactive controls meet ≥24×24 (prefer 44×44) via padding/min-size, or an invisible hit-area. Sizing is visual → confirm against design. GATED
Icon-only controls — label verification — 66 aria attrs + 13 roles present and all 22 <img> have alt (good baseline). Spot-check icon-only buttons (e.g. top-nav SVG buttons) for an aria-label, and custom interactive <div>s for role/tabindex. .alpa-top-nav-btn (svg-only) + any tappable non-button element Low-Med 4.1.2 Name, Role, Value; 1.1.1 Add aria-label to icon-only buttons lacking a text node; ensure custom-interactive elements expose role + keyboard. SAFE
Reduced-motion respected (positive) — a @media (prefers-reduced-motion: reduce) block (L513) neutralizes the skeleton/reveal animations. alpa-components.css L513 Info 2.3.3 Animation from Interactions None — keep. Extend the block if new animations are added. OK

Recommended order

Audited 2026-07-01 against scaffold branch feat/AB1821-login-scaffold-01 · alpa-components.css @ 2,539 lines · UI Refresh Epic AB#1821. Capture-only — no live values changed. Status refreshed 2026-08-28 against imp/blazor-hybrid — see the callout above.