Skip to content

Commit 167ea54

Browse files
committed
feat(skill): tailwind-agents-validator
1 parent 38d405c commit 167ea54

4 files changed

Lines changed: 1239 additions & 0 deletions

File tree

Lines changed: 343 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,343 @@
1+
---
2+
name: tailwind-agents-validator
3+
description: >
4+
Use when reviewing a Tailwind CSS codebase for cross-rule violations,
5+
performing a pre-PR audit of changes that touch styling, validating
6+
a v3-to-v4 migration result, or running a periodic codebase health
7+
check across utility classes, plugin authoring, and configuration
8+
files. The agent acts as a Tailwind-specific code reviewer that
9+
enforces every rule documented in the sibling skills of this
10+
package, producing an actionable report that cites the authoritative
11+
sibling skill for each finding. Prevents the four highest-impact
12+
Tailwind code-review misses: dynamic template-literal class strings
13+
that compile cleanly but emit no CSS in production, scoped @apply
14+
blocks in Vue/Svelte/CSS-modules without @reference under v4,
15+
v3-only config keys (corePlugins, safelist, separator) silently
16+
ignored after a v4 upgrade, and bare border/ring/placeholder usage
17+
inheriting the wrong default colour post-migration. Covers the full
18+
rule catalogue (no dynamic class strings, no scoped @apply without
19+
@reference, no v3 keys in v4 config, no undefined custom classes
20+
outside @layer, variant-stack order consistency, explicit border
21+
and ring colours, no em-dash in user-facing strings, deterministic
22+
prop-to-class mapping, twMerge ordering correctness, plugin theme()
23+
default-shipping), the report format (severity, file:line, rule
24+
citation, sibling-skill link, fix suggestion), the recursive scan
25+
pattern (TSX, JSX, Vue, Svelte, CSS, MDX), the v3-or-v4 detection
26+
heuristic, and integration into CI as a non-blocking advisory.
27+
Keywords: tailwind code review, tailwind validator, tailwind audit,
28+
tailwind linter, pre-PR check tailwind, post-migration audit, v3 v4
29+
consistency check, cross-rule check tailwind, dynamic class
30+
detection, scoped @apply check, @reference missing, corePlugins
31+
leftover, safelist leftover, separator leftover, bare border bare
32+
ring, prop-to-class consistency, variant order check, twMerge
33+
audit, my codebase upgrade looks clean but, find all dynamic
34+
classes, find all scoped @apply, find tailwind antipatterns,
35+
tailwind health check, before I ship audit.
36+
license: MIT
37+
compatibility: "Designed for Claude Code. Requires Tailwind CSS v3.4 or v4.0+."
38+
metadata:
39+
author: OpenAEC-Foundation
40+
version: "1.0"
41+
---
42+
43+
# Tailwind Validator : Cross-Skill Consistency Agent
44+
45+
Acts as a Tailwind-specific code reviewer. Reads the codebase, runs
46+
every rule documented in the sibling skills, produces an actionable
47+
report that cites the authoritative source per finding.
48+
49+
The validator does NOT modify code. It reports. The developer (or a
50+
follow-up agent) applies fixes guided by the cited sibling skill.
51+
52+
## How To Invoke
53+
54+
The user asks "validate my Tailwind code" or "audit this codebase for
55+
Tailwind issues" or "run a pre-PR check on the styling changes". The
56+
agent :
57+
58+
1. Detects v3 vs v4 (heuristic in Method 1, `references/methods.md`).
59+
2. Runs each rule in the catalogue (Section : Rule Catalogue).
60+
3. Emits the report in the standard format (Section : Report Format).
61+
62+
## Detect : v3 or v4
63+
64+
Decision sources, in priority order :
65+
66+
1. `package.json` `dependencies.tailwindcss` semver. `^3.x` is v3.
67+
`^4.x` is v4.
68+
2. CSS entry file contains `@import "tailwindcss"` (v4) OR three
69+
`@tailwind` directives (v3).
70+
3. PostCSS config plugin is `@tailwindcss/postcss` (v4) OR
71+
`tailwindcss` (v3).
72+
4. Vite config imports `@tailwindcss/vite` (v4) OR uses the PostCSS
73+
plugin (v3).
74+
75+
If signals conflict, report a Severity-Error mismatch FIRST. Mixed
76+
configurations produce silent failures.
77+
78+
Mixed-state cross-link : `tailwind-impl-migration-v3-v4`.
79+
80+
## Rule Catalogue
81+
82+
Each rule names a check, the file types it scans, the sibling skill
83+
that documents the rule, and the severity.
84+
85+
### Rule R-01 : No Dynamic Template-Literal Class Strings
86+
87+
**Scans** : `.tsx`, `.jsx`, `.vue`, `.svelte`, `.astro`, `.mdx`
88+
89+
**Detect** : grep for `` className=`...${...}...` `` and `class="...{{ ...?... }}...{{ ... }}"` patterns that build a class fragment from a variable.
90+
91+
**Severity** : Error (production bug, classes silently missing)
92+
93+
**Sibling skill** : `tailwind-errors-dynamic-classes`
94+
95+
**Fix** : static map per Method 1 of the sibling skill.
96+
97+
### Rule R-02 : Scoped @apply Without @reference (v4 only)
98+
99+
**Scans** : `.vue`, `.svelte`, `*.module.css`
100+
101+
**Detect** : file contains `@apply` inside a scoped `<style>` block or
102+
a CSS module, and the same block does NOT contain `@reference`.
103+
104+
**Severity** : Error (build fails with "Cannot apply unknown utility
105+
class")
106+
107+
**Sibling skill** : `tailwind-impl-apply-directive`
108+
109+
**Fix** : prepend `@reference "../path/to/app.css";` inside the style
110+
block.
111+
112+
### Rule R-03 : Forbidden v3 Config Keys in v4 Project
113+
114+
**Scans** : `tailwind.config.js`, `tailwind.config.ts`
115+
116+
**Detect** : project is v4 AND config file contains any of
117+
`corePlugins`, `safelist`, `separator`.
118+
119+
**Severity** : Warning (silently ignored, original intent lost)
120+
121+
**Sibling skill** : `tailwind-errors-v4-migration`, fallback to
122+
`tailwind-impl-migration-v3-v4`
123+
124+
**Fix** :
125+
- `corePlugins` : remove and replace with ESLint rule or `@source not`
126+
- `safelist` : convert to `@source inline("...")` in CSS
127+
- `separator` : remove ; v4 fixes separator to `:`
128+
129+
### Rule R-04 : Undefined Custom Class Outside @layer
130+
131+
**Scans** : `.css`
132+
133+
**Detect** : project is v3 and file contains a custom class
134+
declaration `(.classname { ... })` outside `@layer base` / `@layer
135+
components` / `@layer utilities`. Project is v4 and file contains a
136+
custom class without `@utility` or `@layer`.
137+
138+
**Severity** : Warning (cascade ordering unpredictable, overrides
139+
behave inconsistently)
140+
141+
**Sibling skill** : `tailwind-impl-config-v3`, `tailwind-impl-config-v4`
142+
143+
**Fix** : wrap in `@layer components` (v3) or rewrite as `@utility`
144+
(v4).
145+
146+
### Rule R-05 : Variant Stack Order
147+
148+
**Scans** : `.tsx`, `.jsx`, `.vue`, `.svelte`, `.html`
149+
150+
**Detect** : v4 project contains a class with two or more variants
151+
that look v3-style. Common patterns : `first:*:`, `last:*:`,
152+
`hover:lg:` (the lg should come first under v4 left-to-right rule for
153+
breakpoints AT the variant level).
154+
155+
**Severity** : Warning (visual regression after v3 to v4)
156+
157+
**Sibling skill** : `tailwind-syntax-variants`,
158+
`tailwind-impl-migration-v3-v4`
159+
160+
**Fix** : flip the order. `first:*:pt-0` becomes `*:first:pt-0`.
161+
162+
### Rule R-06 : Bare border / ring / placeholder Under v4
163+
164+
**Scans** : `.tsx`, `.jsx`, `.vue`, `.svelte`, `.html`
165+
166+
**Detect** : v4 project AND class list contains a bare `border`,
167+
`ring`, `placeholder` token with no following colour or width
168+
modifier on the same element.
169+
170+
**Severity** : Info (visual regression risk : default colour changed
171+
from `gray-200` / `blue-500` / `gray-400` to `currentColor`)
172+
173+
**Sibling skill** : `tailwind-errors-v4-migration`,
174+
`tailwind-impl-migration-v3-v4`
175+
176+
**Fix** : add explicit colour (`border-zinc-200`, `ring-blue-500`,
177+
`placeholder-zinc-400`) OR restore v3 defaults globally via `@layer
178+
base` (see method).
179+
180+
### Rule R-07 : Em-Dash in User-Facing String
181+
182+
**Scans** : `.tsx`, `.jsx`, `.vue`, `.svelte`, `.html`, `.mdx`
183+
184+
**Detect** : literal `` (U+2014) inside JSX text, attribute strings
185+
that render to users (`title`, `aria-label`, `placeholder`), or
186+
component children.
187+
188+
**Severity** : Info (typography standard ; CLAUDE.md project rule)
189+
190+
**Sibling skill** : (project rule, see workspace CLAUDE.md)
191+
192+
**Fix** : replace with `.`, `,`, or rewrite the sentence. NEVER
193+
replace with `-`.
194+
195+
### Rule R-08 : Deterministic Prop-to-Class Mapping
196+
197+
**Scans** : `.tsx`, `.jsx`, `.vue`, `.svelte`
198+
199+
**Detect** : component receives a `variant` / `color` / `size` prop
200+
and constructs the class via a switch statement, ternary chain, or
201+
function call that returns differently-shaped strings per branch.
202+
203+
**Severity** : Info (maintainability ; not a bug, but harder to
204+
audit)
205+
206+
**Sibling skill** : `tailwind-core-design-system`
207+
208+
**Fix** : refactor to a single object literal mapping prop value to
209+
complete class string.
210+
211+
### Rule R-09 : twMerge Order Mistake
212+
213+
**Scans** : `.tsx`, `.jsx`, `.vue`, `.svelte`
214+
215+
**Detect** : `twMerge(...)` or `cn(...)` call with conflicting
216+
utilities where the LATER argument is the loser (e.g.
217+
`twMerge('p-4', 'p-2')` resolves to `p-2` ; if the intent was `p-4`
218+
as the override, the argument order is wrong).
219+
220+
**Severity** : Info (requires intent inference ; flag for human
221+
review only)
222+
223+
**Sibling skill** : `tailwind-impl-tailwind-merge`
224+
225+
**Fix** : reverse the argument order so the override is last.
226+
227+
### Rule R-10 : Plugin Missing Default Theme Values
228+
229+
**Scans** : `*.js`, `*.ts` files exporting a Tailwind plugin
230+
231+
**Detect** : the plugin body calls `theme('someNamespace')` AND the
232+
plugin's second-arg config does NOT define
233+
`theme.extend.someNamespace`.
234+
235+
**Severity** : Warning (plugin works only when consumer happens to
236+
configure the namespace, fails silently otherwise)
237+
238+
**Sibling skill** : `tailwind-impl-plugins-custom`
239+
240+
**Fix** : ship defaults via the `plugin(fn, { theme: { ... } })`
241+
second argument.
242+
243+
## Report Format
244+
245+
```
246+
Tailwind Validator Report
247+
=========================
248+
Project state : v4 detected (tailwindcss@^4.0.5)
249+
Scan scope : src/ tests/ tailwind.config.js postcss.config.mjs
250+
Files scanned : 412
251+
Issues : 7 (1 error, 3 warnings, 3 info)
252+
253+
---
254+
255+
[ERROR] R-01 : dynamic class string
256+
File : src/components/Tag.tsx:14
257+
Code : `className={`bg-${color}-100 text-${color}-800`}`
258+
Cite : tailwind-errors-dynamic-classes (Fix 1, static map)
259+
Suggested : replace with lookup object indexed by `color`.
260+
261+
[ERROR] R-02 : scoped @apply without @reference
262+
File : src/components/Hero.vue:42
263+
Block : <style scoped> @apply text-2xl font-bold </style>
264+
Cite : tailwind-impl-apply-directive
265+
Suggested : prepend `@reference "../app.css";` inside <style>.
266+
267+
[WARNING] R-03 : forbidden v3 key in v4 config
268+
File : tailwind.config.js:8
269+
Key : safelist
270+
Cite : tailwind-impl-migration-v3-v4 (Method 7)
271+
Suggested : move to `@source inline(...)` in src/app.css and
272+
remove from JS config.
273+
274+
... etc
275+
```
276+
277+
Per-issue fields :
278+
279+
- Severity tag in brackets
280+
- Rule code
281+
- File and line number
282+
- The offending snippet
283+
- The sibling skill name (NOT a URL ; the in-package skill reference)
284+
- A one-line suggested fix
285+
286+
## Cross-Reference Index
287+
288+
| Rule | Authoritative Skill |
289+
|------|---------------------|
290+
| R-01 dynamic classes | `tailwind-errors-dynamic-classes` |
291+
| R-02 scoped @apply | `tailwind-impl-apply-directive` |
292+
| R-03 v3 keys in v4 | `tailwind-impl-migration-v3-v4` |
293+
| R-04 undefined custom class | `tailwind-impl-config-v3`, `tailwind-impl-config-v4` |
294+
| R-05 variant stack order | `tailwind-syntax-variants` |
295+
| R-06 bare border/ring | `tailwind-impl-migration-v3-v4` |
296+
| R-07 em-dash | workspace CLAUDE.md |
297+
| R-08 prop-to-class shape | `tailwind-core-design-system` |
298+
| R-09 twMerge ordering | `tailwind-impl-tailwind-merge` |
299+
| R-10 plugin theme defaults | `tailwind-impl-plugins-custom` |
300+
301+
## CI Integration
302+
303+
The validator is ADVISORY by default. Exit code is 0 unless errors
304+
exceed the configured threshold. Sample GitHub Actions step :
305+
306+
```yaml
307+
- name: Tailwind audit
308+
run: |
309+
claude --skill tailwind-agents-validator . > tailwind-report.txt
310+
cat tailwind-report.txt
311+
# fail only if errors > 0
312+
grep -q '^\[ERROR\]' tailwind-report.txt && exit 1 || exit 0
313+
```
314+
315+
NEVER block PRs on info or warning levels. Errors (R-01, R-02)
316+
indicate real production bugs and SHOULD block.
317+
318+
## Sequencing With Sibling Skills
319+
320+
The validator is NOT a substitute for the per-area skills. When a
321+
finding cites `tailwind-errors-dynamic-classes`, the developer (or
322+
the follow-up agent) opens that skill for the full fix catalogue.
323+
324+
Recommended workflow :
325+
326+
1. Run `tailwind-agents-validator`.
327+
2. For each error : open the cited skill, apply the documented fix.
328+
3. Re-run the validator. Iterate until errors = 0.
329+
4. Optional : address warnings and info as time allows.
330+
331+
## References
332+
333+
- `references/methods.md` : per-rule detection commands (grep, AST,
334+
PostCSS) and the v3-or-v4 heuristic in detail
335+
- `references/examples.md` : sample input codebase plus the validator
336+
report it should produce
337+
- `references/anti-patterns.md` : ten high-impact violations the
338+
validator catches, with before/after
339+
340+
## Sources
341+
342+
- All sibling skills in this package
343+
- Project CLAUDE.md (workspace conventions)

0 commit comments

Comments
 (0)