From e277aec52c51092a0dd10cdc330ca33d57d6af46 Mon Sep 17 00:00:00 2001 From: ciregenz Date: Fri, 28 Aug 2026 10:50:09 -0700 Subject: [PATCH] [eric] chat: the answer box is a raised surface, not a well dug into the card (ENG-419) Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01U6zrBsUCNzpMBnov3rTVYV --- .../AgentChat/tool-ui/AskQuestionCard.tsx | 4 +- .../pages/AgentChat/tool-ui/QuestionForm.tsx | 6 ++- .../AgentChat/tool-ui/askBoxTone.test.ts | 54 +++++++++++++++++++ 3 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 frontend/src/app/pages/AgentChat/tool-ui/askBoxTone.test.ts diff --git a/frontend/src/app/pages/AgentChat/tool-ui/AskQuestionCard.tsx b/frontend/src/app/pages/AgentChat/tool-ui/AskQuestionCard.tsx index c156cab7..408f0dcb 100644 --- a/frontend/src/app/pages/AgentChat/tool-ui/AskQuestionCard.tsx +++ b/frontend/src/app/pages/AgentChat/tool-ui/AskQuestionCard.tsx @@ -100,8 +100,10 @@ const AskQuestionCard: React.FC = (props) => { }} /> )} + {/* elevated, not secondary: this is the box you TYPE IN, and on dark, secondary (#1f1e1b) sits + below the surface around it, so the one interactive thing in the widget read as a well. */} {pendingOther.length > 0 && ( - + {pendingOther.map((stepId) => { const idx = Number(stepId.slice(2)); return ( diff --git a/frontend/src/app/pages/AgentChat/tool-ui/QuestionForm.tsx b/frontend/src/app/pages/AgentChat/tool-ui/QuestionForm.tsx index c2f8ecaf..29a467cf 100644 --- a/frontend/src/app/pages/AgentChat/tool-ui/QuestionForm.tsx +++ b/frontend/src/app/pages/AgentChat/tool-ui/QuestionForm.tsx @@ -113,8 +113,10 @@ const QuestionForm: React.FC = ({ request, onApprove, onDeny, return ( parseInt(h.slice(i, i + 2), 16) / 255); + const lin = ch.map((c) => (c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4)); + return 0.2126 * lin[0] + 0.7152 * lin[1] + 0.0722 * lin[2]; +} + +function contrast(a: string, b: string): number { + const [hi, lo] = [luminance(a), luminance(b)].sort((x, y) => y - x); + return (hi + 0.05) / (lo + 0.05); +} + +test('on dark, an input surface is never darker than the surface it sits on', () => { + assert.ok( + luminance(darkTokens.bg.elevated) > luminance(darkTokens.bg.surface), + 'bg.elevated must read as raised on dark, or the answer box is a well again', + ); + assert.ok( + luminance(darkTokens.bg.secondary) < luminance(darkTokens.bg.surface), + 'this pins WHY secondary was wrong here; if it ever rises above surface, revisit the choice', + ); +}); + +test('on light, the step is gentler than the one it replaced', () => { + // Honest scope: this holds on LIGHT (0.053 vs 0.098) and NOT on dark, where elevated is a bigger + // step than secondary was (0.0101 vs 0.0063) in the opposite direction. Direction is the fix on + // dark; softness is the bonus on light. An earlier version of this test claimed both palettes and + // was wrong, which is the whole reason it asserts numbers instead of a token name. + const wasStep = Math.abs(luminance(lightTokens.bg.secondary) - luminance(lightTokens.bg.surface)); + const nowStep = Math.abs(luminance(lightTokens.bg.elevated) - luminance(lightTokens.bg.surface)); + assert.ok(nowStep < wasStep, `light tone step got harsher: ${nowStep} >= ${wasStep}`); +}); + +test('on light it still reads as a distinct panel, not an invisible one', () => { + // The other direction of the same trade: soften it too far and the box stops being a box. + const step = Math.abs(luminance(lightTokens.bg.elevated) - luminance(lightTokens.bg.surface)); + assert.ok(step > 0.02, `the panel vanished into the surface: step ${step}`); +}); + +test('text stays comfortably readable on it, which was never the bug but must not become one', () => { + assert.ok(contrast(darkTokens.bg.elevated, darkTokens.text.primary) >= 7); + assert.ok(contrast(lightTokens.bg.elevated, lightTokens.text.primary) >= 7); +});