From c58d9e16575b5d9080ad07477a2bea0613273229 Mon Sep 17 00:00:00 2001 From: ciregenz Date: Wed, 12 Aug 2026 19:09:18 -0700 Subject: [PATCH] [eric] ui: the input readability fix touches colour only, not the geometry of 74 controls (ENG-281) --- .../src/shared/styles/inputOverrides.test.ts | 19 ++++++++++++---- frontend/src/shared/styles/inputOverrides.ts | 22 +++++++++---------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/frontend/src/shared/styles/inputOverrides.test.ts b/frontend/src/shared/styles/inputOverrides.test.ts index 36c6dd18..1f311617 100644 --- a/frontend/src/shared/styles/inputOverrides.test.ts +++ b/frontend/src/shared/styles/inputOverrides.test.ts @@ -24,9 +24,19 @@ const light = { } as unknown as ClaudeTokens; for (const [name, tokens] of [['dark', dark], ['light', light]] as const) { - test(`${name}: typed text is never the same colour as the field it sits on`, () => { + test(`${name}: typed text is never the same colour as the surface it sits on`, () => { const root = inputStyleOverrides(tokens).root as Record; - assert.notEqual(root.color, root.backgroundColor, 'text and field background are identical'); + const surface = (tokens as unknown as { bg: { surface: string } }).bg.surface; + assert.notEqual(root.color, surface, 'text colour equals the surface behind the field'); + }); + + test(`${name}: the override changes ONLY colour, never geometry or fill`, () => { + // Regression guard on my own scope creep: an earlier version restyled 74 controls to fix a + // contrast bug. Anything here that is not a colour is out of bounds. + const root = inputStyleOverrides(tokens).root as Record; + for (const banned of ['borderRadius', 'backgroundColor', 'border', 'padding', 'height', 'fontSize']) { + assert.equal(root[banned], undefined, `override sets ${banned}, which is not a readability fix`); + } }); test(`${name}: the inner input and textarea inherit the readable colour, not MUI's`, () => { @@ -43,6 +53,7 @@ for (const [name, tokens] of [['dark', dark], ['light', light]] as const) { } test('the override actually sets a colour at all, so a future empty return fails here', () => { - const root = inputStyleOverrides(dark).root as Record; - assert.ok(root.color && root.backgroundColor, 'override returned nothing to inherit'); + const root = inputStyleOverrides(dark).root as Record; + assert.ok(root.color, 'override returned nothing to inherit'); + assert.ok(root['& input, & textarea'], 'inner input rule missing'); }); diff --git a/frontend/src/shared/styles/inputOverrides.ts b/frontend/src/shared/styles/inputOverrides.ts index f59f7568..fb4b11dd 100644 --- a/frontend/src/shared/styles/inputOverrides.ts +++ b/frontend/src/shared/styles/inputOverrides.ts @@ -1,24 +1,24 @@ import type { ClaudeTokens } from './claudeTokens'; -// A bare renders with MUI's own palette, not ours. On a dark surface that is black text -// on a black field: you cannot see what you are typing (ENG-281, hit on the question flow's -// "Other..." box). Fixing the one call site would leave every future bare field able to do it again, -// so the colours are pinned at the theme and every input in the app inherits them. +// A bare renders its text with MUI's palette, not ours. On a dark surface that is black +// text on a black field: you cannot see what you are typing (ENG-281, hit on the question flow's +// "Other..." box). Pinned at the theme so every input inherits it and no future bare field can +// reintroduce the bug. +// +// Deliberately COLOUR ONLY. An earlier version of this also set borderRadius, backgroundColor and +// the fieldset border colours, which changed the appearance of all 38 TextFields and 36 Selects in +// the app (Select renders through MuiOutlinedInput too) to fix a problem that was only ever about +// text contrast. Geometry and fill are left exactly as they were: the blast radius of a readability +// fix should be readability. export function inputStyleOverrides(c: ClaudeTokens): Record { return { root: { color: c.text.primary, - backgroundColor: c.bg.surface, - borderRadius: 10, '& input, & textarea': { color: c.text.primary }, - // Placeholders need their own rule: MUI paints them via opacity on the inherited colour, which + // Placeholders need their own rule: MUI dims them with opacity on the inherited colour, which // lands invisible when the inherited colour was already wrong. '& input::placeholder, & textarea::placeholder': { color: c.text.tertiary, opacity: 1 }, - '& fieldset': { borderColor: c.border.medium }, - '&:hover fieldset': { borderColor: c.border.strong }, - '&.Mui-focused fieldset': { borderColor: c.accent.primary }, - '&.Mui-disabled': { color: c.text.ghost }, }, }; }