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:
@@ -14,21 +14,32 @@ import SwiftUI
|
||||
/// target stays live beneath them"), and a shadow that swallowed the release would strand the drop.
|
||||
struct DragShadow: View {
|
||||
|
||||
/// Matched to the surface it stands in for — a lane's plate is 10, a card's is 8.
|
||||
var cornerRadius: CGFloat = 10
|
||||
/// Matched to the surface it stands in for — the lane's plate radius or the card's, both
|
||||
/// font-derived (`BoardMetrics`) and both supplied by the caller, which is the one that knows
|
||||
/// which surface this shadow is standing in for.
|
||||
let cornerRadius: CGFloat
|
||||
|
||||
/// A drop proposal (dashed) or a resting footprint (plain).
|
||||
var dashed: Bool = true
|
||||
|
||||
/// Increase Contrast and Reduce Transparency, for the outline and the fill below
|
||||
/// (10-accessibility.md; `Accommodations`). A shadow's whole job is to say "here", and both
|
||||
/// halves of how it says so are alpha-based by default.
|
||||
@Environment(\.colorSchemeContrast) private var contrast
|
||||
@Environment(\.accessibilityReduceTransparency) private var reduceTransparency
|
||||
|
||||
var body: some View {
|
||||
RoundedRectangle(cornerRadius: cornerRadius)
|
||||
.fill(.quaternary.opacity(0.5))
|
||||
.fill(Accommodations.dragShadowWash(reduceTransparency: reduceTransparency).style)
|
||||
.overlay {
|
||||
if dashed {
|
||||
RoundedRectangle(cornerRadius: cornerRadius)
|
||||
.strokeBorder(
|
||||
Color.accentColor.opacity(0.55),
|
||||
style: StrokeStyle(lineWidth: 1.5, dash: [6])
|
||||
Color.accentColor.opacity(Accommodations.accentOpacity(0.55, contrast: contrast)),
|
||||
style: StrokeStyle(
|
||||
lineWidth: Accommodations.borderWidth(1.5, contrast: contrast),
|
||||
dash: [6]
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -46,16 +57,24 @@ struct DragCountBadge: View {
|
||||
|
||||
let count: Int
|
||||
|
||||
/// The badge is a disc around a numeral, so every figure in it follows the numeral's font
|
||||
/// (`BoardMetrics`, 10-accessibility.md's full-relative-scaling rule) — at the standard body size
|
||||
/// they are the 6, 3 and 10 points it has always drawn.
|
||||
private var pointSize: CGFloat { BoardMetrics.bodyPointSize }
|
||||
|
||||
var body: some View {
|
||||
if count > 1 {
|
||||
Text("\(count)")
|
||||
.font(.caption2.bold())
|
||||
.monospacedDigit()
|
||||
.foregroundStyle(.white)
|
||||
.padding(.horizontal, 6)
|
||||
.padding(.vertical, 3)
|
||||
.padding(.horizontal, BoardMetrics.em(0.45, bodyPointSize: pointSize))
|
||||
.padding(.vertical, BoardMetrics.em(0.25, bodyPointSize: pointSize))
|
||||
.background(Circle().fill(Color.accentColor))
|
||||
.offset(x: 10, y: -10)
|
||||
.offset(
|
||||
x: BoardMetrics.em(0.75, bodyPointSize: pointSize),
|
||||
y: -BoardMetrics.em(0.75, bodyPointSize: pointSize)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user