Skip to content

test(skill): guard colour values and CSS custom properties in examples - #533

Merged
IgorShevchik merged 2 commits into
mainfrom
claude/repo-rules-35um4e
Sep 4, 2026
Merged

test(skill): guard colour values and CSS custom properties in examples#533
IgorShevchik merged 2 commits into
mainfrom
claude/repo-rules-35um4e

Conversation

@IgorShevchik

Copy link
Copy Markdown
Collaborator

Linked issue

Resolves #345

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

Two new checks in test/utils/skill-manifest.spec.ts, and the two live defects they found. Starts where #345 says to start — colours, then CSS custom properties as the cheapest sibling. Prop names are the harder half and are left for a separate pass, as the issue itself notes.

Both guards found something on their first run, which is the argument for having them at all.


The two defects

B24Badge has been rendering uncoloured for a month

data-tables.md shipped color="neutral" on B24Badge. That is in #345's own table. It was fixed by hand in #343 to air-secondary-no-accent — a key Button declares and Badge does not — and sat wrong ever since.

Measured by rendering all three:

rendered style-* class
no colour (default) style-filled
air-secondary (valid) style-tinted
air-secondary-no-accent (shipped) none

The Inactive badge has no colour class at all. Nothing throws, nothing warns. Now air-tertiary, which Badge does declare (style-outline-no-accent — the muted outline that suits an inactive status next to a filled green one).

The manual fix for the defect in the issue was itself wrong. That is the case for doing this mechanically.

A phantom CSS token

forms.md used text-(--ui-color-typography-secondary) to label a role="group". It is declared nowhere, and there is no --ui-color-typography-* family in src/ at all. Replaced with text-legend — the semantic utility the skill's own design-system guideline describes as being for group titles, and which src/theme/timeline.ts and src/theme/dropdown-menu.ts already use.


The checks

Both follow the shape the file already uses: read the truth from the tree, anchor on a positive and a negative sentinel, then assert the scan finds nothing.

