mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-08-17 18:25:42 +02:00
[eric] ui: every input inherits readable colours from the theme, so no field can be black on black (ENG-281)
This commit is contained in:
@@ -75,6 +75,7 @@ import { useInteractionHeartbeat } from '@/shared/hooks/useInteractionHeartbeat'
|
||||
import { ThemeProvider, useThemeMode, useThemeAccent, useClaudeTokens } from '@/shared/styles/ThemeContext';
|
||||
import { ClaudeTokens } from '@/shared/styles/claudeTokens';
|
||||
import { alertStyleOverrides } from '@/shared/styles/alertOverrides';
|
||||
import { inputStyleOverrides } from '@/shared/styles/inputOverrides';
|
||||
|
||||
function buildMuiTheme(c: ClaudeTokens, mode: 'light' | 'dark') {
|
||||
return createTheme({
|
||||
@@ -185,6 +186,7 @@ function buildMuiTheme(c: ClaudeTokens, mode: 'light' | 'dark') {
|
||||
},
|
||||
},
|
||||
MuiAlert: { styleOverrides: alertStyleOverrides(c) },
|
||||
MuiOutlinedInput: { styleOverrides: inputStyleOverrides(c) },
|
||||
MuiTooltip: {
|
||||
styleOverrides: {
|
||||
tooltip: {
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
// Run: node --test (via frontend/scripts/run-tests.mjs)
|
||||
//
|
||||
// ENG-281: the question flow's "Other..." box rendered black text on a black field, because a bare
|
||||
// <TextField> uses MUI's palette rather than ours. The defence is at the theme, so the assertion
|
||||
// that matters is not "this field is styled" but "text and background can never be the same value".
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { inputStyleOverrides } from './inputOverrides.ts';
|
||||
import type { ClaudeTokens } from './claudeTokens';
|
||||
|
||||
// Two grounds, because the bug only showed on one of them and a light-only fix would have "passed".
|
||||
const dark = {
|
||||
text: { primary: '#ECECEC', tertiary: '#8A8A8A', ghost: '#5A5A5A' },
|
||||
bg: { surface: '#1E1E1E' },
|
||||
border: { medium: '#333', strong: '#555' },
|
||||
accent: { primary: '#D97757' },
|
||||
} as unknown as ClaudeTokens;
|
||||
|
||||
const light = {
|
||||
text: { primary: '#141414', tertiary: '#767676', ghost: '#A0A0A0' },
|
||||
bg: { surface: '#FFFFFF' },
|
||||
border: { medium: '#DDD', strong: '#BBB' },
|
||||
accent: { primary: '#D97757' },
|
||||
} 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`, () => {
|
||||
const root = inputStyleOverrides(tokens).root as Record<string, string>;
|
||||
assert.notEqual(root.color, root.backgroundColor, 'text and field background are identical');
|
||||
});
|
||||
|
||||
test(`${name}: the inner input and textarea inherit the readable colour, not MUI's`, () => {
|
||||
const root = inputStyleOverrides(tokens).root as Record<string, Record<string, string>>;
|
||||
assert.equal(root['& input, & textarea'].color, (tokens as unknown as { text: { primary: string } }).text.primary);
|
||||
});
|
||||
|
||||
test(`${name}: the placeholder is readable and not left on inherited opacity`, () => {
|
||||
const root = inputStyleOverrides(tokens).root as Record<string, Record<string, string | number>>;
|
||||
const ph = root['& input::placeholder, & textarea::placeholder'];
|
||||
assert.equal(ph.opacity, 1, 'MUI dims placeholders via opacity on an already-wrong colour');
|
||||
assert.notEqual(ph.color, (tokens as unknown as { bg: { surface: string } }).bg.surface);
|
||||
});
|
||||
}
|
||||
|
||||
test('the override actually sets a colour at all, so a future empty return fails here', () => {
|
||||
const root = inputStyleOverrides(dark).root as Record<string, string>;
|
||||
assert.ok(root.color && root.backgroundColor, 'override returned nothing to inherit');
|
||||
});
|
||||
@@ -0,0 +1,24 @@
|
||||
import type { ClaudeTokens } from './claudeTokens';
|
||||
|
||||
// A bare <TextField> 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.
|
||||
|
||||
export function inputStyleOverrides(c: ClaudeTokens): Record<string, object> {
|
||||
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
|
||||
// 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 },
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user