Session 33: Design system Phase A+B — tokens, fonts, glitch CSS, shared components (1792 tests)

VYNDR 2.0 design-system conversion, foundation only (pages/mobile/systems are
Sessions 34+). Frontend-only; zero backend changes.

Phase A: §2 token set + token-wired typography + Inter/JetBrains Mono fonts +
§4 glitch keyframes (ported verbatim from the prototype's vyndr.css) + scanline
texture + §10 a11y data-* layer in globals.css. Legacy alias block kept.

Phase B: web/src/lib/vyndrTokens.js (CommonJS helpers) + 9 shared components
under web/src/components/vyndr/ (Wordmark, GradeBadge, SportBadge, TerminalInput,
SectionHead, VBtn, Card, Sparkline, Ticker).

74 new design-system tests. Backend 1718 -> 1792, 141 suites, zero regressions.
Web build clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kev
2026-06-15 22:58:57 -04:00
parent f0c8b4f29b
commit a74b5dd1ed
16 changed files with 1234 additions and 10 deletions
+53
View File
@@ -0,0 +1,53 @@
/* ============================================================
VYNDR 2.0 — design-system token helpers (§5).
Plain CommonJS so the .tsx components import it AND the Jest
suite can require it directly (no TS/Babel transform needed).
Every value resolves to a CSS custom property from globals.css.
============================================================ */
/* Grade → CSS variable (the product's core color language). */
const GRADE_COLORS = {
'A+': 'var(--g-ap)', A: 'var(--g-a)', 'A-': 'var(--g-a)',
'B+': 'var(--g-b)', B: 'var(--g-b)', 'B-': 'var(--g-b)',
C: 'var(--g-c)', D: 'var(--g-d)',
};
/* Grade → raw hex (for SVG fills / contexts where a var won't resolve). */
const GRADE_HEX = {
'A+': '#00ffb8', A: '#00d4a0', 'A-': '#00d4a0',
'B+': '#4a9eff', B: '#4a9eff', 'B-': '#4a9eff',
C: '#ffb347', D: '#ff5252',
};
/* Sport config map. */
const SPORT = {
nba: { label: 'NBA', color: 'var(--s-nba)', hex: '#e94b3c' },
mlb: { label: 'MLB', color: 'var(--s-mlb)', hex: '#1e90ff' },
wnba: { label: 'WNBA', color: 'var(--s-wnba)', hex: '#f7944a' },
soccer: { label: 'SOC', color: 'var(--s-soccer)', hex: '#3ddc84' },
};
/* GradeBadge size variants — hero stays 80120px (§5: grade letter is
always the largest, first-read element on a card). Numbers pass through. */
const GRADE_BADGE_SIZES = { sm: 16, md: 30, lg: 48, hero: 100 };
function gradeColor(g) {
return GRADE_COLORS[g] || 'var(--text-0)';
}
function gradeHex(g) {
return GRADE_HEX[g] || '#e8e8f0';
}
function gradeBadgeSize(size) {
if (typeof size === 'number') return size;
return GRADE_BADGE_SIZES[size] || GRADE_BADGE_SIZES.md;
}
module.exports = {
GRADE_COLORS,
GRADE_HEX,
SPORT,
GRADE_BADGE_SIZES,
gradeColor,
gradeHex,
gradeBadgeSize,
};