Skip to content

test(theme): compile the popup-cap arbitrary values instead of only reading them - #564

Merged
IgorShevchik merged 1 commit into
mainfrom
claude/repo-rules-35um4e
Sep 10, 2026
Merged

test(theme): compile the popup-cap arbitrary values instead of only reading them#564
IgorShevchik merged 1 commit into
mainfrom
claude/repo-rules-35um4e

Conversation

@IgorShevchik

Copy link
Copy Markdown
Collaborator

Linked issue

Closes #457

Type of change

  • Documentation (updates to the documentation or readme)
  • Bug fix (a non-breaking change that fixes an issue)
  • Enhancement (improving an existing functionality)
  • New feature (a non-breaking change that adds functionality)
  • Chore (updates to the build process or auxiliary tools and libraries)
  • Revert (undoing a merged change — retitle this PR revert(Scope): ...)
  • Breaking change (fix or feature that would cause existing functionality to change)

Description

Every assertion this repository makes about theme classes is a string assertion — the theme sources are read, the snapshots record the rendered class, and nothing compiles CSS. So an arbitrary value was only ever proved to be written.

The new spec runs src/runtime/air-design-tokens/tw-style/index.css through the installed tailwindcss engine with candidates read out of src/theme/*.ts, and asserts both halves the issue asks for: a max-height declaration per candidate, and the --max-height-popup-* tokens reaching the compiled output.

The incident is #430 — it moved the popup caps from literals into tokens, and the mechanism had to be verified by hand during review, by exactly this compile. That confirmed the caps worked; nothing kept them working.

Six classes, four distinct values

Extracted rather than restated, so an edited class stays covered and a deleted one trips the floor assertion instead of thinning the list in silence:

theme file token
context-menu, dropdown-menu, editor-suggestion-menu --max-height-popup-menu
input-menu, select-menu, select --max-height-popup-list

What it catches — measured, not claimed

mutation result
delete --max-height-popup-list from sizes.css reds keeps --max-height-popup-list in the compiled output, and only that
insert !! into an arbitrary value stays green — see below

That second row is the interesting one, and it is in the file's own comment rather than left as an implied promise. Probed against the installed engine:

EMITTED  max-h-[min(var(--max-height-popup-list)!!,var(--reka-select-...))]
EMITTED  max-h-[min(var(--max-height-popup-list)]        ← unbalanced
EMITTED  max-h-[not a length]
refused  max-h-[]
refused  maxx-h-[200px]

Tailwind v4 does not validate the meaning of an arbitrary value. So the issue's first failure mode is caught only in its total form — the utility ceasing to exist, or the value going empty — while a value that still parses but no longer means anything is not this file's to find. The second failure mode, the token dropping out of :root, is caught exactly.

Stating that plainly matters more than the guard looking stronger than it is: the whole point of #457 is that a green suite was certifying something it never checked.

Method notes

Two wrong turns, both recorded because both looked convincing:

  • The first matcher built a RegExp from the candidate and reported "no rule emitted" for all six while the rules were sitting in the output. It had to escape for the regex and match text that is itself backslash-escaped, and got the depth wrong. Replaced with a literal indexOf over a selector built the way Tailwind escapes one — no second layer to get wrong.
  • The !! mutation staying green briefly read as a broken guard. It was a broken mutation: Tailwind emits that class quite happily. Same lesson as test(stringified-props): scan the corpus once, outside the timed test #561 — a mutation that fails to go red is a claim about the mutation before it is a claim about the test.

The compile runs once at module scope, not inside an it(), which is the shape that flaked in #561. A the guard itself block pins that the matcher reports absence as well as presence.

Upstream

Nothing to compare against: nuxt/ui compiles no CSS in its test suite, and the --max-height-popup-* tokens do not exist there — they are Air's. Entirely b24ui's own surface, so no divergence and nothing a port could revert.

Checks

  • pnpm lint, pnpm typecheck green
  • Full suite green: 351 files, 7953 passed, 6 skipped
  • Guard verified red by deleting the token; sources restored from copies rather than git checkout --

Checklist

  • I have linked an issue or discussion.
  • I have updated the documentation accordingly.

🤖 Generated with Claude Code

https://claude.ai/code/session_012MsMuj8Fic9tjWVjyEyrxc


Generated by Claude Code

…eading them

Every assertion this repository makes about theme classes is a string
assertion: `test/utils/*.spec.ts` reads the theme sources, the component
snapshots record the rendered `class` attribute, and nothing compiles CSS. An
arbitrary value was therefore only ever proved to be written.

`theme-css-compiles.spec.ts` runs the token entry through the installed
`tailwindcss` engine with the candidate classes read out of `src/theme/*.ts`,
and asserts a `max-height` declaration is emitted for each and that the
`--max-height-popup-*` tokens they reference reach the compiled output.

The incident behind it is #430, which moved the popup caps from literals into
tokens: the mechanism had to be checked by hand during that review, by exactly
this compile. It confirmed the caps worked, and nothing kept them working.

Candidates are extracted rather than restated, so a class that is edited stays
covered and one that is deleted trips the floor assertion instead of thinning
the list in silence. The compile happens once at module scope, not inside an
`it()` — the shape that flaked in #561.

What the guard cannot catch is recorded in the file, measured against the
engine rather than assumed: Tailwind v4 does not validate the meaning of an
arbitrary value. It emits a rule for `max-h-[not a length]` and for an
unbalanced `max-h-[min(var(--x)]`; it refuses an empty value or an unknown
utility. So the first failure mode is caught only in its total form, while the
second — the token dropping out of `:root` — is caught exactly, verified by
deleting `--max-height-popup-list` from `sizes.css`.

A `the guard itself` block pins that `ruleFor` reports absence as well as
presence, so a matcher that always found something could not pass the file
vacuously.

Closes #457

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012MsMuj8Fic9tjWVjyEyrxc
@IgorShevchik
IgorShevchik merged commit e40b041 into main Sep 10, 2026
2 checks passed
@IgorShevchik
IgorShevchik deleted the claude/repo-rules-35um4e branch September 10, 2026 07:50
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.

test: nothing verifies that a theme's arbitrary Tailwind value compiles to a rule

2 participants