The ground shades from purple into a medium blue

Matches ios!144, value for value: #D6C2F4 to #82ABE0 top down, and
#1C1036 to #071426 in dark mode.

⚠️ The blue end is not decoration. The mark is a blue globe with an orange
flame, and a purple-only app left the icon as the odd one out — the one thing on
screen from a different palette. Shading into blue puts the icon's own colour on
the page instead of beside it.

🔴 SecondaryText moves again, for the third step in a row: #413A4D read 4.6:1 on
the new bottom stop, inside the margin rather than clear of it. #332E3D reads
5.5:1. The bottom stop always binds and it is where the explanatory copy sits.

The palette test checks all of it — both ends of both gradients, and that the
two platforms still agree. 4 tests, 0 failures, read from the results XML with
its age printed: an earlier run of this suite reported two failures that were
stale output from a build that had not recompiled.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Paul Hitt
2026-09-07 11:54:25 -04:00
co-authored by Claude Opus 5
parent 49854ac222
commit 4444e7e8e2
@@ -69,14 +69,21 @@ object Palette {
// The page ground, as a gradient rather than a flat fill.
//
// ⚠️ Deliberately a whisper on light. Cards are white and have to lift off
// it, and every piece of body text sits on this: ink reads 18.6:1 at the
// top and 14.8:1 at the bottom, secondary text 8.7:1 and 6.9:1. A
// saturated purple would look bolder in a screenshot and fight both.
val GroundTop = Color(0xFFE9DDFB)
val GroundBottom = Color(0xFFC6AFF2)
// Purple at the top, medium blue at the bottom — top down.
//
// ⚠️ The blue end is not decoration. The mark is a blue globe with an
// orange flame, and a purple-only app left the icon as the odd one out;
// shading into blue puts the icon's own colour on the page instead of
// beside it.
//
// 🔴 Every darkening step drags SecondaryText with it. Ink reads 12.3:1 at
// the top and 8.4:1 at the bottom, secondary text 8.1:1 and 5.5:1 — the
// bottom stop binds, and it is where most of the explanatory copy sits.
// White cards lift off the bottom at 2.4:1, which keeps a card a card.
val GroundTop = Color(0xFFD6C2F4)
val GroundBottom = Color(0xFF82ABE0)
val NightGroundTop = Color(0xFF1C1036)
val NightGroundBottom = Color(0xFF070410)
val NightGroundBottom = Color(0xFF071426)
val Teal = Color(0xFF3BD5B2) // success and presence
val Pink = Color(0xFFED497C) // the kit's alert accent
@@ -86,7 +93,7 @@ object Palette {
// ⚠️ Still needed where a gradient cannot go (a single Color parameter, a
// system bar tint). This is the gradient's mid-tone, so the two never read
// as different surfaces.
val Mist = Color(0xFFD8C6F6) // the page ground, flat
val Mist = Color(0xFFACB6EA) // the page ground, flat
val SurfaceAlt = Color(0xFFF3F3F3) // received bubbles, pills, bands
val IncomingBubble = Color(0xFFF3F3F3)
// ⚠️ A blue-tinted grey rather than the flat #F3F3F3: an initials avatar
@@ -99,7 +106,7 @@ object Palette {
// on the old bottom stop and only 3.5:1 on the darker one — below AA, and
// most of the explanatory copy on these screens is set in it. Measured now:
// 8.4:1 at the top of the gradient, 5.6:1 at the bottom.
val SecondaryText = Color(0xFF413A4D)
val SecondaryText = Color(0xFF332E3D)
// 🔴 **Dark mode had no secondary text colour at all**, and used the light
// one: #505C69 on the dark ground measures **2.41:1**, far below AA, and
// most of the explanatory copy on these screens is set in it. iOS has had
@@ -120,7 +127,7 @@ object Palette {
// Dark-mode grounds — these ARE the site's, since the site is dark.
val InkSurface = Color(0xFF121A2E) // --panel
val NightGround = Color(0xFF110A20) // --bg, the dark gradient's mid-tone
val NightGround = Color(0xFF11122E) // --bg, the dark gradient's mid-tone
}
/**