diff --git a/app/mobile-rn/src/theme/tokens.test.ts b/app/mobile-rn/src/theme/tokens.test.ts index 51058ce5b..ada2e1483 100644 --- a/app/mobile-rn/src/theme/tokens.test.ts +++ b/app/mobile-rn/src/theme/tokens.test.ts @@ -11,6 +11,27 @@ function resolvePath(source: unknown, path: string): unknown { ), source); } +function hexToRgb(hex: string): [number, number, number] { + const normalized = hex.replace('#', ''); + const r = parseInt(normalized.slice(0, 2), 16) / 255; + const g = parseInt(normalized.slice(2, 4), 16) / 255; + const b = parseInt(normalized.slice(4, 6), 16) / 255; + return [r, g, b]; +} + +function relativeLuminance(hex: string): number { + const [r, g, b] = hexToRgb(hex); + const linearize = (c: number) => (c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4)); + return 0.2126 * linearize(r) + 0.7152 * linearize(g) + 0.0722 * linearize(b); +} + +function contrastRatio(foreground: string, background: string): number { + const l1 = relativeLuminance(foreground); + const l2 = relativeLuminance(background); + const [lighter, darker] = l1 > l2 ? [l1, l2] : [l2, l1]; + return (lighter + 0.05) / (darker + 0.05); +} + describe('AgentHub mobile tokens', () => { it('keeps light mode as the default white-first mobile surface', () => { expect(getAgentHubTheme('light', true)).toBe(agentHubThemes.light); @@ -125,4 +146,11 @@ describe('AgentHub mobile tokens', () => { expect(getAgentHubTheme('system', true).scheme).toBe('dark'); expect(getAgentHubTheme('system', false).scheme).toBe('light'); }); + + it('keeps inkSubtle at or above WCAG AA contrast (4.5:1) on canvas for every theme', () => { + for (const theme of Object.values(agentHubThemes)) { + const ratio = contrastRatio(theme.color.inkSubtle, theme.color.canvas); + expect(ratio, `${theme.scheme} inkSubtle/canvas`).toBeGreaterThanOrEqual(4.5); + } + }); }); diff --git a/app/mobile-rn/src/theme/tokens.ts b/app/mobile-rn/src/theme/tokens.ts index 9ef8d74d8..0fecb818d 100644 --- a/app/mobile-rn/src/theme/tokens.ts +++ b/app/mobile-rn/src/theme/tokens.ts @@ -196,7 +196,7 @@ export const agentHubThemes = { tint: 'rgba(41, 171, 226, 0.14)', ink: '#e3e4e6', inkMuted: '#a0a1aa', - inkSubtle: '#666875', + inkSubtle: '#8a8d98', line: 'rgba(255, 255, 255, 0.075)', focus: 'rgba(41, 171, 226, 0.42)', accent: '#29ABE2', @@ -243,7 +243,7 @@ export const agentHubThemes = { tint: 'rgba(0, 113, 188, 0.08)', ink: '#171720', inkMuted: '#525463', - inkSubtle: '#8a8d98', + inkSubtle: '#6b6e78', line: 'rgba(15, 23, 42, 0.1)', focus: 'rgba(0, 113, 188, 0.32)', accent: '#0071BC', @@ -290,7 +290,7 @@ export const agentHubThemes = { tint: 'rgba(41, 171, 226, 0.16)', ink: '#e3e4e6', inkMuted: '#a0a1aa', - inkSubtle: '#666875', + inkSubtle: '#8a8d98', line: 'rgba(255, 255, 255, 0.065)', focus: 'rgba(41, 171, 226, 0.42)', accent: '#29ABE2',