Colours come from each src/theme/*.ts color variant map, found by brace matching rather than a line regex — the map nests, and a flat scan picks up slot names from inside it. Static color= / active-color= attributes are checked, and so are the ternary branches of a bound :color=, which is where the Badge defect actually lived.

CSS custom properties are checked in every shape the skill writes: var(--x), var(--x, fallback), text-(--x), and the typed arbitrary properties text-(length:--x) / font-(family-name:--x) — plus declarations, since writing --b24ui-* in your own CSS is a claim the library reads it.

Prose placeholders are deliberately not flagged: --ui-color-base-N, …-bg-gradient-{1,2,3} and …-bg-content-* are documentation, not references, and requiring a closing ) or , separates them cleanly. Without that the check reports five false positives to one real finding.

What /review caught

Six holes in the first draft. Four made the checks weaker than their own comments claimed, one was a false positive, one was a wrong statement:

Finding Consequence
colourKeys hardcoded a 4-space indent input-number.ts nests its map at 6 — map found, zero keys read, component silently unchecked
tag matched with [^>]* <B24Badge v-if="a > b" color="bogus" /> passed green
:color flagged every quoted string row.status === 'active' would fail as an unknown colour
active-color dismissed as an "other prop" it takes the same colour map
token pattern matched only bare (--x) missed 27 references, including the untouched half of the very line the phantom token was found on
the comment claimed it guards --b24ui-header-heights it did not — that defect is a declaration, which a usage-only pattern cannot see

All fixed, and each fix probed by injecting the case:

InputNumber colour (factory theme)         RED ✓
active-color attribute                     RED ✓
> inside an attribute value                RED ✓
typed token text-(length:--phantom)        RED ✓
var() with a fallback                      RED ✓
declaration form --b24ui-header-heights    RED ✓
row.status === 'active'                    green ✓  (no false positive)

The two original defects were also each restored and confirmed to fail exactly their matching check, naming the file and the value — and re-verified after eslint --fix rewrote one of the regexes.

Checks

  • pnpm lint, pnpm typecheck green
  • Full suite: 340 files, 7818 passed, 6 skipped (+4 — the two new checks × two projects)
  • No runtime file touched

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

Closes #345, starting where the issue says to start — colours, then CSS
custom properties as the cheapest sibling. Prop names are the harder half and
are left for a separate pass, as the issue itself notes.

**Both guards found a live defect, which is the argument for having them.**

`data-tables.md` shipped `color="neutral"` on `B24Badge`. That was in #345's
own table, was fixed by hand in #343 to `air-secondary-no-accent` — a key
`Button` declares and `Badge` does not — and sat wrong for another month.
Measured by rendering it: a valid colour carries `style-tinted`, the default
carries `style-filled`, and this one carries no `style-*` class at all. The
Inactive badge has been rendering uncoloured the whole time. Now
`air-tertiary`, which `Badge` does declare.

`forms.md` reached for `--ui-color-typography-secondary` to label a
`role="group"`. It is declared nowhere, and there is no
`--ui-color-typography-*` family in `src/` at all. Replaced with
`text-legend`, the semantic utility the skill's own design-system guideline
describes as being for group titles — and which `src/theme/timeline.ts` and
`src/theme/dropdown-menu.ts` already use.

That is the pattern #345 describes: a name inside a fenced block, contradicted
by `src/`, that renders without complaint. The colour one is worse than the
issue knew, because the manual fix for it was also wrong.

**The checks.** Both follow the shape of the existing component and icon
checks — read the truth from the tree, anchor on a positive and a negative
sentinel, then assert the scan finds nothing.

- Colours are read from each `src/theme/*.ts` `color` variant map by brace
  matching, not a line regex, because the map nests and a flat scan picks up
  slot names from inside it. Static `color="x"` attributes are checked, and so
  are quoted literals inside a bound `:color="…"` — which is where the Badge
  defect actually lived, in a ternary rather than an attribute.
- CSS custom properties are matched only where they are *used*: inside
  `var(--x)` or Tailwind's `text-(--x)`. The closing paren is what separates a
  real reference from the placeholders the prose writes —
  `--ui-color-base-N`, `…-bg-gradient-{1,2,3}`, `…-bg-content-*` — which
  otherwise all read as phantom tokens. Without it the check reports five
  false positives and one real one.

Each was verified to go red on its own defect: restoring
`air-secondary-no-accent` and `--ui-color-typography-secondary` fails exactly
the matching check, naming the file and the value. Re-verified after `eslint
--fix` rewrote one of the regexes.

Refs #345

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012MsMuj8Fic9tjWVjyEyrxc
`/review` found six holes in the first commit. Four were coverage gaps that
made the checks weaker than their own comments said; one was a false positive
that would have failed a legitimate example; one was a wrong statement in a
comment. Each fix was probed by injecting the case and confirming the check
goes red.

**Colours**

- `colourKeys` hardcoded a four-space indent. A factory theme nests the map
  one level deeper — `input-number.ts` declares it at six — so the map was
  found and *zero* keys were read out of it, silently dropping the component.
  The indent is now derived from the match, and key depth with it.
- The tag pattern was `[^>]*`, which ends the tag at the first `>` inside an
  attribute value. `<B24Badge v-if="a > b" color="bogus" />` passed green.
  Quoted values are now skipped over rather than scanned for tag ends.
- The `:color` scan flagged every lowercase quoted string in the expression,
  so a comparison operand — `row.status === 'active'` — failed as an unknown
  colour. Only ternary branches are read now. The shipped example survived
  only because its operand happens to be capitalised.
- `active-color` was dismissed in a comment as an "other prop". It takes the
  same map (`Button['variants']['color']`, and `ButtonProps['color']` on
  `Pagination` and `EditorToolbar`). Both attributes are checked now.

**CSS custom properties**

- The pattern matched only the bare `(--x)` form, missing `var(--x, fallback)`
  and Tailwind's typed arbitrary properties `text-(length:--x)` /
  `font-(family-name:--x)`. That is 27 of the references in this package —
  including the untouched half of the very line the phantom token was found
  on, `text-(length:--ui-font-size-sm)`.
- Worse, the comment cited `--b24ui-header-heights` as a defect this check
  guards, and it does not: that one is written as a *declaration*, which the
  usage-only pattern cannot see. Declarations of `--b24ui-*` / `--ui-*` are
  now checked too — a reader defining `--my-thing` is left alone, but writing
  the library's namespace is a claim the library reads it. `--b24ui-header-height`
  and its plural are now the check's sentinels.

Probes, all confirmed: a bogus `B24InputNumber` colour, a bogus
`active-color`, a `>` inside an attribute, a phantom typed token, a phantom
token inside `var(… , fallback)` and a phantom declaration each fail the
matching check — and `row.status === 'active'` stays green.

Refs #345

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

skill: guard prop and colour names in examples, not just component and icon names

2 participants