Implement visual accommodations and Full Keyboard Access

Full relative text scaling per DESIGN/10: BoardMetrics is the board
strip's geometry as a pure function of the body point size
(CardWindowMetrics' twin) — lane plate/header/band, card
corner/stripe/padding, masonry spacing, the drop model's nominal card
height, resize-handle geometry, trash hatch pitch, and both window
floors all derive from an em; CardFaceMetrics folded in. The two fixed
font sizes (welcome brand/glyph) went relative; the toolbar search
field is 17 ems like the transient bar's. The no-horizontal-scroll
invariant is pinned by test at six text sizes by twelve lane counts.

Accommodations is Motion's sibling for the visual settings: Increase
Contrast adds a flat point to strokes (monotone, hierarchy-preserving),
gives borderless card/lane plates a resting separator hairline, and
takes faded accents to full alpha; Reduce Transparency turns the
transient search bar's glass solid and does the same for the alpha
washes that composite over a user-chosen board background (trash plate,
hatched header, drag shadow). Reduce Motion audited — every animated
surface already routes through Motion with a reduced variant; no gaps.

Full Keyboard Access: the template chooser's tiles were pointer-only —
now focusable, arrow-navigable (clamped, StyleWellGrid's rule), Space
picks, Return stays the sheet's default action, focus names the
selection one-way. The board's single tab stop shows its focus ring
under FKA (focusEffectDisabled inverts). Style editor verified already
conformant. Edge accents verified text-free; trash hatch pitch now
font-derived so it still reads as hatching at large text.

1549 unit tests green, both schemes build.

Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
This commit is contained in:
2026-07-29 08:48:25 -04:00
parent c339b4cecf
commit 8564814754
21 changed files with 1419 additions and 218 deletions
+11 -1
View File
@@ -48,7 +48,17 @@ final class LaneDropRegistry {
/// The height a card with no registered measurement is assumed to have a lane whose faces have
/// not laid out yet. Nominal rather than zero, so the resting rows still tile.
static let nominalCardHeight: CGFloat = 44
///
/// **Font-derived** (`BoardMetrics.nominalCardHeight`, 10-accessibility.md's full-relative-scaling
/// rule), and it matters more here than at most sites: this is the stand-in the drop model tiles
/// rows with before anything has measured itself, so a figure fixed at 13pt's 44 points would put
/// every un-measured slot boundary in the wrong place at a large system text size and the drop
/// model is forbidden from reading measured frames mid-flight (03-board-ui.md § Motion), so this
/// guess is all it has until the lane lays out.
@MainActor
static var nominalCardHeight: CGFloat {
BoardMetrics.nominalCardHeight(bodyPointSize: BoardMetrics.bodyPointSize)
}
/// The board strip's own frame in the window's SwiftUI global space the origin the strip
/// coordinates `DropSlotMath.laneExtents` is written in are measured from. It lives here rather