From 8a9d3cac518a7a4dc532bd8af6d98439a10d4ca2 Mon Sep 17 00:00:00 2001 From: yearthmain Date: Wed, 15 Jul 2026 19:32:18 +0800 Subject: [PATCH] fix(kimi-web): improve dark mono button contrast --- .../kimi-web/src/components/chat/Composer.vue | 4 +- apps/kimi-web/src/components/ui/Switch.vue | 4 +- apps/kimi-web/src/style.css | 6 ++- apps/kimi-web/test/theme-contrast.test.ts | 40 +++++++++++++++++++ 4 files changed, 49 insertions(+), 5 deletions(-) create mode 100644 apps/kimi-web/test/theme-contrast.test.ts diff --git a/apps/kimi-web/src/components/chat/Composer.vue b/apps/kimi-web/src/components/chat/Composer.vue index 79dfc1e249..8c25cf8674 100644 --- a/apps/kimi-web/src/components/chat/Composer.vue +++ b/apps/kimi-web/src/components/chat/Composer.vue @@ -1328,7 +1328,7 @@ function selectModel(modelId: string): void { } .att-lightbox-name { max-width: 100%; - color: var(--color-text-on-accent); + color: var(--color-text-inverse); font-family: var(--mono); font-size: calc(var(--ui-font-size) - 2px); overflow: hidden; @@ -1344,7 +1344,7 @@ function selectModel(modelId: string): void { border: 1px solid rgba(255,255,255,0.45); border-radius: 50%; background: rgba(20,23,28,0.82); - color: var(--color-text-on-accent); + color: var(--color-text-inverse); cursor: pointer; } diff --git a/apps/kimi-web/src/components/ui/Switch.vue b/apps/kimi-web/src/components/ui/Switch.vue index 2033c6203f..5ae8149b54 100644 --- a/apps/kimi-web/src/components/ui/Switch.vue +++ b/apps/kimi-web/src/components/ui/Switch.vue @@ -48,9 +48,9 @@ const emit = defineEmits<{ 'update:modelValue': [value: boolean] }>(); width: 16px; height: 16px; border-radius: var(--radius-full); - background: var(--color-text-on-accent); + background: var(--color-text-inverse); box-shadow: var(--shadow-xs); transition: transform var(--duration-base) var(--ease-out); } -.ui-switch.is-on .ui-switch__thumb { transform: translateX(16px); } +.ui-switch.is-on .ui-switch__thumb { background: var(--color-text-on-accent); transform: translateX(16px); } diff --git a/apps/kimi-web/src/style.css b/apps/kimi-web/src/style.css index da20f80734..1b0634469a 100644 --- a/apps/kimi-web/src/style.css +++ b/apps/kimi-web/src/style.css @@ -343,6 +343,7 @@ html[data-accent="mono"] { html[data-color-scheme="dark"][data-accent="mono"] { --accent-primary: #e8eaed; --color-accent: #e8eaed; + --color-text-on-accent: #171717; --color-accent-hover: #c9cdd4; --color-accent-soft: #21262d; --color-accent-bd: #444c56; @@ -358,6 +359,7 @@ html[data-color-scheme="dark"][data-accent="mono"] { html[data-color-scheme="system"][data-accent="mono"] { --accent-primary: #e8eaed; --color-accent: #e8eaed; + --color-text-on-accent: #171717; --color-accent-hover: #c9cdd4; --color-accent-soft: #21262d; --color-accent-bd: #444c56; @@ -398,11 +400,13 @@ html[data-color-scheme="dark"][data-accent="mono"] { --color-surface-sunken: #f3f5f8; /* Foreground text colours. `--color-text` is the default solid body / UI colour (headings and emphasis share it); muted / faint step down for - secondary and tertiary copy; on-accent is text drawn on the accent fill. */ + secondary and tertiary copy; on-accent is text drawn on the accent fill; + inverse stays light on dark image and modal overlays. */ --color-text: rgba(0, 0, 0, 0.9); --color-text-muted: #6b7280; --color-text-faint: #9aa3af; --color-text-on-accent: #ffffff; + --color-text-inverse: #ffffff; --color-line: #e7eaee; --color-line-strong: #d4d9e0; /* Neutral selected fill (sidebar rows, list pickers) — deliberately NOT diff --git a/apps/kimi-web/test/theme-contrast.test.ts b/apps/kimi-web/test/theme-contrast.test.ts new file mode 100644 index 0000000000..d7f33d5af8 --- /dev/null +++ b/apps/kimi-web/test/theme-contrast.test.ts @@ -0,0 +1,40 @@ +import { readFileSync } from 'node:fs'; + +import { describe, expect, it } from 'vitest'; + +const css = readFileSync(new URL('../src/style.css', import.meta.url), 'utf8'); +const composer = readFileSync(new URL('../src/components/chat/Composer.vue', import.meta.url), 'utf8'); +const switchComponent = readFileSync(new URL('../src/components/ui/Switch.vue', import.meta.url), 'utf8'); +const designTokens = css.slice(css.indexOf('DESIGN TOKENS v2')); + +function declarationBlock(source: string, selector: string): string { + const selectorIndex = source.indexOf(`${selector} {`); + expect(selectorIndex).toBeGreaterThanOrEqual(0); + const openBrace = source.indexOf('{', selectorIndex); + const closeBrace = source.indexOf('}', openBrace); + return source.slice(openBrace + 1, closeBrace); +} + +describe('dark mono theme contrast', () => { + it('defines a stable light foreground for dark overlays', () => { + expect(declarationBlock(designTokens, ':root')).toMatch(/--color-text-inverse:\s*#ffffff;/); + }); + + it.each(['.att-lightbox-name', '.att-lightbox-close'])('keeps %s readable on the dark overlay', (selector) => { + expect(declarationBlock(composer, selector)).toMatch(/color:\s*var\(--color-text-inverse\);/); + }); + + it.each([ + ['.ui-switch__thumb', '--color-text-inverse'], + ['.ui-switch.is-on .ui-switch__thumb', '--color-text-on-accent'], + ])('uses %s background token %s', (selector, token) => { + expect(declarationBlock(switchComponent, selector)).toMatch(new RegExp(`background:\\s*var\\(${token}\\);`)); + }); + + it.each([ + 'html[data-color-scheme="dark"][data-accent="mono"]', + 'html[data-color-scheme="system"][data-accent="mono"]', + ])('uses dark text on the near-white accent for %s', (selector) => { + expect(declarationBlock(css, selector)).toMatch(/--color-text-on-accent:\s*#171717;/); + }); +});