commit d9b0aaff4707d88af79842ad3576c61ba230276c
parent 01a04be3a139e688076faa2a120b8354002b63c9
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Fri, 25 Sep 2026 23:12:09 -0400
common: light success and warning read at 4.5:1 on their own soft fills, as sepia and dark do; the token test covers every base (brand S3)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Diffstat:
2 files changed, 16 insertions(+), 10 deletions(-)
diff --git a/common/components/themeTokens.test.ts b/common/components/themeTokens.test.ts
@@ -210,10 +210,10 @@ test("text tokens read at 4.5:1 on each base's ground and card", () => {
});
// "Soft = a low-alpha tint of the hue for badge/alert fills; the base color is
-// tuned to read as TEXT on that soft fill" (tokens.css @theme). Sepia and dark
-// keep that promise on the ground and on a card. The light block's success and
-// warning (4.18 / 3.89 on their soft fills) predate the base × accent themes
-// and are not covered here.
+// tuned to read as TEXT on that soft fill" (tokens.css @theme). Every base
+// keeps that promise on the ground and on a card. (Light's success and warning
+// read 4.18 / 3.89 on their fills until the brand's integrate slice deepened
+// them.)
function compositeOver(rgba: string, ground: string): string {
const m = /^rgba\((\d+),\s*(\d+),\s*(\d+),\s*([\d.]+)\)$/.exec(rgba);
assert.ok(m, `not an rgba(): ${rgba}`);
@@ -224,7 +224,7 @@ function compositeOver(rgba: string, ground: string): string {
return `#${out.map((v) => v.toString(16).padStart(2, "0")).join("")}`;
}
-test("status text reads at 4.5:1 on its own soft fill (sepia and dark)", () => {
+test("status text reads at 4.5:1 on its own soft fill, on every base", () => {
const PAIRS = [
["--success", "--success-soft"],
["--warning", "--warning-soft"],
@@ -232,14 +232,17 @@ test("status text reads at 4.5:1 on its own soft fill (sepia and dark)", () => {
["--destructive", "--destructive-soft"],
["--state-gone", "--state-gone-soft"],
] as const;
- for (const base of ["sepia", "dark"] as const) {
+ let checks = 0;
+ for (const base of BASE_GROUND_IDS) {
const r = rule(BASE_SELECTOR[base]);
for (const bg of ["--background", "--card"]) {
for (const [text, soft] of PAIRS) {
+ checks++;
const fill = compositeOver(r.decls.get(soft)!, r.decls.get(bg)!);
const ratio = contrastRatio(r.decls.get(text)!, fill);
assert.ok(ratio >= 4.5, `${base}: ${text} on ${soft} over ${bg} is ${ratio.toFixed(2)}:1`);
}
}
}
+ assert.equal(checks, 30); // 3 bases × 2 grounds × 5 pairs
});
diff --git a/common/styles/tokens.css b/common/styles/tokens.css
@@ -176,12 +176,15 @@ html[data-base="light"] {
--panel: rgba(255, 255, 255, 0.78);
--panel-2: rgba(232, 237, 239, 0.78);
- --success: #1f7a4d;
+ /* Each deep enough to read at 4.5:1 on its OWN soft fill over the ground
+ and the card, as the @theme note promises (themeTokens.test.ts): success
+ 4.74 / 5.12, warning 4.75 / 5.14, info 5.20 / 5.62. */
+ --success: #1c7046;
--success-foreground: #ffffff;
- --success-soft: rgba(31, 122, 77, 0.12);
- --warning: #96650b;
+ --success-soft: rgba(28, 112, 70, 0.12);
+ --warning: #825809;
--warning-foreground: #ffffff;
- --warning-soft: rgba(150, 101, 11, 0.14);
+ --warning-soft: rgba(130, 88, 9, 0.14);
--info: #1c5f96;
--info-foreground: #ffffff;
--info-soft: rgba(28, 95, 150, 0.12);