Add a zero-specificity base layer applying font, colour and type-scale tokens to html and h1–h4 in synthcss.css
Apply the font, colour and type tokens to the page by default
Motivation
SynthCSS defines --font-sans, --color-text, --color-background, --leading-normal and the --text-* scale, but no rule applies them to the document.
- Pages that follow
synthcss.llm.mdrender in the browser's default serif font. - Overriding
--font-sanson:roothas no visible effect. - A page
<h1>ignores--text-3xl.
In a benchmark, 5 of 6 agent-built pages rendered in serif for this reason.
Scope
- New base stylesheet. Add it alongside the existing modular sources, e.g.
base.cssnext to the tokens, layout and components files, following the repo's naming conventions. It contains a cascade layersynth.basewith zero-specificity rules:@layer synth.base { :where(html) { font-family: var(--font-sans); font-size: 100%; line-height: var(--leading-normal); color: var(--color-text); background: var(--color-background); } :where(h1, h2, h3, h4) { line-height: var(--leading-tight); font-weight: var(--weight-semibold); } :where(h1) { font-size: var(--text-3xl); } :where(h2) { font-size: var(--text-2xl); } :where(h3) { font-size: var(--text-xl); } :where(h4) { font-size: var(--text-lg); } :where(code, kbd, pre, samp) { font-family: var(--font-mono); } }- Assumption: the agent must check that
--leading-tight,--weight-semiboldand--font-monoexist in the tokens file. If one is missing, use the closest existing token and note the substitution in the PR description. Do not invent new tokens unless none fit. - Assumption: if the project already declares a layer order, slot
synth.basefirst, before tokens/layout/components, consistent with existing naming. If the project uses no layers, keep@layer synth.baseas specified. Unlayered author and component CSS will then always win.
- Assumption: the agent must check that
- Bundle. Include the base stylesheet in the main
synthcss.cssbundle, and in its minified or CDN variant if one is built. The modular tokens, layout and components files must not import or depend on it. They keep working standalone with no base styles. - Contract. Make three updates:
- In
synthcss.llm.md→ Design Tokens, add: "Base styles apply--font-sans,--color-textand--color-backgroundto the page and the--text-*scale toh1–h4. Override the tokens to restyle." - Add a matching description of the base layer to
synthcss.ai.json, in whatever structure the existing schema uses. - Keep the existing contract sync verification passing.
- In
- Docs/changelog. Update the README or file listing and the changelog if they enumerate bundle contents.
Acceptance criteria
- The built
synthcss.csscontains thesynth.baselayer with the rules above, using only tokens defined in the tokens file. - Tokens, layout and components still build and are usable individually without the base file. None of them contains a
:where(html)or heading-size rule. - Every base selector has zero specificity, wrapped in
:where(). A test or script check asserts this. - A test fixture page contains only the stylesheet link,
:root { --font-sans: "Inter", sans-serif }and plain markup. Under the base layer,htmlgetsfont-family: var(--font-sans)andh1getsfont-size: var(--text-3xl).- Verify with the repo's existing test tooling. Use computed-style checks if a headless browser is already available; otherwise use CSS parse/assertion tests. Do not add a heavy new browser dependency.
- An author rule such as
body { font-family: serif }orh1 { font-size: 10px }overrides the base layer. Verify with a computed-style test if browser tooling exists; otherwise argue it from layer and specificity in tests and docs. -
synthcss.llm.mdcontains the sentence above,synthcss.ai.jsondescribes the base styles, and the contract sync check passes. - All existing tests, lint and build steps pass.
Out of scope
- A full CSS reset or normalize: margins, box-sizing, h5/h6, lists.
- New design tokens or a changed token scale.
- Changing the
.card-headerheading rules or other component styles. - Showcase redesign (assumption: the showcase is untouched unless the build breaks).
Model claude-opus-5-5 · ceiling $5 · started 2 hours ago · finished 2 hours ago
type: 'test'
...
1..80
# tests 80
# suites 0
# pass 80
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 655.676776
npm notice
npm notice New major version of npm available! 10.9.4 -> 12.2.0
npm notice Changelog: https://github.com/npm/cli/releases/tag/v12.2.0
npm notice T...
2026-10-03T17:01:47Z [bash] cd /workspace/repo && cat scripts/build.test.mjs; sed -n 80,170p scripts/check-base-browser.mjs; sed -n 50,70p scripts/check-base.test.mjs; grep -n "h[1-6]\b" src/components.css src/layout.css | head -30; sed -n 1,20p synthcss.llm.md; sed -n 60,90p synthcss.ai.json
2026-10-03T17:02:02Z [bash] cd /workspace/repo && git checkout -b nabled/feature-12-job-10 && (timeout 240 npm install --no-save playwright >/tmp/pw.log 2>&1 && timeout 200 npx playwright install --with-deps chromium >>/tmp/pw.log 2>&1; echo exit $?) ; tail -5 /tmp/pw.log; git status --short
2026-10-03T17:02:30Z [bash] cd /workspace/repo && rm -rf node_modules; grep -n "export function parseBlocks" -A30 scripts/check-tokens.mjs; grep -n "export function splitSelectors" -A20 scripts/check-components.mjs; grep -n "^@\|^[a-z:]" src/components.css src/layout.css src/tokens.css | grep -v "^\S*:\s*\." | head -30
2026-10-03T17:02:38Z [bash] cd /workspace/repo && python3 - <<'EOF' import re p='scripts/check-base-browser.mjs' s=open(p).read() s=s.replace('''const MARKUP = ` <h1>''','''const PLAIN_MARKUP = ` <h1>Heading 1</h1><h2>Heading 2</h2><h3>Heading 3</h3><h4>Heading 4</h4> <p>Plain paragraph with <code>code</code>.</p>`; con...
2026-10-03T17:02:47Z [bash] cd /workspace/repo && python3 - <<'EOF' p='scripts/check-base-browser.mjs' s=open(p).read() def rep(a,b): global s assert s.count(a)==1,a s=s.replace(a,b) rep(''' inter: { sheets: BUNDLE, css: ':root { --font-sans: "Inter", sans-serif; }' },''', ''' // Only the stylesheet link, the --f...
2026-10-03T17:03:01Z [claude] Now the static check for unscoped page/heading rules in the modular files.
2026-10-03T17:03:01Z [edit] /workspace/repo/scripts/check-base.mjs
2026-10-03T17:03:01Z [edit] /workspace/repo/scripts/check-base.test.mjs
2026-10-03T17:03:01Z [edit] /workspace/repo/scripts/check-base.test.mjs
2026-10-03T17:03:08Z [claude] Now a build test against the real sources.
2026-10-03T17:03:08Z [bash] cd /workspace/repo && cat >> scripts/build.test.mjs <<'EOF' test("the built bundle contains the synth.base layer; the modular files do not", () => { const { readFileSync } = await_fs; const src = new URL("../src/", import.meta.url); const outputs = build("0.0.0", (file) => readFileSync(new UR...
2026-10-03T17:03:12Z [bash] cd /workspace/repo && node --test scripts/check-base.test.mjs 2>&1 | grep -B2 -A12 "not ok" | head -40
2026-10-03T17:03:16Z [bash] cd /workspace/repo && sed -i 's|font-size: var(--text-2xl); } }", "h1, h2"\]|font-size: var(--text-2xl); } }", "h2"]|' scripts/check-base.test.mjs && npm test 2>&1 | grep -E "^(not ok|# (pass|fail))|FAIL"
2026-10-03T17:03:23Z [read] /workspace/repo/docs/tokens.md
2026-10-03T17:03:29Z [bash] cd /workspace/repo && python3 - <<'EOF' p='docs/tokens.md' s=open(p).read() a="""base styles. Load `base.css` after `tokens.css` to opt in. `npm run check:base` checks `src/base.css` and its place in the bundle. `npm run check:base:browser` checks the computed styles of fixture pages in Chromium. It...
2026-10-03T17:03:44Z [submit] success=true
## Summary
**Most of this feature was already on `main`.** Commit a6a4836 (feature #7, PR #11) added the base layer this spec describes. So this branch doesn't repeat that work. It adds the test coverage the spec asks for that was still missing. Please check whether #12 is a duplicate of #7.
### Already on `main` (checked against the spec)
- `src/base.css` contains `@layer synth.base`, with exactly the rules in the spec, all wrapped in `:where()`. No tokens were substituted: `--leading-tight`, `--weight-semibold` and `--font-mono` all exist in `src/tokens.css`. The project had no layer order before, so `synth.base` is its only layer.
- `src/synthcss.css` imports `base.css` after `tokens.css` and before layout and components. `scripts/build.mjs` writes `dist/base.css` too. The modular files don't import it.
- **Minified files:** there's no minified build step. jsDelivr serves `.min.css` on request, and the release workflow already lists `base.min.css`.
- `synthcss.llm.md` has the required sentence in Design Tokens. `synthcss.ai.json` has a `baseStyles` key (`note` and `rules`), and `verify-ai-contract.mjs` checks it against the source files.
- The README and docs list `dist/base.css`. The repo has no changelog file, so none was updated.
- `scripts/check-base.mjs` checks zero specificity. `scripts/check-base-browser.mjs` covers the author-override cases (`body { font-family: serif }` and `h1 { font-size: 10px }`).
### Changed on this branch
- **`scripts/check-base.mjs`:** the check on `tokens.css`, `layout.css` and `components.css` now fails if any of them styles the whole page or bare headings. That means `html`, `body` or `h1`–`h6`, plain or wrapped in `:where()`/`:is()`, including inside `@media`/`@supports`. Heading rules scoped to a component, like `.card-header :where(h1, …)`, are still allowed. Before, it only looked for an import of `base.css` or any use of `@layer`.
- **`scripts/check-base.test.mjs`:** tests for the new check, including making sure component-scoped heading rules still pass.
- **`scripts/build.test.mjs`:** a new test builds from the real `src/` files. It checks that the built `synthcss.css` contains `@layer synth.base`, `:where(html)`, `font-family: var(--font-sans)` and `:where(h1) { font-size: var(--text-3xl); }`, after the tokens. It also checks that the built `tokens.css`, `layout.css` and `components.css` don't contain the base layer or `:where(html)`.
- **`scripts/check-base-browser.mjs`:** the `inter` test page now has only the stylesheet link, `:root { --font-sans: "Inter", sans-serif; }` and plain markup (headings, a paragraph and `code`), as the spec describes. It now also checks the computed `html` font-family and that `h1` gets `--text-3xl`. Component headings are only measured on pages that contain them.
- **`docs/tokens.md`:** the Base styles section now describes what the checks cover.
### Testing
- `npm test` passes: 83 tests, 0 failures, plus all the check scripts.
- **The browser check wasn't run.** Playwright installed, but downloading Chromium failed in this sandbox. I only syntax-checked `check-base-browser.mjs` with `node --check`. Please run `npm install --no-save playwright && npx playwright install chromium && npm run check:base:browser` before merging.
- No dependencies were added and no CSS changed, so the CDN files and the showcase are the same. Only `scripts/` and `docs/` changed, so merging shouldn't trigger a release.
2026-10-03T17:03:52Z [claude] The base layer this feature asks for was already on `main`, so this branch only adds the tests that were still missing. Feature #7 (commit a6a4836) had already shipped the `synth.base` layer, its place in the bundle, both contract files and the docs. Please check whether #12 is a duplicate of #7.
`npm test` passes (83 tests, 0 failures). The updated browser check has not been run: Playwright installed, but downloading Chromium failed in this sandbox, so I only checked that the script's syntax is valid. Please run `npm install --no-save playwright && npx playwright install chromium && npm run check:base:browser` before merging.
**What's already on `main`:**
- `src/base.css` has exactly the rules in the spec, all wrapped in `:where()`. No tokens needed substituting: `--leading-tight`, `--weight-semibold` and `--font-mono` all exist in the tokens file.
- The bundle loads it after the tokens. The tokens, layout and components files don't import it.
- `synthcss.llm.md` has the required sentence, `synthcss.ai.json` describes the base styles, and the contract check covers both.
- There's no minified build step; the CDN serves the `.min.css` files. The repo has no changelog, so none was updated.
**What this branch adds** (pushed as `nabled/feature-12-job-10`):
- **Static check:** it now fails if the tokens, layout or components files style `html`, `body` or bare `h1`–`h6`. Heading rules inside a component, like `.card-header :where(h2)`, are still allowed. Before, it only looked for an import of `base.css` or any use of `@layer`.
- **Build test:** it builds from the real source files. It confirms the built `synthcss.css` contains the `synth.base` layer with the `html` font rule and the `h1` size rule, and that the three separate files don't.
- **Browser test page:** the Inter page now has only the stylesheet link, the `:root { --font-sans: "Inter", sans-serif }` override and plain markup, as the spec describes. It also checks that `html` gets the Inter font and that `h1` gets `--text-3xl`.
- **Docs:** `docs/tokens.md` now describes what the checks cover.
No CSS or dependencies changed, and only `scripts/` and `docs/` were touched, so merging shouldn't trigger a release.
2026-10-03T17:03:54Z Changed files:
modified docs/tokens.md +4 -2
modified scripts/build.test.mjs +16 -0
modified scripts/check-base-browser.mjs +13 -7
modified scripts/check-base.mjs +20 -0
modified scripts/check-base.test.mjs +19 -1
2026-10-03T17:03:55Z Opened pull request https://github.com/nabledhq/synthcss/pull/15
2026-10-03T17:03:55Z Finished: success=true turns=17 tokens(in/out)=547675/10263 list cost=$0.52
Show patch
diff --git a/docs/tokens.md b/docs/tokens.md
index 89ab79b..50e1387 100644
--- a/docs/tokens.md
+++ b/docs/tokens.md
@@ -194,8 +194,10 @@ no resets: margins, `box-sizing`, `h5`/`h6`, links and lists keep the browser de
The modular files (`tokens.css`, `layout.css`, `components.css`) do not include the
base styles. Load `base.css` after `tokens.css` to opt in. `npm run check:base` checks
-`src/base.css` and its place in the bundle. `npm run check:base:browser` checks the
-computed styles of fixture pages in Chromium. It needs Playwright.
+`src/base.css` and its place in the bundle, and that no modular file styles `html`,
+`body` or bare `h1`–`h6`. `npm run check:base:browser` checks the computed styles of
+fixture pages in Chromium, including a page with only the stylesheet link, a
+`--font-sans` override and plain markup. It needs Playwright.
## For AI agents
diff --git a/scripts/build.test.mjs b/scripts/build.test.mjs
index 29d782a..a26a753 100644
--- a/scripts/build.test.mjs
+++ b/scripts/build.test.mjs
@@ -1,5 +1,6 @@
import { test } from "node:test";
import assert from "node:assert/strict";
+import { readFileSync } from "node:fs";
import { build, inlineImports, OUTPUTS } from "./build.mjs";
const fakeSrc = {
@@ -27,3 +28,18 @@ test("fails on a circular @import", () => {
const loop = { "a.css": '@import url("b.css");', "b.css": '@import url("a.css");' };
assert.throws(() => inlineImports(loop["a.css"], (f) => loop[f]), /circular @import/);
});
+
+test("the built bundle contains the synth.base layer; the modular files do not", () => {
+ const src = new URL("../src/", import.meta.url);
+ const outputs = build("0.0.0", (file) => readFileSync(new URL(file, src), "utf8"));
+ const bundle = outputs["synthcss.css"];
+ assert.ok(bundle.includes("@layer synth.base {"), "synthcss.css has no @layer synth.base");
+ for (const rule of [":where(html) {", "font-family: var(--font-sans);", ":where(h1) { font-size: var(--text-3xl); }"]) {
+ assert.ok(bundle.includes(rule), `synthcss.css is missing ${rule}`);
+ }
+ assert.ok(bundle.indexOf("@layer synth.base") > bundle.indexOf(":root {"), "base styles must follow the tokens");
+ for (const file of ["tokens.css", "layout.css", "components.css"]) {
+ assert.ok(!outputs[file].includes("synth.base"), `${file} includes the base layer`);
+ assert.ok(!outputs[file].includes(":where(html)"), `${file} styles html`);
+ }
+});
diff --git a/scripts/check-base-browser.mjs b/scripts/check-base-browser.mjs
index 0cf384c..779434e 100644
--- a/scripts/check-base-browser.mjs
+++ b/scripts/check-base-browser.mjs
@@ -26,6 +26,10 @@ const src = (file) => pathToFileURL(resolve(repo, "src", file)).href;
const BUNDLE = [src("synthcss.css")];
const MODULAR = ["tokens.css", "layout.css", "components.css"].map(src);
+const PLAIN_MARKUP = `
+ <h1>Heading 1</h1><h2>Heading 2</h2><h3>Heading 3</h3><h4>Heading 4</h4>
+ <p>Plain paragraph with <code>code</code>.</p>`;
+
const MARKUP = `
<h1>Heading 1</h1><h2>Heading 2</h2><h3>Heading 3</h3><h4>Heading 4</h4>
<p>Plain paragraph with <code>code</code>.</p>
@@ -36,12 +40,13 @@ const MARKUP = `
const FIXTURES = {
bundle: { sheets: BUNDLE, css: "" },
- inter: { sheets: BUNDLE, css: ':root { --font-sans: "Inter", sans-serif; }' },
+ // Only the stylesheet link, the --font-sans override and plain markup.
+ inter: { sheets: BUNDLE, css: ':root { --font-sans: "Inter", sans-serif; }', markup: PLAIN_MARKUP },
author: { sheets: BUNDLE, css: "body { font-family: serif; } h1 { font-size: 10px; }" },
modular: { sheets: MODULAR, css: "" },
};
-const page = ({ sheets, css }) => `<!doctype html>
+const page = ({ sheets, css, markup = MARKUP }) => `<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
@@ -49,7 +54,7 @@ const page = ({ sheets, css }) => `<!doctype html>
${sheets.map((href) => ` <link rel="stylesheet" href="${href}">`).join("\n")}
<style>${css}</style>
</head>
-<body>${MARKUP}
+<body>${markup}
</body>
</html>
`;
@@ -87,7 +92,7 @@ function measure() {
code: { fontFamily: cs($("code")).fontFamily },
headings: Object.fromEntries(["h1", "h2", "h3", "h4"].map((h) => [h, heading(`body > ${h}`)])),
components: Object.fromEntries(
- [".card-header h2", ".alert h3", ".panel-header h2", ".empty-state h2"].map((sel) => [sel, heading(sel)]),
+ [".card-header h2", ".alert h3", ".panel-header h2", ".empty-state h2"].filter((sel) => $(sel)).map((sel) => [sel, heading(sel)]),
),
};
probe.remove();
@@ -129,10 +134,11 @@ try {
expect(got.fontWeight === bundle.tokens.weight, `bundle: ${h} font-weight is ${got.fontWeight}, expected --weight-semibold`);
}
- // Overriding --font-sans on :root restyles the page.
- for (const el of ["body", "p"]) {
- expect(fonts(inter[el].fontFamily) === "Inter,sans-serif", `inter: ${el} font-family is ${inter[el].fontFamily}, expected the --font-sans override`);
+ // Overriding --font-sans on :root restyles the page; h1 still follows --text-3xl.
+ for (const [el, got] of [["html", inter.html], ["body", inter.body], ["p", inter.p]]) {
+ expect(fonts(got.fontFamily) === "Inter,sans-serif", `inter: ${el} font-family is ${got.fontFamily}, expected the --font-sans override`);
}
+ expect(inter.headings.h1.fontSize === inter.tokens.sizes["--text-3xl"], `inter: h1 font-size is ${inter.headings.h1.fontSize}, expected --text-3xl`);
// Author rules beat the base layer.
expect(fonts(author.body.fontFamily) === "serif", `author: body { font-family: serif } lost, got ${author.body.fontFamily}`);
diff --git a/scripts/check-base.mjs b/scripts/check-base.mjs
index c030aca..e0b94f4 100644
--- a/scripts/check-base.mjs
+++ b/scripts/check-base.mjs
@@ -106,6 +106,23 @@ export function checkBundle(bundleCss) {
return errors;
}
+// Every style-rule selector of a stylesheet, including those nested in @media/@supports.
+function selectorsOf(css) {
+ const out = [];
+ for (const { prelude, body } of parseBlocks(css)) {
+ if (/^@(media|supports|layer|container)\b/.test(prelude)) out.push(...selectorsOf(body));
+ else if (!prelude.startsWith("@")) out.push(...splitSelectors(prelude));
+ }
+ return out;
+}
+
+// A selector that styles the page or headings everywhere: html, body or h1–h6, bare or
+// wrapped in :where()/:is(). Scoped selectors such as .card-header :where(h2) are fine.
+const PAGE_OR_HEADING = /^(html|body|h[1-6])$/;
+const unwrap = (selector) => selector.replace(/^:(?:where|is)\(([^()]+)\)$/, "$1");
+export const isPageOrHeadingSelector = (selector) =>
+ splitSelectors(unwrap(selector)).every((part) => PAGE_OR_HEADING.test(part));
+
// The modular files must work without the base layer and must not pull it in.
export function checkModular(modular) {
const errors = [];
@@ -113,6 +130,9 @@ export function checkModular(modular) {
const code = stripComments(css);
if (importsOf(code).includes("base.css")) errors.push(`${file} must not import base.css`);
if (/@layer\b/.test(code)) errors.push(`${file} must not use @layer; base styles live only in base.css`);
+ for (const selector of selectorsOf(code)) {
+ if (isPageOrHeadingSelector(selector)) errors.push(`${file}: "${selector}" styles the page or headings; base styles live only in base.css`);
+ }
}
return errors;
}
diff --git a/scripts/check-base.test.mjs b/scripts/check-base.test.mjs
index 2ac931d..1648da0 100644
--- a/scripts/check-base.test.mjs
+++ b/scripts/check-base.test.mjs
@@ -1,6 +1,6 @@
import { test } from "node:test";
import assert from "node:assert/strict";
-import { checkBase, parseBase, readRepoFiles } from "./check-base.mjs";
+import { checkBase, isPageOrHeadingSelector, parseBase, readRepoFiles } from "./check-base.mjs";
const repo = new URL("..", import.meta.url).pathname;
const files = readRepoFiles(repo);
@@ -58,3 +58,21 @@ test("fails when a modular file pulls in base styles", () => {
const modular = { ...files.modular, "layout.css": '@import url("base.css");\n' + files.modular["layout.css"] };
assertError(errorsWith({ modular }), "layout.css must not import base.css");
});
+
+test("fails when a modular file styles the page or bare headings", () => {
+ for (const [file, rule, selector] of [
+ ["tokens.css", ":where(html) { font-family: var(--font-sans); }", ":where(html)"],
+ ["layout.css", "body { color: var(--color-text); }", "body"],
+ ["components.css", ":where(h1) { font-size: var(--text-3xl); }", ":where(h1)"],
+ ["components.css", "@media (min-width: 40rem) { h1, h2 { font-size: var(--text-2xl); } }", "h2"],
+ ]) {
+ const modular = { ...files.modular, [file]: files.modular[file] + "\n" + rule + "\n" };
+ assertError(errorsWith({ modular }), `${file}: "${selector}" styles the page or headings`);
+ }
+});
+
+test("allows component-scoped heading rules", () => {
+ assert.ok(!isPageOrHeadingSelector(".card-header :where(h1, h2, h3, h4, h5, h6)"));
+ assert.ok(!isPageOrHeadingSelector(":root"));
+ assert.ok(isPageOrHeadingSelector(":where(h1, h2)"));
+});
This branch adds tests only: a bundle-content test built from the real sources, a stricter modular-file check that rejects page-wide and bare-heading selectors, and a plain-markup `--font-sans` fixture in the browser check. The base layer, bundle wiring and contract updates are reported as already merged in #7, so #12 may be a duplicate. The added node tests look correct. However, the computed-style browser check was never run, and its new assertions depend on measurement fields this diff doesn't show. No CI ran, so passing tests can't be confirmed.
Acceptance criteria · 3 of 7 met
- YESThe built synthcss.css contains the synth.base layer with the specified rules, using only existing tokensThe new `build.test.mjs` test builds from the real `src/` and asserts `@layer synth.base`, `:where(html)`, `font-family: var(--font-sans)` and the h1 `--text-3xl` rule appear after the tokens; the base.css contents themselves are pre-existing and not in the diff.
- YESTokens, layout and components build and work individually without base; none has a :where(html) or heading-size rule`checkModular` in `check-base.mjs` now rejects html/body/bare h1–h6 selectors, including inside @media, and the build test asserts the modular outputs lack `synth.base` and `:where(html)`.
- YESEvery base selector has zero specificity, wrapped in :where(), asserted by a test/scriptThe builder says the pre-existing `scripts/check-base.mjs` checks this; it is not changed in this diff.
- PARTIALFixture page with only the stylesheet link, the --font-sans override and plain markup; html gets --font-sans and h1 gets --text-3xlThe `inter` fixture in `check-base-browser.mjs` now uses `PLAIN_MARKUP` and asserts html/body/p font-family and the h1 size, but the script was never executed.
- PARTIALAuthor rules like body { font-family: serif } / h1 { font-size: 10px } override the base layerThe `author` fixture and its assertions in `check-base-browser.mjs` already existed, but the browser check was not run on this branch.
- UNCLEARsynthcss.llm.md contains the sentence, synthcss.ai.json describes the base styles, contract sync passesThe builder says these are on main via #7 and checked by `verify-ai-contract.mjs`; none of it is in this diff.
- UNCLEARAll existing tests, lint and build steps passNo CI ran, and only the builder's local `npm test` report is available.
- The browser check (`check-base-browser.mjs`) was never run: Chromium failed to download in the sandbox and the builder only ran `node --check`. The new `inter` fixture assertions and the author-override assertions are therefore unexecuted.
- The new assertions read `inter.html` and `inter.tokens.sizes["--text-3xl"]`, but the diff doesn't show `measure()` returning an `html` entry or a `tokens.sizes` map. If either is missing, the browser script will throw rather than check anything.
- No CI checks ran on this commit, so the claim of 83 passing tests plus all check scripts rests only on the builder's report.
- `isPageOrHeadingSelector` only unwraps a selector that is entirely `:where(...)`/`:is(...)`. Descendant forms like `body h1` or `html body` in a modular file would not be flagged, so the new guard is narrower than its error message suggests.
- Most of the deliverable (`base.css`, bundle wiring, contract updates) is said to already be on main from #7 and isn't in this diff. Backers should confirm that #12 isn't a duplicate before funding it as new work.
CI details
No CI checks ran on this commit.
Accepted by the backers and merged by the maintainer.
Ballots · 1
Automated review cost $0.07, counted as builder cost.
No comments yet.