…ar)` spelling
The Tailwind v4 custom-property migration (925051d) on
`packages/components/src/ui/sidebar.tsx` is an undeclared local edit: it lives
only as prose in `shadcn-components.json` (`localEdits`), with nothing in
`scripts/shadcn-local-patches.mjs` re-applying it, so a `--force` sync drops
it. It is the only one of `sheet`/`sidebar`'s four undeclared edits that no
gate catches — the v3 `[--var]` spelling compiles, renders, and emits invalid
CSS that browsers silently discard.
Adds an offline assertion to the existing `shadcn-local-patches` suite (no new
workflow) that the SHIPPED file — not a vendored fixture — carries no v3
spellings, since the regression guarded is "a forced sync overwrote the shipped
file". Guards the inverse too (the correct v4 `(--var)` spellings must stay
green) and pins the tolerated/refused counts so a sync that deletes the
utilities outright cannot pass as "no v3 spellings found". The docblock records
the other three undeclared edits and the gate that catches each.
Chosen over declaring the migration as a patch family: that would anchor ~20
class strings, each a maintenance surface that can drift from upstream.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019b5UBNMtTzKbVtZZGvFuxe
Fixes #6027
Tests only — no published behaviour changes. Verified green at
0eadb67ab.What and why
The Tailwind v4 custom-property migration (
925051db6) onpackages/components/src/ui/sidebar.tsxis an undeclared local edit: it lives only as prose inshadcn-components.json(localEdits), with nothing inscripts/shadcn-local-patches.mjsre-applying it. A--forcesync — which bypasses thelocalOnlyLinesrefusal by design — drops it.Of
sheet/sidebar's four undeclared edits it is the outlier: the v3 spelling compiles, renders, and emits invalid CSS that browsers silently discard (the sidebar collapses to 0 width over the main content). This adds the cheap offline assertion that catches that regression from the other side.Per the dispatch ruling, the migration is deliberately not declared as a patch family — that would anchor ~20 class strings, each a maintenance surface that can drift out of agreement with upstream, the exact failure class #4996 just finished measuring.
Wired into the existing
scripts/__tests__/shadcn-local-patches.test.tssuite. No new workflow.Asserted against the SHIPPED file, not a fixture
The regression guarded is "a forced sync overwrote the shipped file", so a fixture-based check would pass while the defect landed. The block reads
packages/components/src/ui/sidebar.tsxoff disk.The pattern distinguishes the two spellings
It refuses
[followed immediately by a custom-property name and closed by](w-[--sidebar-width]) — not any--varoccurrence. Measured on the shipped file:w-(--sidebar-width))var()inside an arbitrary value (w-[calc(var(--sidebar-width-icon)_+_1rem)],shadow-[0_0_0_1px_hsl(var(--sidebar-border))])w-[--sidebar-width])Also deliberately tolerated: the arbitrary property form
[--sidebar-width:16rem], which sets a variable and remains valid on v4 — the trailing:breaks the match. Guarding the inverse matters: without it the check is a trap for whoever does the next migration correctly.All three counts are pinned, so a sync that quietly deletes the custom-property utilities rather than respelling them cannot pass as "no v3 spellings found".
Non-vacuity — shown going red
Reintroduced the v3 spelling on line 199, proved it reached disk by grepping the literal (not by trusting the editor's exit code):
The assertion failed naming the spelling and the file:
Three tests went red (
3 failed | 40 passed) — the guard, the inverse guard (expected 6 to be greater than or equal to 7) and the count pin (v4Shorthand: 6vs7). Restored withgit checkout HEAD -- <path>;git diff HEADempty, counts back to 7 v4 / 0 v3. The mutation script carried atrap … EXIT INT TERMrestore so a mid-run kill could not leave the tree mutated.No rebuild leg applies here: the assertion reads the
.tsxsource directly viafs.readFileSync, so there is nodist/resolution path that could serve stale bytes.The record of the other three edits
Half the deliverable, and it lives in the assertion's docblock: each of the other three undeclared edits with the gate that catches its loss —
eslint-disable react-hooks/purity→ Lint (the rule arrives viareactHooks.configs.recommended.rulesateslint.config.js:39and is not downgraded by the repo's overrides), and the unconditionaldata-collapsiblequalifiers → the locator[data-collapsible][data-side][data-state]pinned inpackages/app-shell/src/__tests__/print-stylesheet-4462.test.ts.hideOverlay— credited with a type-check failure — has zero consumers in the tree (theDesignDrawerthatROADMAP.mdcredits no longer exists). It is an optional prop, so a sync dropping it type-checks clean; that gate is vacuous as things stand. Recorded in the docblock and filed separately as #6090 rather than fixed here. Out of scope for this PR.Scope
Untouched, as ruled:
scripts/shadcn-local-patches.mjspatch declarations, the vendored registry fixtures (#4996 / PR #6030 owns those — branched off currentmain,7c96c9420, which includes it), andshadcn-components.json. Noshadcnsync of any kind was run.Verification
npx vitest run scripts/__tests__/from the repo root (never package-scoped) at0eadb67ab— 65 files, 1793 tests passed, includingscripts-type-check.test.tsandcheck-changeset-fixed.test.ts.npx eslint scripts/__tests__/shadcn-local-patches.test.ts— exit 0.Exit codes captured before any pipe, not through
tail.Generated by Claude Code