Skip to content
Open
Show file tree
Hide file tree
Changes from 5 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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"**/*.css"
],
"main": "./dist/cjs/index.js",
"module": "index.js",
"module": "./dist/esm/index.js",
"types": "index.d.ts",
"exports": {
".": {
Expand Down
63 changes: 63 additions & 0 deletions scripts/verify-css-colors.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/**
* Regenerates the browser-verified table in src/theme/cssColors.spec.ts.
*
* Whether a colour value is valid CSS is a question of fact, not of reading the
* grammar carefully — several rounds of review on this parser turned on cases where
* the grammar reads one way and browsers do another (`rgb(255 50% 0)` looks legal and
* is not; `\72ed` looks like `red` and is not). So the expectations are taken from a
* real engine and checked in, rather than argued about.
*
* Needs a browser, which CI does not have, so this is run by hand:
*
* npx playwright install chromium
* node scripts/verify-css-colors.mjs
*
* It prints the table; paste it over the CHROMIUM block in cssColors.spec.ts. The
* version is printed with it, because a verdict is only ever that engine's: this build
* has no oklch(), so it rejects values a newer one accepts. The assertion is written to
* survive that — the audit may always decline to resolve a value, it may never resolve
* one to different channels than the browser, and it may never resolve one the browser
* rejects.
*/
import { chromium } from '@playwright/test';

const VALUES = [
'#ff0000', '#f00', '#f008', '#ff000080', '#027EB5', '#ABC',
'#12345', '#1234567', '#123456789', '#ggg', '#gggggg', '#12345g',
'red', 'RED', 'Red', 'rebeccapurple', 'notacolor', 'tan', 'white',
'r\\65 d', 're\\64', '\\red', '\\72 ed', '\\72ed', '\\110000', 'r\\65d',
'rgb(255, 0, 0)', 'rgba(255, 0, 0, 0.5)', 'rgb(100%, 0%, 0%)',
'rgba(0, 0, 0, 20%)', 'rgb(0, 50%, 0)', 'rgba(255, 50%, 0, 0.5)',
'rgb(0, 0)', 'rgb(0, 0, 0, 0, 0)', 'rgb(0, 0, 0 / 0.5)', 'rgb(300, 0, 0)',
'rgb(-10, 0, 0)', 'rgb(255 0 0)', 'rgb(255 0 0 / 0.5)', 'rgb(50% 50% 50%)',
'rgb(255 50% 0)', 'rgb(0 0 0 0.5)', 'rgb(0 0 0 // 0.5)', 'rgb(0 0 0 /)',
'rgb(2.55e2 0 0)', 'rgb(.0 0 0)', 'rgb(+255 0 0)', 'rgb(. 0 0)',
'rgb(1..2 0 0)', 'rgb(1e 0 0)', 'rgb(0 0 0 / 50%)', 'rgb(var(--c), 0, 0)',
'hsl(0 100% 50%)', 'hsl(0, 100%, 50%)', 'oklch(0.7 0.1 200)',
'color(display-p3 1 0 0)', 'hwb(0 0% 0%)', 'lab(50% 40 30)',
];

const SENTINEL = 'rgb(1, 2, 3)';
const browser = await chromium.launch();
const page = await browser.newPage();
await page.setContent(
`<style>body { color: ${SENTINEL} }\n` +
VALUES.map((v, i) => `#c${i} { color: ${v} }`).join('\n') + '</style>' +
VALUES.map((_, i) => `<p id="c${i}">x</p>`).join('')
);

const rows = [];
for (let i = 0; i < VALUES.length; i++) {
const computed = await page.$eval('#c' + i, (el) => getComputedStyle(el).color);
rows.push([VALUES[i], computed === SENTINEL ? 'REJECTED' : computed]);
}
const version = browser.version();
await browser.close();

console.log(` // generated by scripts/verify-css-colors.mjs against Chromium ${version}`);

const escape = (text) => text.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
const width = Math.max(...rows.map(([v]) => escape(v).length)) + 3;
console.log(rows
.map(([v, c]) => ` [${(`'${escape(v)}',`).padEnd(width)}'${c}'],`)
.join('\n'));
Loading
Loading