// Vite inlines the file's text at build time. This is deliberately NOT a `readFileSync` // against `import.meta.url`: that works in dev and then throws ENOENT during prerender, // because the bundled chunk sits in `dist/server/.prerender/` and the CSS does not follow // it there. `?raw` puts the bytes in the bundle, where they are needed. import tokensCss from '../styles/tokens.css?raw'; /** * Reads `tokens.css` at build time and exposes its custom properties to JavaScript. * * This exists because a few values have to leave CSS: ``, the OG * card's background, an SVG diagram's stroke. Copying them into a template would be * exactly the drift ยง7 warns about โ€” "one CSS file changes most of the appearance, and * then there is a hardcoded #0e1318 in the footer" โ€” and `checkTokens.mjs` would fail the * build for it, correctly. * * So the token file stays the single source and this reads it, rather than the other way * round. Deliberately a plain regex over `--name: value;` and not a CSS parser: the file * it reads is one we own and keep flat, and a dependency here would be a dependency in the * build of every page. * * Note that this resolves the STOCK values. A bind-mounted `theme.css` overrides tokens in * the browser, at runtime, which is the whole point โ€” anything derived through this module * is therefore build-time and will not follow a mounted theme. Keep that list short. */ function readTokens() { const withoutComments = tokensCss.replace(/\/\*[\s\S]*?\*\//g, ''); const out = {}; for (const match of withoutComments.matchAll(/(--[a-z0-9-]+)\s*:\s*([^;]+);/gi)) { out[match[1]] = match[2].trim(); } return Object.freeze(out); } export const tokens = readTokens(); /** Throws rather than emitting `undefined` into a template. */ export function token(name) { const value = tokens[name]; if (!value) { throw new Error( `Unknown design token "${name}". Every token is defined in src/styles/tokens.css; ` + `add it there rather than inlining a value at the call site.` ); } return value; }