Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions webview-ui/src/__tests__/ref-badge-contrast.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Issue #80: a high-contrast light theme puts both `vscode-light` and
// `vscode-high-contrast` on <body>. The high-contrast `.ref-badge` rule comes
// last, so it wins the text color while the light rules above supply the pale
// fills — a hardcoded white made tags/branches/stashes unreadable. The rule
// must use a theme color that is white in HC dark and dark in HC light.
//
// happy-dom does not apply component <style> blocks, so these assertions read
// the component source: they pin the exact declarations that caused the bug.
import { describe, it, expect } from 'vitest';
import { readFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';

const COMPONENTS: Array<{ name: string; file: string }> = [
{ name: 'CommitGraph', file: '../components/graph/CommitGraph.svelte' },
{ name: 'CommitDetails', file: '../components/commit/CommitDetails.svelte' },
];

function escapeRegExp(value: string): string {
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}

function ruleBody(source: string, selector: string): string {
const pattern = new RegExp(`${escapeRegExp(selector).replace(/ /g, '\\s+')}\\s*\\{([^}]*)\\}`);
const match = source.match(pattern);
if (!match) throw new Error(`CSS rule not found: ${selector}`);
return match[1];
}

describe.each(COMPONENTS)('$name high-contrast ref badges (#80)', ({ file }) => {
const source = readFileSync(fileURLToPath(new URL(file, import.meta.url)), 'utf8');
const body = ruleBody(source, ':global(body.vscode-high-contrast) .ref-badge');

it('does not hardcode a white text color', () => {
expect(body).not.toMatch(/color:\s*(#fff\b|#ffffff\b|white\b)/i);
});

it('uses an adaptive theme foreground (white in HC dark, black in HC light)', () => {
expect(body).toMatch(/color:\s*var\(--vscode-strongForeground/);
expect(body).toContain('var(--vscode-foreground, #fff)');
});
});
8 changes: 6 additions & 2 deletions webview-ui/src/components/commit/CommitDetails.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -1502,10 +1502,14 @@
color: #000;
}

/* High contrast overrides */
/* High contrast overrides. HC light themes put both vscode-light and
vscode-high-contrast on <body>, so this rule supplies the text color while
the light rules above supply pale fills — a hardcoded white was unreadable
there (#80). strongForeground is white in HC dark and black in HC light;
the foreground fallback covers VS Code builds without strongForeground. */
:global(body.vscode-high-contrast) .ref-badge {
background: transparent;
color: #fff;
color: var(--vscode-strongForeground, var(--vscode-foreground, #fff));
border: 1px solid var(--badge-color);
}

Expand Down
8 changes: 6 additions & 2 deletions webview-ui/src/components/graph/CommitGraph.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2400,10 +2400,14 @@
color: #000;
}

/* High contrast overrides */
/* High contrast overrides. HC light themes put both vscode-light and
vscode-high-contrast on <body>, so this rule supplies the text color while
the light rules above supply pale fills — a hardcoded white was unreadable
there (#80). strongForeground is white in HC dark and black in HC light;
the foreground fallback covers VS Code builds without strongForeground. */
:global(body.vscode-high-contrast) .ref-badge {
background: transparent;
color: #fff;
color: var(--vscode-strongForeground, var(--vscode-foreground, #fff));
border: 1px solid var(--badge-color);
}

Expand Down