From ef9d6cfd697bc08049fe92e87c9ab8c894b41ad5 Mon Sep 17 00:00:00 2001 From: Gab Date: Wed, 12 Aug 2026 11:08:51 -0300 Subject: [PATCH] docs(rules): correct what the unit suite can see, and name the dead-shorthand trap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit testing.md claimed `setup.ts` imports `@aziontech/theme/globals.css` and that a styled DOM makes axe contrast real. The opposite is true, deliberately: the file loads no CSS and says why, because the env runs no Tailwind. Anyone trusting the rule would write a contrast or computed-style assertion that silently proves nothing — the precise false positive browser mode exists to eliminate. So the CSS boundary is now stated where it matters: never assert a computed style (unstyled DOM returns the same value whether the style is right or absent), axe covers semantics not pixels, and contrast lives in Storybook plus the visual gate. The stack list now describes what `setup.ts` actually does. styling.md gains the malformed-shorthand trap: whitespace inside the parens emits nothing, an inverted bracket/paren form emits a value the browser discards, and a visual baseline generated from the broken render encodes the bug as correct. Both spellings shipped — chip's fill and popover-header's min-height — so the section names the `dead-token-shorthand` check that now blocks them, and points at probing the class rather than the cascade. Only the sections touched are reformatted; styling.md predates the current prettier config and is left otherwise as found. --- .claude/rules/styling.md | 21 ++++++++++++++++++++ .claude/rules/testing.md | 42 +++++++++++++++++++++++++--------------- 2 files changed, 47 insertions(+), 16 deletions(-) diff --git a/.claude/rules/styling.md b/.claude/rules/styling.md index 1f4946fd3..974ead970 100644 --- a/.claude/rules/styling.md +++ b/.claude/rules/styling.md @@ -180,6 +180,25 @@ Three shapes: The guardrail token-checks (`typography-raw-length`, `leading-raw`, `tracking-raw`, `font-family-raw`, `animate-arbitrary`, `motion-hardcoded`) match **both** spellings, so accepting the IntelliSense suggestion cannot walk a raw value through the typography / motion / animation gates. +### A malformed shorthand is dead, and looks alive + +The shorthand has no tolerance: the parens hold **exactly** `--token` or `type:--token`, nothing else. Get it slightly wrong and the style never reaches the element, while the class keeps reading as correct — in a 1000-character root `class` string, forever. + +| Written | What actually happens | +| --- | --- | +| `bg-(--bg-surface )` | The space **terminates the candidate**. Tailwind sees the unterminated `bg-(--bg-surface` and emits **nothing**. | +| `bg-( --bg-surface)` | Same — whitespace anywhere inside the parens. | +| `min-h-[--(size-4)]` | Bracket/paren inverted. This one *does* emit a rule — `min-height: --(size-4)`, an invalid value the **browser discards at parse time**. | + +Both shipped: chip's `filled` kind was fully transparent in both themes from the day it shipped (the first form), and `popover-header`'s min-height was inert from the v4 sweep until 2026-08-12 (the third). Neither is a lint, type, or build error on its own, and **a visual baseline generated from the broken render encodes the bug as correct** — so nothing downstream fails either. + +Two consequences worth internalizing: + +- **A dead class is invisible to the unit suite.** Vitest browser mode runs without Tailwind, so a computed-style assertion returns the same value whether the class works or does not exist (see [`testing.md`](./testing.md) § "What a real browser does NOT give this suite"). Verify a fill by sampling the rendered pixel, or probe the class through the v4 compile API — not through `getComputedStyle` in a test. +- **When a style "does nothing", suspect the class before the cascade.** Probe it (`compile(...).build(['the-class'])`) with a deliberately bogus class in the list as a control; if the bogus one also "emits", the probe is measuring nothing. + +The **`dead-token-shorthand`** token-check blocks both spellings at write time and in the CI ratchet. It scans raw file text, so a comment that *quotes* a malformed class trips it too — describe such a class in prose instead of spelling it out. + ## A zero length carries no unit A zero is the one value that is identical in every unit, so the unit is pure noise — and it makes the same zero read three different ways across the codebase (`0px` here, `0rem` there, `0em` in a token). **Write `0`.** @@ -219,6 +238,7 @@ padding: 0 var(--spacing-md); ## Hard prohibitions - No zero with a length unit — `0`, never `0px` / `0rem` / `0em` (in tokens, arbitrary Tailwind values, inline `style`, or authored CSS). The single exception is inside `calc()`/`min()`/`max()`/`clamp()`, where CSS requires a unit and that unit is **`rem`**. +- No malformed token shorthand — no whitespace inside the parens (`bg-(--token )`, `bg-( --token)`) and never the inverted `[--(token)]`. The style silently never applies; blocked by `dead-token-shorthand`. - No `const sharedClasses = [...]`, `const kindClasses = {...}`, `const sizeClasses = {...}`, `const rootClasses = computed(...)`. The whole "class map" pattern goes away. - No `