The two pickers rhyme — one two-zone chrome, a face onto a standalone browser, a trigger onto the popover

The colour combo was a wide two-zone field with a second door onto the Colors
panel; the symbol picker was a small square button with one. Both now subclass
one `ComboFieldControl`, so they are the same width, height, radius and trigger
by construction: click the face for the standalone picker, click the chevron for
the quick list. The symbol face opens a new floating browser over the OS's own
category, ordering and keyword plists out of CoreGlyphs.bundle — searchable,
categorised, trademark-restricted glyphs withheld.

The palette grows twelve to sixteen per table, filling the hue ring's four
widest gaps with lime, jade, indigo and magenta at each table's own saturation
and brightness. That gives the Style… popover's background grid a third row and
the tint grid its third row of four, and both grids gain an Other… row onto the
system colour picker — which the card sidebar's combo has had all along and the
primary styling surface never did. An arbitrary hex already round-tripped; it is
asserted now, including that an unquoted one is a YAML comment and no value.

Claude-Session: https://claude.ai/code/session_014PtZdPwqZuqEDLc6wZMtEy
This commit is contained in:
2026-08-09 09:44:30 -04:00
parent ca0328be2e
commit ece33bbf78
17 changed files with 2030 additions and 322 deletions
+14 -10
View File
@@ -21,8 +21,8 @@ import Testing
/// paints (palette name and hand-written hex alike, one path), that a translucent one resolves
/// differently in the two appearances, and that a board painting nothing is left alone.
///
/// The palette's own suite closes the loop. 03-board-ui.md Styling Controls promises the twelve
/// wells are "AA-verified at design time pinned by a computed-contrast unit test over all 12
/// The palette's own suite closes the loop. 03-board-ui.md Styling Controls promises the palette
/// wells are "AA-verified at design time pinned by a computed-contrast unit test over all
/// pairs"; a pair is a background *and its ink*, so the promise is only keepable by checking the ink
/// the seam chooses which `PaletteContrastTests` does, in both appearances. That is the whole of
/// the difference between the two paths: the palette is a fixed set and can be checked in advance,
@@ -384,7 +384,7 @@ struct BoardTextInkTests {
#expect(BoardTextInk.paintedColor(.valid("#1e1e1e")) != nil)
#expect(BoardTextInk.paintedColor(.valid("smokey-ocean")) != nil)
#expect(BoardTextInk.paintedColor(.valid("chalk")) != nil)
// A foreground-table name in the `background` field resolves, because the 12+12 split is a
// A foreground-table name in the `background` field resolves, because the two-table split is a
// picker split and not a namespace (`Palette`) so it paints, so it decides an ink.
#expect(BoardTextInk.paintedColor(.valid("carnation")) != nil)
#expect(BoardTextInk.paintedColor(.valid("#12345")) == nil)
@@ -479,21 +479,25 @@ struct BoardTextInkTests {
struct PaletteContrastTests {
/// **03-board-ui.md Styling Controls' promise, computed and this test *is* the promise**:
/// "the background grid offers the 12 palette colors every pair AA-verified at design time
/// (10-accessibility.md), the claim pinned by a computed-contrast unit test over all 12 pairs so
/// "the background grid offers the palette colors every pair AA-verified at design time
/// (10-accessibility.md), the claim pinned by a computed-contrast unit test over all pairs so
/// palette drift can never silently break it."
///
/// **This is also the gate the palette grew through.** Four backgrounds were added on 2026-08-09;
/// the reason that was a safe thing to do is that a new well with no readable ink fails here
/// rather than shipping, which is exactly the first failure mode named below.
///
/// A **pair** is a background and the ink its text is drawn in, so the claim cannot be settled by
/// a table of colours alone only by the code that chooses the ink. What is asserted here is
/// therefore end to end and in both appearances: for every well, the scheme `BoardTextInk`
/// *selects* clears 4.5:1 on the colour that well paints. Nothing weaker would be the design's
/// claim; nothing stronger is true, since no single ink reads on all twelve.
/// claim; nothing stronger is true, since no single ink reads on all of them.
///
/// Two ways to fail, both of them the point. A **new well** whose colour has no readable ink at
/// all a mid-grey, the dead zone `InkChoiceTests.neitherPassingTakesTheHigherRatio` documents
/// fails here instead of shipping. And a regression in the *selection* fails here too: with the
/// appearance-native label, which is what the board drew before this card, every one of the
/// twelve failed in one appearance (ten dark wells under Aqua, `chalk` and `aluminum` under Dark
/// wells failed in one appearance (every dark well under Aqua, `chalk` and `aluminum` under Dark
/// Aqua), so this test would have caught the m4 bug it now guards against returning.
@Test("The ink the board picks clears 4.5:1 on every palette background, in both appearances")
func everyPaletteBackgroundHasAReadableInk() throws {
@@ -528,8 +532,8 @@ struct PaletteContrastTests {
/// The other half of "verified at design time": **the appearance-native label is not enough**,
/// which is why the selection has to happen at all.
///
/// Every one of the twelve wells is a colour the system's own label fails on in one of the two
/// appearances the ten dark ones under Aqua, `chalk` and `aluminum` under Dark Aqua. Stating
/// Every well is a colour the system's own label fails on in one of the two
/// appearances the dark ones under Aqua, `chalk` and `aluminum` under Dark Aqua. Stating
/// it as a test keeps the reasoning from decaying into folklore: if a future palette were tame
/// enough that the native label always worked, this would fail and the seam's board-side wiring
/// could be reconsidered rather than carried on faith.
@@ -548,7 +552,7 @@ struct PaletteContrastTests {
}
}
/// The twelve are opaque, which is why the design can verify them at all: a palette well with
/// Every well is opaque, which is why the design can verify them at all: a palette well with
/// alpha would make its own contrast a function of the appearance's window background, and
/// "verified at design time" would stop being a statement anyone could check.
@Test("No palette background carries alpha")