P0-1 fix: ledger/u grade badges are token-derived — kill blue-B / amber-C / non-A glow

Phone audit found LEDGER READ CARDS rendering B badges with BLUE borders + C
with AMBER right now in prod — GradePill (components/GradeCard.tsx) hardcoded the
OLD palette (rgba(74,158,255) blue-B, rgba(255,179,71) amber-C) for bg/border
while the text used the migrated token. Migrated bg/border to color-mix on the
grade token, so B renders neutral-white and C grey (matching the board).
- globals.css .grade-*-bg → token-derived color-mix (was raw blue/amber rgba).
- DELETED glow from .grade-glow-b/c/d (glow is A-tier ONLY, by law) — B/C/D keep
  their token color, no text-shadow.
- Purged the last dead grade-blue #4A9EFF fallbacks (SoccerGradeResult, the
  intelligence INFO dot).
- REGRESSION LOCK: vyndrParityQA fails if #4a9eff / rgba(74,158,255) reappears
  anywhere in web/src, if GradePill hardcodes blue/amber rgba, or if grade-glow
  B/C/D grow a text-shadow again.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kev
2026-07-17 00:48:56 -04:00
parent 4f32eab859
commit 489d849f1e
5 changed files with 64 additions and 15 deletions
+41
View File
@@ -205,3 +205,44 @@ describe('M4 — mobile structural rules locked (source, not visual)', () => {
expect(css).toMatch(/@media\s*\(max-width:\s*767px\)\s*\{[^]*?html,\s*body\s*\{[^}]*overflow-x:\s*hidden/);
});
});
// ── P0-1 regression — blue-B / amber-C grade colors must never return ─────
describe('P0-1 — grade badges are token-derived; blue-B / amber-C banished', () => {
const css = read('app/globals.css');
it('the dead grade-blue #4a9eff / rgba(74,158,255) appears NOWHERE in web/src', () => {
const banned = /#4a9eff|rgba\(\s*74\s*,\s*158\s*,\s*255/i;
const offenders = [];
const walk = (d) => {
for (const e of fs.readdirSync(d, { withFileTypes: true })) {
const fp = path.join(d, e.name);
if (e.isDirectory()) { walk(fp); continue; }
if (!/\.(tsx?|css|js)$/.test(e.name)) continue;
if (banned.test(fs.readFileSync(fp, 'utf8'))) offenders.push(path.relative(WEB, fp));
}
};
walk(WEB);
expect(offenders).toEqual([]);
});
it('GradePill (ledger/u cards) derives bg/border from the grade token, no old-palette rgba', () => {
const gc = read('components/GradeCard.tsx');
// the B/C tiers must NOT hardcode blue/amber rgba; must go through color-mix on the token
expect(gc).not.toMatch(/rgba\(\s*74\s*,\s*158\s*,\s*255/); // blue-B
expect(gc).not.toMatch(/bg:\s*'rgba\(\s*255\s*,\s*179\s*,\s*71/); // amber-C badge bg
expect(gc).toMatch(/color-mix\(in srgb, \$\{tok\}/);
});
it('GLOW is A-tier only — grade-glow-b/c/d carry NO text-shadow', () => {
const glowBlock = css.slice(css.indexOf('.grade-glow-aplus'), css.indexOf('.grade-glow-d') + 60);
expect(glowBlock).toMatch(/\.grade-glow-b\s*\{\s*color:[^}]*\}/);
expect(glowBlock).not.toMatch(/\.grade-glow-b\s*\{[^}]*text-shadow/);
expect(glowBlock).not.toMatch(/\.grade-glow-c\s*\{[^}]*text-shadow/);
expect(glowBlock).not.toMatch(/\.grade-glow-d\s*\{[^}]*text-shadow/);
});
it('grade-*-bg utility classes are token-derived (color-mix), not raw rgba', () => {
expect(css).toMatch(/\.grade-b-bg\s*\{[^}]*color-mix\(in srgb, var\(--grade-b\)/);
expect(css).toMatch(/\.grade-c-bg\s*\{[^}]*color-mix\(in srgb, var\(--grade-c\)/);
});
});