The Colors panel joins the palette — the combo ratified, and each anchor composes the halves it needs

Four rulings close Redesign Contradiction 3452893f (2026-08-06): the in-app
escape hatch is ratified in full, reversing 2026-07-29's palette-only rule —
the combo's Other… opens the system Colors panel, a pick landing on a palette
color stores the name, anything else the hex. Free-picked colors change no
contrast story: they land on the same runtime ink computation hand-written hex
always got (10 amended to say so; no warning surface is owed). Anchor
ownership: the card sidebar's background story is the combo alone — the well
grid's background half stays with the other anchors (StyleEditorView gains
showsBackground beside showsSymbols; the popover's symbol half already went to
its inline SymbolPicker). Quick-style recents stay palette-vocabulary — a
panel pick never enters them.

Claude-Session: https://claude.ai/code/session_014PtZdPwqZuqEDLc6wZMtEy
This commit is contained in:
2026-08-06 22:05:48 -04:00
parent 73698cd77b
commit 9766e1f61c
9 changed files with 981 additions and 18 deletions
+3 -3
View File
@@ -41,10 +41,10 @@ Toolbars are **pure enhancement**: every function they host already has a menu i
### Controls (settled)
One **style editor** component — a background palette grid and a curated symbol grid — presented from three anchors: **embedded** in the card window sidebar's Style section (05-card-window.md) and in the board popover's styling area, and as a **popover** opened by Style… from a card/lane context menu or the menu bar (Board ▸ Style…, ⌥⌘S — 11-command-nexus.md; selection-aware: it styles the selected cards or lane, and with nothing selected, the board). One component, one behavior, three anchors — replacing the pathfinder's swatch-row-plus-Style-popover split, whose functions were right and whose form wasn't.
One **style editor** component — a background palette grid and a curated symbol grid — presented from three anchors: **embedded** in the card window sidebar's Style section (05-card-window.md) and in the board popover's styling area, and as a **popover** opened by Style… from a card/lane context menu or the menu bar (Board ▸ Style…, ⌥⌘S — 11-command-nexus.md; selection-aware: it styles the selected cards or lane, and with nothing selected, the board). One component, one behavior, three anchors — replacing the pathfinder's swatch-row-plus-Style-popover split, whose functions were right and whose form wasn't. **The anchors compose the halves they need** (2026-08-06): the card sidebar shows the symbol grid with the **color combo** (below) standing in for the background half — the narrow context the combo was built for; the board popover shows the background half only, its symbol picker beside the rename field owning the board glyph (two surfaces writing one key in one popover would read as two settings); the Style… popover carries both grids in full.
- **Palette-only in-app**: the background grid offers the 12 palette colors — every one AA-verified through one code path (ratified 2026-07-29: palette names route through the same runtime ink-selection seam as hand-written hex — the appearance flip picks the readable label vocabulary — and PaletteContrastTests pins that the chosen ink meets AA in both appearances for all 12 backgrounds, so palette drift can never silently break it) — plus a leading **None** well that removes the `background` key. Custom hex is not pickable in-app but stays fully honored from disk (runtime contrast, 10-accessibility.md): curated in-app, unlimited on disk.
- **Curated symbol grid**: a hand-picked set (roughly five dozen kanban-relevant SF Symbols); its leading well is the level's default symbol and removes the `icon` key. Any other SF Symbol name works written by hand — named symbols the running OS knows, that is: inventories grow per macOS release, so a newer-OS name renders the level default on an older Mac, value preserved on disk — the palette stance again. No full-browser escape hatch in-app; the raw file is the escape hatch.
- **Curated-first, panel-backed** (re-ratified 2026-08-06, reversing 2026-07-29's palette-only ruling): the background grid offers the 12 palette colors — every one AA-verified through one code path (ratified 2026-07-29: palette names route through the same runtime ink-selection seam as hand-written hex — the appearance flip picks the readable label vocabulary — and PaletteContrastTests pins that the chosen ink meets AA in both appearances for all 12 backgrounds, so palette drift can never silently break it) — plus a leading **None** well that removes the `background` key. Beside the grid the vocabulary now has a second, compact form: the **color combo** — a swatch-faced popup listing None, the role's twelve, the current off-palette value verbatim when there is one, and **Other…**, which opens the system Colors panel. The panel is the in-app escape hatch the 2026-07-29 ruling withheld: a pick landing exactly on a palette color stores the *name* (so a re-pick never drifts to a hex spelling), anything else stores the hex — the same unlimited vocabulary hand-editing always had, now pickable. An arbitrary pick changes no contrast story (it lands on the identical runtime ink computation hand-written hex already gets — 10-accessibility.md), and the quick-style recents stay palette-vocabulary: a panel pick never enters them.
- **Curated symbol grid**: a hand-picked set (roughly five dozen kanban-relevant SF Symbols); its leading well is the level's default symbol and removes the `icon` key. Any other SF Symbol name works written by hand — named symbols the running OS knows, that is: inventories grow per macOS release, so a newer-OS name renders the level default on an older Mac, value preserved on disk — the palette stance again. No full-browser escape hatch in-app; the raw file is the escape hatch. (Symbols keep this stance deliberately — the 2026-08-06 color-panel reversal above is colors only: the system offers a Colors panel worth deferring to, and no symbol browser of equal standing.)
- **Off-palette values display leniently**: a hand-written hex background or uncurated symbol shows as the current value in the editor (labeled verbatim, outside the grids); choosing any well replaces it.
- **Batch edits**: a multi-selection shows per-dimension mixed state (no well selected, "—" where a value would read); choosing a well applies to the whole selection — one gesture, one commit on git boards.
- **The Style… popover tracks its target set live and dismisses when it empties** (settled): its target is the selection, re-resolved across reloads by 02-architecture.md's UUID rule — a member that vanishes or flips liveness leaves the set and the mixed-state display recomputes; a set emptied by a foreign reload dismisses the popover (the inline-rename discard applied here) — it never silently retargets to the board, and nothing writes into a vanished folder (a member moved to the trash leaves the set like any other departure). **The read-only lock instead disables its wells in place** (settled): a popover open when the lock lands stays open, content disabled — the banner names why, and the lock never yanks a surface (the Edit buffer's keeps-its-place posture). The embedded anchors need no rule of their own: the card sidebar dismisses with its card's window, and the board popover's target is the board itself.
+1 -1
View File
@@ -63,7 +63,7 @@ Stacked sections under small-caps headers, in this order; quiet rows, read-optim
### Style
The card-level styling home: the **embedded style editor** — background palette grid (with the leading None well) and curated symbol grid, per 03-board-ui.md ▸ Styling ▸ Controls. Card styling is discoverable here without a context menu; the same component appears in the board popover and behind Style….
The card-level styling home: the **Background color combo** over the **curated symbol grid** (03-board-ui.md ▸ Styling ▸ Controls, its 2026-08-06 anchor-ownership rule) — the sidebar is exactly the narrow context the combo was built for, so it stands in for the well grid's background half here, panel escape hatch included; the full grid remains the surface at the other anchors. Card styling is discoverable here without a context menu; the component family is shared with the board popover and Style….
### Details — unknown frontmatter keys
+1 -1
View File
@@ -45,7 +45,7 @@ The stance is committed in 00-vision.md: **accessibility is a requirement of "na
- **Full relative scaling** (decided): relative text styles everywhere, no fixed point sizes. Card face, lane header, and masonry metrics derive from font metrics, so layout survives the largest system text sizes; the no-horizontal-scroll invariant is untouched (lane count is the user's choice; lanes scroll vertically), and 03-board-ui.md's graceful-truncation rules apply at every scale.
- **Board zoom is that scaling's user-facing control** (settled 2026-08-02; behavior in 03-board-ui.md ▸ Layout, rows in 11-command-nexus.md). macOS ships no system text-size setting, so the commitment above had nothing to move it — the point size the whole board derives from is read once and never changes. View ▸ Zoom In / Zoom Out / Actual Size supply the multiplier: one ladder, one effective body size, and every em multiple and every text style scaling off it together. This is an accessibility feature before it is a convenience one, which is why it is a first-class menu command with a chord rather than a setting buried in a pane, and why **the strip's own truncation rules are the acceptance test** — 03's graceful-truncation promise "at every scale" is only checkable now that a scale exists. **A zoom change announces its new level** ("Zoom 125%") through the ordinary announcement path: it is chrome, not information — nothing about the board's meaning changes — so no label, value, or trait anywhere else moves with it.
- **Contrast is pinned to WCAG AA.** Lane and card colors render as edge accents (03-board-ui.md's top-edge band / left-edge stripe), so text never sits on them — they are supplementary decoration, never the sole carrier of information, and carry no text-contrast obligation. The ≥ 4.5:1 automatic-contrast rule binds where text does sit on a user-chosen color: the **board** background (palette pairs verified at design time; arbitrary hex computes its text color at runtime against that threshold). An `#RRGGBBAA` background with alpha computes against the color **composited over its effective backdrop** in the active appearance (the board's over the window background; light and dark resolve differently), recomputed on appearance change. Palette names and hex share one ink-selection code path — the palette's AA claim is pinned by a computed-contrast test over all 12 backgrounds in both appearances (ratified 2026-07-29). **The AA obligation binds the primary label tier** (ruled 2026-07-29): the ink seam moves the whole label hierarchy with the primary, and subordinate tiers (.secondary, .quaternary) inherit the system vocabulary's own contrast posture, which sits below 4.5:1 on any background including the system's — the platform-standard reading; the strict path for users who need more is Increase Contrast, which raises accents and washes to full alpha (▸ Visual accommodations). Increase Contrast strengthens borders and the selection indicator.
- **Contrast is pinned to WCAG AA.** Lane and card colors render as edge accents (03-board-ui.md's top-edge band / left-edge stripe), so text never sits on them — they are supplementary decoration, never the sole carrier of information, and carry no text-contrast obligation. The ≥ 4.5:1 automatic-contrast rule binds where text does sit on a user-chosen color: the **board** background (palette pairs verified at design time; arbitrary hex computes its text color at runtime against that threshold). The 2026-08-06 color-combo reversal (03 ▸ Styling ▸ Controls) changes none of this: a panel-picked color is stored as the palette name when it lands on one, else as hex, and either spelling renders through the same runtime ink seam — in-app picking gained the freedom hand-editing always had, and the ink math was already waiting for it. No warning surface exists or is owed; the app's answer to a low-contrast pick is to choose readable ink, not to argue. An `#RRGGBBAA` background with alpha computes against the color **composited over its effective backdrop** in the active appearance (the board's over the window background; light and dark resolve differently), recomputed on appearance change. Palette names and hex share one ink-selection code path — the palette's AA claim is pinned by a computed-contrast test over all 12 backgrounds in both appearances (ratified 2026-07-29). **The AA obligation binds the primary label tier** (ruled 2026-07-29): the ink seam moves the whole label hierarchy with the primary, and subordinate tiers (.secondary, .quaternary) inherit the system vocabulary's own contrast posture, which sits below 4.5:1 on any background including the system's — the platform-standard reading; the strict path for users who need more is Increase Contrast, which raises accents and washes to full alpha (▸ Visual accommodations). Increase Contrast strengthens borders and the selection indicator.
- **State is never color-alone**: selection is a ring plus trait, cut-pending is dim plus stated value, the trash header is hatched plus labeled — all already patterned; kept as a rule.
- **Reduce Motion is a per-voice rule, not a feature list** (settled): movement animations go **instant**, appear/disappear transitions go **crossfade**, uniformly — every animated surface derives its reduced variant from its voice, the store's reload seam included (the largest animated surface in the app), so new surfaces never need individual rulings. The named cases — reflow-on-drag, search animate-out, the drag replica's lift and settle transitions (its 1:1 tracking never animates, like the selection marquee, which needs no variant — 03-board-ui.md ▸ Motion), the lane-resize rubber-band feedback (03-board-ui.md ▸ Lane), trash animations — are applications of the rule, not the rule itself. **Reduce Transparency**: glass underlays go solid, wherever they appear.
- **Full Keyboard Access** (independent of VoiceOver): the board is one tab stop with arrow-key navigation within; every control — lane buttons, popover, card window, welcome — is Tab-reachable. **"Every control" is literal and includes banner-row buttons** (ruled 2026-07-29): a Dismiss or Cancel on a banner must be a Tab stop — FKA serves sighted keyboard-only users, to whom VO custom actions are invisible, and Cancel on an in-progress operation is exactly the control that cannot require a pointer. This coexists with the VoiceOver presentation (one combined row-sentence with Dismiss/Cancel as custom actions): the AX combine and the FKA focus loop are independent surfaces; the implementation may uncombine conditionally under FKA if the focus system requires it.