Refactor public/style.css to semantic CSS custom properties under :root (light palette by default) and add a @media (prefers-color-scheme: dark) override block that flips background/foreground/accent/danger tokens to a soft near-black palette. The original --black/--white/--gray-*/--red literal tokens and the lone #fff5f5 floating literal are gone — every color now derives from a semantic token, so the dark override is the only place that re-declares them. Add <meta name="color-scheme" content="light dark"> to the shared layout <head> so the browser renders its UA backdrop, native scrollbars, and form-control popups in the user's preferred scheme before the stylesheet parses — this is what avoids a white flash on initial paint in dark mode. No toggle, no JS, no preference persistence — the page follows the OS setting directly via the media query. The 16px font-size rule on text-entry inputs is preserved (still asserted by tests/integration /style.test.ts).
67 lines
2.4 KiB
TypeScript
67 lines
2.4 KiB
TypeScript
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
import { readFileSync } from 'fs';
|
|
import { join } from 'path';
|
|
import { createTestApp, type TestContext } from '../helpers/setup.ts';
|
|
|
|
const STYLE_PATH = join(process.cwd(), 'public', 'style.css');
|
|
|
|
describe('public/style.css (file contents)', () => {
|
|
const css = readFileSync(STYLE_PATH, 'utf8');
|
|
|
|
it('does not @import any external font CSS', () => {
|
|
expect(css).not.toContain('googleapis.com');
|
|
expect(css).not.toContain('@import');
|
|
});
|
|
|
|
it('does not reference the previous IBM Plex Mono webfont', () => {
|
|
expect(css).not.toContain('IBM Plex');
|
|
});
|
|
|
|
it('uses the system sans-serif stack as its default font', () => {
|
|
expect(css).toContain('-apple-system');
|
|
});
|
|
|
|
it('keeps a monospace stack available via --font-mono for code-like UI', () => {
|
|
expect(css).toContain('--font-mono');
|
|
expect(css).toMatch(/\.share-box input\[readonly\][\s\S]*?font-family:\s*var\(--font-mono\)/);
|
|
});
|
|
|
|
it('uses font-size >= 16px on text-entry inputs to prevent iOS focus-zoom', () => {
|
|
expect(css).toMatch(
|
|
/input\[type="text"\][\s\S]*?input\[type="password"\][\s\S]*?\{[\s\S]*?font-size:\s*(1[6-9]|[2-9]\d)px/
|
|
);
|
|
});
|
|
|
|
it('declares a prefers-color-scheme: dark override', () => {
|
|
expect(css).toMatch(/@media\s*\(\s*prefers-color-scheme:\s*dark\s*\)/);
|
|
});
|
|
|
|
it('uses CSS custom properties for the core palette', () => {
|
|
expect(css).toMatch(/--bg:/);
|
|
expect(css).toMatch(/--fg:/);
|
|
expect(css).toMatch(/--accent:/);
|
|
});
|
|
|
|
it('does not contain unreplaced literal hex colors outside the :root and @media blocks', () => {
|
|
const stripped = css
|
|
.replace(/:root\s*\{[\s\S]*?\}/g, '')
|
|
.replace(/@media\s*\([^)]*\)\s*\{[\s\S]*?\}\s*\}/g, '');
|
|
expect(stripped).not.toMatch(/#[0-9a-fA-F]{3,6}\b/);
|
|
});
|
|
});
|
|
|
|
describe('GET /public/style.css', () => {
|
|
let ctx: TestContext;
|
|
|
|
beforeEach(() => { ctx = createTestApp(); });
|
|
afterEach(async () => { await ctx.app.close(); ctx.cleanup(); });
|
|
|
|
it('serves the stylesheet as text/css with no external font import', async () => {
|
|
const res = await ctx.app.inject({ method: 'GET', url: '/public/style.css' });
|
|
expect(res.statusCode).toBe(200);
|
|
expect(res.headers['content-type']).toMatch(/text\/css/);
|
|
expect(res.body).not.toContain('googleapis.com');
|
|
expect(res.body).toContain('-apple-system');
|
|
});
|
|
});
|