fix: improve terminal contrast in light themes (Tango Light + xterm minimumContrastRatio) (#5986)

* fix: darken Builtin Tango Light ANSI colors for readability

Nine ANSI colors had contrast ratios below 3:1 against the white
background, making CLI output unreadable in light mode. Darkened
to readable Tango shades while keeping the warm palette identity.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: enable xterm minimumContrastRatio and add light theme tests

xterm.js minimumContrastRatio (4.5) auto-adjusts low-contrast ANSI
foreground colors at render time — a safety net for all themes,
including custom imports. Added test coverage for this option and
for the Tango Light ANSI color contrast assertions.

Co-Authored-By: Claude <noreply@anthropic.com>

* chore: trigger CI re-run

---------

Co-authored-by: zhangqinzhong <vhudsongit@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Robert V Hudson
2026-06-22 14:53:34 -07:00
committed by GitHub
co-authored by Claude zhangqinzhong
parent 1b62f6511e
commit 41add74a08
4 changed files with 37 additions and 8 deletions
@@ -83,6 +83,10 @@ describe('buildDefaultTerminalOptions', () => {
expect(buildDefaultTerminalOptions().scrollbar?.width).toBe(7)
})
it('enables xterm contrast correction for low-contrast CLI colors', () => {
expect(buildDefaultTerminalOptions().minimumContrastRatio).toBe(4.5)
})
it('only uses inactive outline for block cursors', () => {
expect(resolveTerminalCursorInactiveStyle('block')).toBe('outline')
expect(resolveTerminalCursorInactiveStyle('bar')).toBe('bar')
@@ -27,6 +27,9 @@ export function buildDefaultTerminalOptions(): ITerminalOptions {
fontWeightBold: '500',
scrollback: 10000,
allowTransparency: false,
// Why: agent CLIs sometimes render body text with ANSI white/bright-white
// on light themes; xterm can keep those cells readable across renderers.
minimumContrastRatio: 4.5,
// Why: on macOS, non-US layouts rely on Option to compose characters like @ and €.
macOptionIsMeta: false,
macOptionClickForcesSelection: true,
@@ -261,6 +261,26 @@ describe('default dark terminal theme selection contrast', () => {
})
})
describe('default light terminal theme ANSI contrast', () => {
it('keeps CLI body/header ANSI colors readable on the terminal background', () => {
const theme = getBuiltinTheme(DEFAULT_TERMINAL_THEME_LIGHT)
expect(theme, `${DEFAULT_TERMINAL_THEME_LIGHT} should exist`).not.toBeNull()
if (!theme?.background) {
throw new Error(`${DEFAULT_TERMINAL_THEME_LIGHT} is missing a background color`)
}
for (const key of ['cyan', 'white', 'brightCyan', 'brightWhite'] as const) {
const color = theme[key]
expect(color, `${DEFAULT_TERMINAL_THEME_LIGHT}.${key} should be defined`).toBeDefined()
if (!color) {
throw new Error(`${DEFAULT_TERMINAL_THEME_LIGHT}.${key} is missing`)
}
expect(contrastRatio(color, theme.background)).toBeGreaterThanOrEqual(4.5)
}
})
})
describe('isTerminalBackgroundLight', () => {
it('classifies common terminal background color formats by luminance', () => {
const split = vi.spyOn(String.prototype, 'split')
@@ -38,18 +38,20 @@ export const DEFAULT_TERMINAL_THEMES: TerminalThemeMap = {
black: '#2e3436',
red: '#cc0000',
green: '#4e9a06',
yellow: '#c4a000',
// Why: Claude-style previews use ANSI accent colors for readable text,
// so the light theme cannot keep Tango's near-white legacy values.
yellow: '#8e7700',
blue: '#3465a4',
magenta: '#75507b',
cyan: '#06989a',
white: '#d3d7cf',
cyan: '#05727e',
white: '#6a6a6a',
brightBlack: '#555753',
brightRed: '#ef2929',
brightGreen: '#8ae234',
brightYellow: '#fce94f',
brightBlue: '#729fcf',
brightGreen: '#1b7a1b',
brightYellow: '#6d5a00',
brightBlue: '#204a87',
brightMagenta: '#ad7fa8',
brightCyan: '#34e2e2',
brightWhite: '#eeeeec'
brightCyan: '#034b50',
brightWhite: '#3d3d3d'
}
}