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:
@@ -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\)/);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user