Skip to content

feat(theme-contract): exportable token/utility contract checker + bin (#188 Phase 2)#196

Merged
drewstone merged 1 commit into
mainfrom
feat/theme-contract-export
Jul 15, 2026
Merged

feat(theme-contract): exportable token/utility contract checker + bin (#188 Phase 2)#196
drewstone merged 1 commit into
mainfrom
feat/theme-contract-export

Conversation

@drewstone

Copy link
Copy Markdown
Contributor

The incident class

Consumer apps ship UI that references theme tokens or Tailwind utilities their own build never emits. CSS resolves the missing custom property to nothing, the surface paints transparent, and nothing errors — it ships invisible. Concrete cases: tax-agent's transparent model dropdown, and the whole bg-surface-container-* family that maps (via the agent-app preset) onto elevation tokens the consumer's build dropped.

tests/theme/tokens-contract.test.ts only guarded agent-app's own components, so every consumer was unprotected against exactly the bug agent-app's tokens created for them.

What this adds

checkThemeContract({ srcDirs, tokensCss?, extraTokensCss?, allowlist? }){ ok, missing } — the referenced-vs-defined token walk, lifted out of the self-test into an exported function every consumer runs against its own source. tokens-contract.test.ts now consumes the export (single source of truth; its behavior is unchanged).

agent-app-theme-check bin--src <dir> (repeatable), --extra-css <file> (repeatable), --tokens, --allow. Prints each missing token with the referencing file and exits 1 on any miss. Products add it as a CI step.

Reads fs, so it lives in a new ./theme-contract subpath; ./theme stays browser-clean (the browser-safe manifest test is still green).

Scope of each check (kept honest)

Check Coverage Catches
var(--…) literal scan Complete inline styles, bg-[var(--…)] arbitrary values, CSS strings — any literal custom-property reference
Tailwind utility scan Intentionally partial the specific known-dangerous families the preset maps to elevation tokens: surface-container / -high / -highest, card, popover. Bare classes carry no var() so check 1 can't see them; fully resolving arbitrary Tailwind config is out of scope. The canvas/sequence aliases are consumed as bg-[var(--…)] and so are already covered by check 1.

The boundary is documented in the module docblock.

Tests

Fixture consumer dir + controlled tokens.css (omits --popover): defined-token reference passes, undefined var() fails with file attribution, undefined dangerous utility (bg-surface-container-high--popover) fails and names the class, allowlist suppresses, extra-css satisfies. Full suite: 150 files / 2421 tests green; heap-bumped build green; CLI verified end-to-end (fail exit 1, pass exit 0, default-tokens resolution from dist).

Part of #188 Phase 2 (gate class 7).

…#188 Phase 2)

Consumer apps ship UI that references theme tokens/utilities their own build
never emits, so the surface resolves to nothing and paints transparent with no
error at runtime (tax-agent's invisible model dropdown; the whole
bg-surface-container-* family). tests/theme/tokens-contract.test.ts only guards
agent-app's OWN components, so every consumer was unprotected.

Lift the referenced-vs-defined token walk out of that test into an exported
checkThemeContract(), and ship a bin every consumer runs in CI over ITS OWN
source. Two checks:
  - var(--…): COMPLETE. Every literal custom-property reference (inline styles,
    bg-[var(--…)] arbitrary Tailwind values, CSS strings) vs the shipped
    tokens.css + any extra app CSS.
  - Tailwind utilities: INTENTIONALLY PARTIAL. Bare classes carry no var() so
    the first check can't see them; fully resolving Tailwind is out of scope.
    Check the specific known-dangerous families the preset maps to elevation
    tokens (surface-container*/card/popover) — exactly the ones that shipped
    invisible. The canvas/sequence aliases are consumed as bg-[var(--…)] and so
    are already covered by check 1.

The checker reads fs, so it lives in a new ./theme-contract subpath; ./theme
stays browser-clean (browser-safe manifest test still green). tokens-contract.
test.ts now consumes the export as the single source of truth for the walk.

@tangletools tangletools left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Auto-approved drewstone PR — 9d421a13

This PR was opened by the trusted drewstone account.
The full PR reviewer audit still runs separately and will publish findings if it detects issues.

tangletools · auto-approval · reason: drewstone_author · 2026-07-15T19:38:26Z

@drewstone
drewstone merged commit 84f326c into main Jul 15, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants