Merge DS3 (design): the color contract — one meaning, enforced by tests
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> # Conflicts: # web/src/components/vyndr/GradeResultCard.tsx
This commit is contained in:
@@ -66,7 +66,7 @@ const row: React.CSSProperties = {
|
||||
borderBottom: '1px solid var(--border)',
|
||||
};
|
||||
|
||||
function Stat({ label, value, tone }: { label: string; value: string; tone?: 'positive' }) {
|
||||
function Stat({ label, value, tone }: { label: string; value: string; tone?: 'positive' | 'negative' }) {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
@@ -83,7 +83,8 @@ function Stat({ label, value, tone }: { label: string; value: string; tone?: 'po
|
||||
style={{
|
||||
fontSize: 14,
|
||||
fontWeight: 700,
|
||||
color: tone === 'positive' ? 'var(--grade-a)' : 'var(--text-primary)',
|
||||
// Color contract #2: edge/CLV colored by SIGN — positive green, negative muted red.
|
||||
color: tone === 'positive' ? 'var(--grade-a)' : tone === 'negative' ? 'var(--miss)' : 'var(--text-primary)',
|
||||
}}
|
||||
>
|
||||
{value}
|
||||
@@ -258,7 +259,7 @@ export default function LiveHeroProp() {
|
||||
{/* Projection + edge — visible, the proof. */}
|
||||
<div style={{ display: 'flex', gap: 8, marginBottom: 12 }}>
|
||||
<Stat label="Projection" value={`${projection} ${statTypeLabel}`} />
|
||||
<Stat label="Edge" value={edgeDisplay} tone={edge > 0 ? 'positive' : undefined} />
|
||||
<Stat label="Edge" value={edgeDisplay} tone={edge > 0 ? 'positive' : edge < 0 ? 'negative' : undefined} />
|
||||
</div>
|
||||
|
||||
{/* Reasoning — BLURRED, the paywall. */}
|
||||
|
||||
@@ -7,6 +7,7 @@ import VBtn from '@/components/vyndr/VBtn';
|
||||
import ArchetypeBlend from '@/components/vyndr/ArchetypeBlend';
|
||||
import GradeBadge from '@/components/vyndr/GradeBadge';
|
||||
import { gradeColor, gradeHex } from '@/lib/vyndrTokens';
|
||||
import { edgeColor, gradeGlows } from '@/lib/colorContract';
|
||||
import { playerHref } from '@/lib/playerHref';
|
||||
|
||||
export interface GradeResultData {
|
||||
@@ -69,9 +70,9 @@ export default function GradeResultCard({
|
||||
}, [replayKey]);
|
||||
|
||||
const sideColor = d.side === 'Over' ? 'var(--g-a)' : 'var(--miss)';
|
||||
// COLOR CONTRACT (Part 1 #3): edge/CLV colored by SIGN. A negative edge must
|
||||
// NEVER render green — negative = var(--miss), positive/zero = signal-green.
|
||||
const edgeColor = d.edge != null && d.edge < 0 ? 'var(--miss)' : 'var(--g-a)';
|
||||
// COLOR CONTRACT (Part 1 #3): edge/CLV colored by SIGN via the centralized
|
||||
// colorContract.edgeColor helper (imported) — negative = var(--miss). DS4
|
||||
// introduced a local const that DS3 superseded with the shared enforcer.
|
||||
const hasKill = !!d.killConditions && d.killConditions.length > 0;
|
||||
const hasBooks = Array.isArray(d.books) && d.books.length > 0;
|
||||
const hasAlt = Array.isArray(d.altLadder) && d.altLadder.length > 0;
|
||||
@@ -140,7 +141,8 @@ export default function GradeResultCard({
|
||||
lineHeight: 0.95,
|
||||
color: hex,
|
||||
letterSpacing: '-0.04em',
|
||||
textShadow: `0 0 28px ${hex}aa, 0 0 60px ${hex}55`,
|
||||
// GLOW = A/A+ ONLY (color contract #4): a glowing C/D devalues the cue.
|
||||
textShadow: gradeGlows(d.grade) ? `0 0 28px ${hex}aa, 0 0 60px ${hex}55` : 'none',
|
||||
fontFamily: 'var(--sans)',
|
||||
}}
|
||||
>
|
||||
@@ -153,7 +155,7 @@ export default function GradeResultCard({
|
||||
{d.edge != null && (
|
||||
<>
|
||||
<span style={{ color: 'rgba(232,255,244,.4)', margin: '0 9px' }}>·</span>
|
||||
<span style={{ color: edgeColor }}>{d.edge >= 0 ? '+' : ''}{d.edge}% edge</span>
|
||||
<span style={{ color: edgeColor(d.edge) }}>{d.edge >= 0 ? '+' : ''}{d.edge}% edge</span>
|
||||
</>
|
||||
)}
|
||||
<span style={{ color: 'rgba(232,255,244,.4)', margin: '0 9px' }}>·</span>
|
||||
@@ -176,7 +178,7 @@ export default function GradeResultCard({
|
||||
{[
|
||||
{ l: 'MODEL', v: d.projection != null ? d.projection : '—', col: d.projection != null ? 'var(--g-a)' : 'var(--text-2)' },
|
||||
{ l: 'LINE', v: d.line, col: 'var(--text-0)' },
|
||||
{ l: 'EDGE', v: d.edge != null ? `${d.edge >= 0 ? '+' : ''}${d.edge}%` : '—', col: d.edge != null ? edgeColor : 'var(--text-2)' },
|
||||
{ l: 'EDGE', v: d.edge != null ? `${d.edge >= 0 ? '+' : ''}${d.edge}%` : '—', col: d.edge != null ? edgeColor(d.edge) : 'var(--text-2)' },
|
||||
].map((x, i) => (
|
||||
<div key={i} style={{ padding: '14px 16px', textAlign: 'center', borderRight: i < 2 ? '1px solid var(--border)' : 'none' }}>
|
||||
<div className="label" style={{ fontSize: 10, marginBottom: 5 }}>{x.l}</div>
|
||||
@@ -219,15 +221,18 @@ export default function GradeResultCard({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 5c. VYNDR INTELLIGENCE (Session 42) */}
|
||||
{/* 5c. VYNDR INTELLIGENCE (Session 42). Color contract #6: the panel is
|
||||
MUTED (neutral border) with a single green label as its identity
|
||||
accent. Form/Rest are CONTEXT data, not "edge" — neutral-bright,
|
||||
never green. */}
|
||||
{d.vyndrIntel && (
|
||||
<div className="intel-surface" style={{ margin: '0 20px 16px', padding: '15px 16px', borderRadius: 12, border: '1px solid rgba(0,212,160,0.24)' }}>
|
||||
<div className="intel-surface" style={{ margin: '0 20px 16px', padding: '15px 16px', borderRadius: 12, border: '1px solid var(--border)' }}>
|
||||
<div className="mono" style={{ fontSize: 10, fontWeight: 600, letterSpacing: '0.1em', color: 'var(--g-a)', marginBottom: 12 }}>VYNDR INTELLIGENCE</div>
|
||||
<div className="mono" style={{ display: 'flex', gap: 10, flexWrap: 'wrap', alignItems: 'center', fontSize: 12 }}>
|
||||
{d.vyndrIntel.form != null && (<><span style={{ color: 'var(--text-2)' }}>Form</span><span style={{ color: 'var(--g-ap)', fontWeight: 700 }}>{d.vyndrIntel.form}</span><span style={{ color: '#2A3531' }}>·</span></>)}
|
||||
{d.vyndrIntel.form != null && (<><span style={{ color: 'var(--text-2)' }}>Form</span><span style={{ color: 'var(--text-0)', fontWeight: 700 }}>{d.vyndrIntel.form}</span><span style={{ color: '#2A3531' }}>·</span></>)}
|
||||
{d.vyndrIntel.usage && (<><span style={{ color: 'var(--text-2)' }}>Usage</span><span style={{ color: 'var(--text-0)', fontWeight: 600 }}>{d.vyndrIntel.usage}</span><span style={{ color: '#2A3531' }}>·</span></>)}
|
||||
{d.vyndrIntel.matchup && (<><span style={{ color: 'var(--text-2)' }}>Matchup</span><GradeBadge grade={d.vyndrIntel.matchup} size="sm" /><span style={{ color: '#2A3531' }}>·</span></>)}
|
||||
{d.vyndrIntel.rest && (<><span style={{ color: 'var(--text-2)' }}>Rest</span><span style={{ color: 'var(--g-a)', fontWeight: 600 }}>{d.vyndrIntel.rest}</span></>)}
|
||||
{d.vyndrIntel.rest && (<><span style={{ color: 'var(--text-2)' }}>Rest</span><span style={{ color: 'var(--text-0)', fontWeight: 600 }}>{d.vyndrIntel.rest}</span></>)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user