import { test, expect } from 'vitest'; import { READER_THEMES } from './readerThemes.js'; // Contrast of two #rrggbb colours (WCAG ratio), used to make sure selected text can be seen on every theme. const lum = (hex) => { const [r, g, b] = [1, 3, 5].map((i) => parseInt(hex.slice(i, i + 2), 16) / 255).map((c) => (c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4)); return 0.2126 * r + 0.7152 * g + 0.0722 * b; }; const mix = (bg, rgba) => { const [r, g, b, a] = rgba.match(/[\d.]+/g).map(Number); return `#${[r, g, b].map((c, i) => Math.round(c * a + parseInt(bg.slice(1 + i * 2, 3 + i * 2), 16) * (1 - a)).toString(16).padStart(2, '0')).join('')}`; }; const ratio = (a, b) => { const [x, y] = [lum(a), lum(b)].sort((p, q) => q - p); return (x + 0.05) / (y + 0.05); }; test('every theme sets a selection colour that is different from the page and keeps text readable', () => { for (const [id, theme] of Object.entries(READER_THEMES)) { const v = theme.vars; expect(v['--r-sel'], id).toBeTruthy(); const selected = mix(v['--r-bg'], v['--r-sel']); expect(ratio(selected, v['--r-bg']), `${id}: selection stands out from the page`).toBeGreaterThan(1.25); expect(ratio(selected, v['--r-text']), `${id}: text stays readable on the selection`).toBeGreaterThan(4); } });