@csszyx/mcp-server 0.9.10 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -65,12 +65,14 @@ Key csszyx rules:
65
65
  - Colors are strings: { bg: 'blue-500' } \u2192 bg-blue-500
66
66
  - Color + opacity is an object: { bg: { color: 'blue-500', op: 50 } } \u2192 bg-blue-500/50
67
67
  - Variants nest: { hover: { bg: 'blue-700' } } \u2192 hover:bg-blue-700
68
- - Boolean sugar: { flex: true } \u2192 flex
68
+ - ONE way per property \u2014 single-property props use the canonical key with a value, never a boolean alias. display/position/visibility/isolation/textTransform/fontStyle/fontSmoothing/decoration: { display: 'flex' } \u2192 flex, { position: 'absolute' } \u2192 absolute, { fontStyle: 'italic' } \u2192 italic, { decoration: 'underline' } \u2192 underline, { fontSmoothing: 'grayscale' } \u2192 antialiased. Boolean aliases like { flex: true }/{ italic: true } were REMOVED and emit nothing.
69
69
  - Fractions stay native: { w: '1/2' } \u2192 w-1/2
70
70
  - Arbitrary values: { p: '5px' } \u2192 p-[5px]
71
71
  - CSS variables: { p: '--my-var' } \u2192 p-(--my-var)
72
72
  - Scope variants: { group: { hover: { bg: 'blue-700' } } } \u2192 group-hover:bg-blue-700 (also peer)
73
- - Conditional/feature variants: { has: { checked: { ... } } } \u2192 has-[:checked]:\u2026, { supports: { 'display:grid': { ... } } } \u2192 supports-[display:grid]:\u2026 (also not/data/aria)`
73
+ - Conditional/feature variants: { has: { checked: { ... } } } \u2192 has-[:checked]:\u2026, { supports: { 'display:grid': { ... } } } \u2192 supports-[display:grid]:\u2026 (also not/data/aria)
74
+ - Type-safe keys: the sz prop type is CLOSED \u2014 an unknown/typo key (e.g. { bgColor: 'red' }, canonical is { bg: 'red' }) is a TypeScript error, so a literal sz object is caught by tsc/CI. To force a brand-new Tailwind utility csszyx has no key for yet, opt out with // @ts-expect-error on that line (the runtime still emits the class). Arbitrary variants (@container, min-[320px], [&>span]) are allowed by pattern.
75
+ - Custom breakpoints are typed from CSS: define it once in Tailwind @theme (e.g. @theme { --breakpoint-tablet: 40rem }) and csszyx auto-generates the type so { tablet: { ... } } type-checks \u2014 no manual declaration.`
74
76
  }
75
77
  }
76
78
  ]
@@ -96,7 +98,7 @@ Please:
96
98
 
97
99
  Key csszyx syntax:
98
100
  - <div sz={{ p: 4, bg: 'blue-500', hover: { bg: 'blue-700' } }} />
99
- - <div sz={{ flex: true, items: 'center', gap: 4 }} />
101
+ - <div sz={{ display: 'flex', items: 'center', gap: 4 }} />
100
102
  - <div sz={{ md: { gridCols: 3 }, gridCols: 1, gap: 6 }} />
101
103
  - <div sz={{ dark: { bg: 'gray-900', color: 'white' } }} />
102
104
  - <div sz={{ bg: { color: 'blue-500', op: 50 }, w: '1/2' }} /> (color opacity + fraction)
@@ -356,11 +358,13 @@ const EXAMPLES = {
356
358
  h: ["{ h: 16 }", "{ h: 'screen' }"],
357
359
  text: ["{ text: 'lg' }", "{ text: '2xl' }"],
358
360
  color: ["{ color: 'white' }", "{ color: 'slate-500' }"],
359
- fontWeight: ["{ fontWeight: 'bold' }", "{ fontWeight: 700 }"],
361
+ weight: ["{ weight: 'bold' }", "{ weight: 700 }"],
360
362
  fontFamily: ["{ fontFamily: 'sans' }", "{ fontFamily: 'mono' }"],
361
- flex: ["{ flex: true }", "{ flex: 'auto' }", "{ flex: 1 }"],
363
+ display: ["{ display: 'flex' }", "{ display: 'none' }"],
364
+ position: ["{ position: 'absolute' }", "{ position: 'relative' }"],
365
+ flex: ["{ flex: 1 }", "{ flex: 'auto' }"],
362
366
  flexDir: ["{ flexDir: 'col' }", "{ flexDir: 'row' }"],
363
- grid: ["{ grid: true }"],
367
+ grid: ["{ display: 'grid' }"],
364
368
  gridCols: ["{ gridCols: 3 }", "{ gridCols: 'none' }"],
365
369
  gap: ["{ gap: 4 }"],
366
370
  items: ["{ items: 'center' }", "{ items: 'start' }"],
package/llms-full.txt CHANGED
@@ -48,20 +48,37 @@ module.exports = {
48
48
  <div className="flex items-center p-4 bg-blue-500 rounded-lg" />
49
49
 
50
50
  // After (CSSzyx)
51
- <div sz={{ flex: true, items: 'center', p: 4, bg: 'blue-500', rounded: 'lg' }} />
51
+ <div sz={{ display: 'flex', items: 'center', p: 4, bg: 'blue-500', rounded: 'lg' }} />
52
52
  ```
53
53
 
54
54
  The compiler transforms `sz` props → Tailwind classes at build time. The browser never sees `sz`.
55
55
 
56
- ### Boolean shorthand
56
+ ### One key per property
57
57
 
58
- Many display/position properties accept `true`:
58
+ Single-property props use their canonical key with a value — one way, no
59
+ aliases. display/position/visibility/isolation/textTransform/fontStyle/
60
+ fontSmoothing/decoration:
59
61
 
60
62
  ```tsx
61
- <div sz={{ flex: true, relative: true, hidden: true }} />
62
- // → className="flex relative hidden"
63
+ <div
64
+ sz={{
65
+ display: "flex",
66
+ position: "relative",
67
+ visibility: "hidden",
68
+ fontStyle: "italic",
69
+ }}
70
+ />
71
+ // → className="flex relative invisible italic"
63
72
  ```
64
73
 
74
+ Boolean aliases (`{ flex: true }`, `{ italic: true }`, `{ uppercase: true }`)
75
+ were **removed** — they emit nothing and warn; run `csszyx migrate` to rewrite.
76
+ Each property has exactly one key, so it can never be set twice in one object.
77
+
78
+ Genuinely on/off utilities — composite (`truncate`, `srOnly`), stackable
79
+ font-variant-numeric (`ordinal`, `tabularNums`), default-or-value (`grow`,
80
+ `ring`, `blur`), plugin (`container`, `prose`) — stay boolean.
81
+
65
82
  ### Arbitrary values
66
83
 
67
84
  ```tsx
@@ -162,7 +179,7 @@ sz={{ group: { card: { data: { active: { color: 'blue-600' } } } } }}
162
179
  sz={{ group: { aria: { expanded: { color: 'blue-600' } } } }}
163
180
  // → className="group-aria-expanded:text-blue-600"
164
181
 
165
- sz={{ group: { aria: { 'current=page': { fontWeight: 'bold' } } } }}
182
+ sz={{ group: { aria: { 'current=page': { weight: 'bold' } } } }}
166
183
  // → className="group-aria-[current=page]:font-bold"
167
184
 
168
185
  // peer-data / peer-aria work the same way
@@ -195,7 +212,7 @@ sz={{ placeholder: { color: 'gray-400' } }}
195
212
  ### Container queries
196
213
 
197
214
  ```tsx
198
- sz={{ '@container': true, '@md': { flex: true } }}
215
+ sz={{ '@container': true, '@md': { display: 'flex' } }}
199
216
  // → className="@container @md:flex"
200
217
  ```
201
218
 
@@ -221,7 +238,35 @@ sz={{ '@container': true, '@md': { flex: true } }}
221
238
  <div className="p-4" sz={{ m: 2 }} /> // Wrong — use sz only
222
239
 
223
240
  // ✅ Correct
224
- { p: 4, bg: 'blue-500', flex: true, flexDir: 'col', w: '333px', mt: 4, text: 'lg' }
241
+ { p: 4, bg: 'blue-500', display: 'flex', flexDir: 'col', w: '333px', mt: 4, text: 'lg' }
242
+ ```
243
+
244
+ ## Type safety — unknown keys are TypeScript errors
245
+
246
+ The `sz` prop type is **closed**: a key that is not a known sz prop or variant is a
247
+ compile error, so typos and legacy CSS-property names are caught by `tsc` (run
248
+ `tsc --noEmit` in CI to enforce it).
249
+
250
+ ```tsx
251
+ {
252
+ bgColor: "red-500";
253
+ } // ❌ tsc error — unknown key; canonical is { bg: 'red-500' }
254
+
255
+ // Forcing a brand-new Tailwind utility csszyx has no key for yet — opt out explicitly:
256
+ // @ts-expect-error - forward-compat utility not yet in csszyx
257
+ {
258
+ someNewUtility: "x";
259
+ } // ✅ allowed; the runtime still emits the class
260
+ ```
261
+
262
+ Arbitrary variants are allowed by pattern: `{ '@container': {…} }`, `{ 'min-[320px]': {…} }`,
263
+ `{ '[&>span]': {…} }`. **Custom breakpoints are typed from your CSS** — define one once in
264
+ Tailwind `@theme` and csszyx auto-generates the type:
265
+
266
+ ```css
267
+ @theme {
268
+ --breakpoint-tablet: 40rem;
269
+ } /* → { tablet: { p: 6 } } now type-checks */
225
270
  ```
226
271
 
227
272
  ## Full Snippets Reference
@@ -241,7 +286,7 @@ Simple property-value mappings.
241
286
  | **Text Color** | `color: white` | `text-white` | `{ color: 'white' }` |
242
287
  | **Border Radius** | `border-radius: var(--radius-lg)` | `rounded-lg` | `{ rounded: 'lg' }` |
243
288
  | **Box Shadow** | `box-shadow: var(--shadow-xl)` | `shadow-xl` | `{ shadow: 'xl' }` |
244
- | **Flex Container** | `display: flex` | `flex` | `{ flex: true }` |
289
+ | **Flex Container** | `display: flex` | `flex` | `{ display: 'flex' }` |
245
290
 
246
291
  ## State Modifiers (Hover, Focus)
247
292
 
@@ -258,11 +303,11 @@ Handling pseudo-classes using nested objects or string prefixes.
258
303
 
259
304
  Handling media queries via properties.
260
305
 
261
- | Concept | CSS Rule | Tailwind v4 Class | `sz` Prop (Object Syntax) |
262
- | :------------------- | :---------------------------------- | :---------------- | :------------------------ |
263
- | **Small Breakpoint** | `@media (width >= 40rem) { (etc) }` | `sm:grid-cols-3` | `{ sm: { gridCols: 3 } }` |
264
- | **Medium Layout** | `@media (width >= 48rem) { (etc) }` | `md:flex` | `{ md: { flex: true } }` |
265
- | **Large Spacing** | `@media (width >= 64rem) { (etc) }` | `lg:px-8` | `{ lg: { px: 8 } }` |
306
+ | Concept | CSS Rule | Tailwind v4 Class | `sz` Prop (Object Syntax) |
307
+ | :------------------- | :---------------------------------- | :---------------- | :---------------------------- |
308
+ | **Small Breakpoint** | `@media (width >= 40rem) { (etc) }` | `sm:grid-cols-3` | `{ sm: { gridCols: 3 } }` |
309
+ | **Medium Layout** | `@media (width >= 48rem) { (etc) }` | `md:flex` | `{ md: { display: 'flex' } }` |
310
+ | **Large Spacing** | `@media (width >= 64rem) { (etc) }` | `lg:px-8` | `{ lg: { px: 8 } }` |
266
311
 
267
312
  ## Dark Mode
268
313
 
@@ -341,9 +386,9 @@ Standard interactive states.
341
386
  | **Hover, Focus, Active** | `hover:bg-red focus:bg-blue active:bg-green` | `{ hover: { bg: 'red' }, focus: { bg: 'blue' }, active: { bg: 'green' } }` | |
342
387
  | **First/Last Child** | `first:pt-0 last:pb-0` | `{ first: { pt: 0 }, last: { pb: 0 } }` | |
343
388
  | **Odd/Even Child** | `odd:bg-white even:bg-gray` | `{ odd: { bg: 'white' }, even: { bg: 'gray' } }` | |
344
- | **First of Type** | `first-of-type:block` | `{ firstOfType: { block: true } }` | **Sugar**: CamelCase alias. |
345
- | **Only Child** | `only:block` | `{ only: { block: true } }` | |
346
- | **Empty** | `empty:hidden` | `{ empty: { hidden: true } }` | |
389
+ | **First of Type** | `first-of-type:block` | `{ firstOfType: { display: 'block' } }` | **Sugar**: CamelCase alias. |
390
+ | **Only Child** | `only:block` | `{ only: { display: 'block' } }` | |
391
+ | **Empty** | `empty:hidden` | `{ empty: { display: 'none' } }` | |
347
392
  | **Visited** | `visited:text-purple` | `{ visited: { color: 'purple' } }` | |
348
393
  | **Focus Within** | `focus-within:ring` | `{ focusWithin: { ring: true } }` | **Sugar**: CamelCase alias. |
349
394
  | **Focus Visible** | `focus-visible:ring` | `{ focusVisible: { ring: true } }` | **Sugar**: CamelCase alias. |
@@ -353,11 +398,11 @@ Standard interactive states.
353
398
 
354
399
  Styling based on descendants.
355
400
 
356
- | Concept | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
357
- | :----------------- | :-------------------------- | :---------------------------------------------- | :------------------------------------ |
358
- | **Has Descendant** | `has-[img]:bg-blue` | `{ has: { img: { bg: 'blue' } } }` | **Sugar**: Nested selector. |
359
- | **Has State** | `has-[:checked]:bg-blue` | `{ has: { checked: { bg: 'blue' } } }` | **Sugar**: Auto-detects pseudo-class. |
360
- | **Arbitrary Has** | `has-[.custom-class]:block` | `{ has: { '.custom-class': { block: true } } }` | |
401
+ | Concept | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
402
+ | :----------------- | :-------------------------- | :--------------------------------------------------- | :------------------------------------ |
403
+ | **Has Descendant** | `has-[img]:bg-blue` | `{ has: { img: { bg: 'blue' } } }` | **Sugar**: Nested selector. |
404
+ | **Has State** | `has-[:checked]:bg-blue` | `{ has: { checked: { bg: 'blue' } } }` | **Sugar**: Auto-detects pseudo-class. |
405
+ | **Arbitrary Has** | `has-[.custom-class]:block` | `{ has: { '.custom-class': { display: 'block' } } }` | |
361
406
 
362
407
  ## Styling based on parent state (Groups)
363
408
 
@@ -369,76 +414,76 @@ Styling children based on parent `group` class.
369
414
  | **Group Focus** | `group-focus:text-white` | `{ group: { focus: { color: 'white' } } }` | |
370
415
  | **Group Active** | `group-active:text-white` | `{ group: { active: { color: 'white' } } }` | |
371
416
  | **Nested Groups** | `group-hover/name:text-white` | `{ group: { name: { hover: { color: 'white' } } } }` | **Sugar**: Scope name as nested key. |
372
- | **Arbitrary Groups** | `group-[.is-published]:block` | `{ group: { '.is-published': { block: true } } }` | |
373
- | **Group Has** | `group-has-[a]:block` | `{ group: { has: { a: { block: true } } } }` | |
417
+ | **Arbitrary Groups** | `group-[.is-published]:block` | `{ group: { '.is-published': { display: 'block' } } }` | |
418
+ | **Group Has** | `group-has-[a]:block` | `{ group: { has: { a: { display: 'block' } } } }` | |
374
419
  | **Group Data** | `group-data-[active]:text-blue` | `{ group: { data: { active: { color: 'blue' } } } }` | **Sugar**: Nested `data` key. |
375
420
  | **Group Data (named)** | `group-data-[active]/card:text-blue` | `{ group: { card: { data: { active: { color: 'blue' } } } } }` | Name before `data` key. |
376
- | **Group Data (value match)** | `group-data-[state=open]:block` | `{ group: { data: { 'state=open': { block: true } } } }` | `=` in key → bracket form always. |
377
- | **Group ARIA** | `group-aria-expanded:block` | `{ group: { aria: { expanded: { block: true } } } }` | Standard states: bare form. |
378
- | **Group ARIA (arbitrary)** | `group-aria-[current=page]:font-bold` | `{ group: { aria: { 'current=page': { fontWeight: 'bold' } } } }` | Non-standard: bracket form. |
421
+ | **Group Data (value match)** | `group-data-[state=open]:block` | `{ group: { data: { 'state=open': { display: 'block' } } } }` | `=` in key → bracket form always. |
422
+ | **Group ARIA** | `group-aria-expanded:block` | `{ group: { aria: { expanded: { display: 'block' } } } }` | Standard states: bare form. |
423
+ | **Group ARIA (arbitrary)** | `group-aria-[current=page]:font-bold` | `{ group: { aria: { 'current=page': { weight: 'bold' } } } }` | Non-standard: bracket form. |
379
424
 
380
425
  ## Styling based on sibling state (Peers)
381
426
 
382
427
  Styling based on previous sibling `peer` class.
383
428
 
384
- | Concept | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
385
- | :-------------------------- | :---------------------------------- | :--------------------------------------------------------- | :----------------------------------- |
386
- | **Peer Hover** | `peer-hover:text-white` | `{ peer: { hover: { color: 'white' } } }` | **Sugar**: Nested scope. |
387
- | **Peer Checked** | `peer-checked:bg-blue` | `{ peer: { checked: { bg: 'blue' } } }` | |
388
- | **Differentiating Peers** | `peer-checked/name:bg-blue` | `{ peer: { name: { checked: { bg: 'blue' } } } }` | **Sugar**: Scope name as nested key. |
389
- | **Arbitrary Peers** | `peer-[.is-dirty]:block` | `{ peer: { '.is-dirty': { block: true } } }` | |
390
- | **Peer Data** | `peer-data-[active]:text-blue` | `{ peer: { data: { active: { color: 'blue' } } } }` | **Sugar**: Nested `data` key. |
391
- | **Peer Data (value match)** | `peer-data-[state=open]:block` | `{ peer: { data: { 'state=open': { block: true } } } }` | `=` in key → bracket form always. |
392
- | **Peer ARIA** | `peer-aria-checked:bg-blue` | `{ peer: { aria: { checked: { bg: 'blue' } } } }` | Standard states: bare form. |
393
- | **Peer ARIA (arbitrary)** | `peer-aria-[invalid=true]:text-red` | `{ peer: { aria: { 'invalid=true': { color: 'red' } } } }` | Non-standard: bracket form. |
429
+ | Concept | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
430
+ | :-------------------------- | :---------------------------------- | :----------------------------------------------------------- | :----------------------------------- |
431
+ | **Peer Hover** | `peer-hover:text-white` | `{ peer: { hover: { color: 'white' } } }` | **Sugar**: Nested scope. |
432
+ | **Peer Checked** | `peer-checked:bg-blue` | `{ peer: { checked: { bg: 'blue' } } }` | |
433
+ | **Differentiating Peers** | `peer-checked/name:bg-blue` | `{ peer: { name: { checked: { bg: 'blue' } } } }` | **Sugar**: Scope name as nested key. |
434
+ | **Arbitrary Peers** | `peer-[.is-dirty]:block` | `{ peer: { '.is-dirty': { display: 'block' } } }` | |
435
+ | **Peer Data** | `peer-data-[active]:text-blue` | `{ peer: { data: { active: { color: 'blue' } } } }` | **Sugar**: Nested `data` key. |
436
+ | **Peer Data (value match)** | `peer-data-[state=open]:block` | `{ peer: { data: { 'state=open': { display: 'block' } } } }` | `=` in key → bracket form always. |
437
+ | **Peer ARIA** | `peer-aria-checked:bg-blue` | `{ peer: { aria: { checked: { bg: 'blue' } } } }` | Standard states: bare form. |
438
+ | **Peer ARIA (arbitrary)** | `peer-aria-[invalid=true]:text-red` | `{ peer: { aria: { 'invalid=true': { color: 'red' } } } }` | Non-standard: bracket form. |
394
439
 
395
440
  ## :not()
396
441
 
397
442
  Inverse conditions.
398
443
 
399
- | Concept | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
400
- | :--------------- | :---------------------------------- | :----------------------------------------------------------- | :--- |
401
- | **Not Hover** | `not-hover:opacity-75` | `{ not: { hover: { opacity: 75 } } }` | |
402
- | **Not First** | `not-first:mt-4` | `{ not: { first: { mt: 4 } } }` | |
403
- | **Not Supports** | `not-supports-[display:grid]:block` | `{ not: { supports: { 'display:grid': { block: true } } } }` | |
444
+ | Concept | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
445
+ | :--------------- | :---------------------------------- | :---------------------------------------------------------------- | :--- |
446
+ | **Not Hover** | `not-hover:opacity-75` | `{ not: { hover: { opacity: 75 } } }` | |
447
+ | **Not First** | `not-first:mt-4` | `{ not: { first: { mt: 4 } } }` | |
448
+ | **Not Supports** | `not-supports-[display:grid]:block` | `{ not: { supports: { 'display:grid': { display: 'block' } } } }` | |
404
449
 
405
450
  ## Pseudo-elements
406
451
 
407
452
  Advanced content styling.
408
453
 
409
- | Concept | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
410
- | :--------------- | :---------------------- | :----------------------------------- | :-------------------- |
411
- | **Before/After** | `before:content-['']` | `{ before: { (etc) } }` | Defaults used. |
412
- | **Placeholder** | `placeholder:text-gray` | `{ placeholder: { color: 'gray' } }` | |
413
- | **File** | `file:border` | `{ file: { border: true } }` | |
414
- | **Marker** | `marker:text-blue` | `{ marker: { color: 'blue' } }` | |
415
- | **Selection** | `selection:bg-pink` | `{ selection: { bg: 'pink' } }` | |
416
- | **First Line** | `first-line:uppercase` | `{ firstLine: { uppercase: true } }` | **Sugar**: CamelCase. |
417
- | **First Letter** | `first-letter:text-7xl` | `{ firstLetter: { text: '7xl' } }` | **Sugar**: CamelCase. |
418
- | **Backdrop** | `backdrop:blur` | `{ backdrop: { blur: true } }` | |
454
+ | Concept | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
455
+ | :--------------- | :---------------------- | :---------------------------------------------- | :-------------------- |
456
+ | **Before/After** | `before:content-['']` | `{ before: { (etc) } }` | Defaults used. |
457
+ | **Placeholder** | `placeholder:text-gray` | `{ placeholder: { color: 'gray' } }` | |
458
+ | **File** | `file:border` | `{ file: { border: true } }` | |
459
+ | **Marker** | `marker:text-blue` | `{ marker: { color: 'blue' } }` | |
460
+ | **Selection** | `selection:bg-pink` | `{ selection: { bg: 'pink' } }` | |
461
+ | **First Line** | `first-line:uppercase` | `{ firstLine: { textTransform: 'uppercase' } }` | **Sugar**: CamelCase. |
462
+ | **First Letter** | `first-letter:text-7xl` | `{ firstLetter: { text: '7xl' } }` | **Sugar**: CamelCase. |
463
+ | **Backdrop** | `backdrop:blur` | `{ backdrop: { blur: true } }` | |
419
464
 
420
465
  ## Media & Feature Queries
421
466
 
422
467
  Environment-based styling.
423
468
 
424
- | Concept | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
425
- | :-------------------- | :-------------------------------- | :-------------------------------------------------- | :---------------------------- |
426
- | **Breakpoints** | `md:block lg:flex` | `{ md: { block: true }, lg: { flex: true } }` | |
427
- | **Container Queries** | `@md:block @lg:flex` | `{ '@md': { block: true }, '@lg': { flex: true } }` | **Note**: String key for `@`. |
428
- | **Reduced Motion** | `motion-reduce:hidden` | `{ motionReduce: { hidden: true } }` | **Sugar**: CamelCase. |
429
- | **Prefers Contrast** | `contrast-more:border` | `{ contrastMore: { border: true } }` | |
430
- | **Forced Colors** | `forced-colors:border-gray` | `{ forcedColors: { borderColor: 'gray' } }` | **Sugar**: CamelCase. |
431
- | **Inverted Colors** | `inverted-colors:invert` | `{ invertedColors: { invert: true } }` | **Sugar**: CamelCase. |
432
- | **Pointer** | `pointer-coarse:p-4` | `{ pointerCoarse: { p: 4 } }` | **Sugar**: CamelCase. |
433
- | **Any-Pointer** | `any-pointer-fine:cursor-pointer` | `{ anyPointerFine: { cursor: 'pointer' } }` | v4.1. **Sugar**: CamelCase. |
434
- | **User Valid** | `user-valid:border-green-500` | `{ userValid: { borderColor: 'green-500' } }` | v4.1. Form validation state. |
435
- | **User Invalid** | `user-invalid:border-red-500` | `{ userInvalid: { borderColor: 'red-500' } }` | v4.1. Form validation state. |
436
- | **Details Content** | `details-content:block` | `{ detailsContent: { block: true } }` | v4.1. **Sugar**: CamelCase. |
437
- | **Print** | `print:hidden` | `{ print: { hidden: true } }` | |
438
- | **Orientation** | `portrait:hidden` | `{ portrait: { hidden: true } }` | |
439
- | **Scripting** | `noscript:block` | `{ noscript: { block: true } }` | |
440
- | **Supports** | `supports-[display:grid]:grid` | `{ supports: { 'display:grid': { grid: true } } }` | |
441
- | **Starting Style** | `starting:opacity-0` | `{ starting: { opacity: 0 } }` | |
469
+ | Concept | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
470
+ | :-------------------- | :-------------------------------- | :------------------------------------------------------------ | :---------------------------- |
471
+ | **Breakpoints** | `md:block lg:flex` | `{ md: { display: 'block' }, lg: { display: 'flex' } }` | |
472
+ | **Container Queries** | `@md:block @lg:flex` | `{ '@md': { display: 'block' }, '@lg': { display: 'flex' } }` | **Note**: String key for `@`. |
473
+ | **Reduced Motion** | `motion-reduce:hidden` | `{ motionReduce: { display: 'none' } }` | **Sugar**: CamelCase. |
474
+ | **Prefers Contrast** | `contrast-more:border` | `{ contrastMore: { border: true } }` | |
475
+ | **Forced Colors** | `forced-colors:border-gray` | `{ forcedColors: { borderColor: 'gray' } }` | **Sugar**: CamelCase. |
476
+ | **Inverted Colors** | `inverted-colors:invert` | `{ invertedColors: { invert: true } }` | **Sugar**: CamelCase. |
477
+ | **Pointer** | `pointer-coarse:p-4` | `{ pointerCoarse: { p: 4 } }` | **Sugar**: CamelCase. |
478
+ | **Any-Pointer** | `any-pointer-fine:cursor-pointer` | `{ anyPointerFine: { cursor: 'pointer' } }` | v4.1. **Sugar**: CamelCase. |
479
+ | **User Valid** | `user-valid:border-green-500` | `{ userValid: { borderColor: 'green-500' } }` | v4.1. Form validation state. |
480
+ | **User Invalid** | `user-invalid:border-red-500` | `{ userInvalid: { borderColor: 'red-500' } }` | v4.1. Form validation state. |
481
+ | **Details Content** | `details-content:block` | `{ detailsContent: { display: 'block' } }` | v4.1. **Sugar**: CamelCase. |
482
+ | **Print** | `print:hidden` | `{ print: { display: 'none' } }` | |
483
+ | **Orientation** | `portrait:hidden` | `{ portrait: { display: 'none' } }` | |
484
+ | **Scripting** | `noscript:block` | `{ noscript: { display: 'block' } }` | |
485
+ | **Supports** | `supports-[display:grid]:grid` | `{ supports: { 'display:grid': { display: 'grid' } } }` | |
486
+ | **Starting Style** | `starting:opacity-0` | `{ starting: { opacity: 0 } }` | |
442
487
 
443
488
  ## Attribute Selectors
444
489
 
@@ -476,8 +521,8 @@ Targeting specific screen sizes and container states.
476
521
  | Concept | CSS Rule | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
477
522
  | :----------------------- | :-------------------------- | :------------------------ | :------------------------------------------------ | :---------------------------------- |
478
523
  | **Mobile First** | `min-width: (etc)` | `w-full md:w-1/2` | `{ w: 'full', md: { w: '1/2' } }` | Unprefixed utilities target mobile. |
479
- | **Breakpoint Range** | `768px <= width < 1280px` | `md:max-xl:flex` | `{ md: { maxXl: { flex: true } } }` | **Sugar**: CamelCase for `max-xl`. |
480
- | **Single Breakpoint** | `md only` | `md:max-lg:flex` | `{ md: { maxLg: { flex: true } } }` | Target specific range. |
524
+ | **Breakpoint Range** | `768px <= width < 1280px` | `md:max-xl:flex` | `{ md: { maxXl: { display: 'flex' } } }` | **Sugar**: CamelCase for `max-xl`. |
525
+ | **Single Breakpoint** | `md only` | `md:max-lg:flex` | `{ md: { maxLg: { display: 'flex' } } }` | Target specific range. |
481
526
  | **Custom Breakpoint** | `@media (min-width: 320px)` | `min-[320px]:text-center` | `{ min: { '[320px]': { textAlign: 'center' } } }` | Arbitrary one-off breakpoint. |
482
527
  | **Max-Width Breakpoint** | `@media (max-width: 600px)` | `max-[600px]:bg-sky-300` | `{ max: { '[600px]': { bg: 'sky-300' } } }` | |
483
528
 
@@ -497,15 +542,15 @@ The `container` class sets `width: 100%` and applies responsive max-widths.
497
542
 
498
543
  The `@container` class enables CSS container queries.
499
544
 
500
- | Concept | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
501
- | :----------------------- | :------------------- | :------------------------------------------ | :---------------------------------------- |
502
- | **Mark Container** | `@container` | `{ '@container': true }` | |
503
- | **Named Container** | `@container/sidebar` | `{ '@container': 'sidebar' }` | **Sugar**: Value string = container name. |
504
- | **Container Breakpoint** | `@md:flex` | `{ '@md': { flex: true } }` | **Note**: `@` prefix in key. |
505
- | **Named Query** | `@md/sidebar:block` | `{ '@md': { sidebar: { block: true } } }` | **Sugar**: Nest name inside query key. |
506
- | **Container Range** | `@sm:@max-md:block` | `{ '@sm': { '@maxMd': { block: true } } }` | |
507
- | **Arbitrary Query** | `@min-[475px]:flex` | `{ '@min': { '[475px]': { flex: true } } }` | |
508
- | **Container Units** | `w-[50cqw]` | `{ w: '50cqw' }` | |
545
+ | Concept | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
546
+ | :----------------------- | :------------------- | :----------------------------------------------- | :---------------------------------------- |
547
+ | **Mark Container** | `@container` | `{ '@container': true }` | |
548
+ | **Named Container** | `@container/sidebar` | `{ '@container': 'sidebar' }` | **Sugar**: Value string = container name. |
549
+ | **Container Breakpoint** | `@md:flex` | `{ '@md': { display: 'flex' } }` | **Note**: `@` prefix in key. |
550
+ | **Named Query** | `@md/sidebar:block` | `{ '@md': { sidebar: { display: 'block' } } }` | **Sugar**: Nest name inside query key. |
551
+ | **Container Range** | `@sm:@max-md:block` | `{ '@sm': { '@maxMd': { display: 'block' } } }` | |
552
+ | **Arbitrary Query** | `@min-[475px]:flex` | `{ '@min': { '[475px]': { display: 'flex' } } }` | |
553
+ | **Container Units** | `w-[50cqw]` | `{ w: '50cqw' }` | |
509
554
 
510
555
  ## Dark mode
511
556
 
@@ -836,14 +881,14 @@ Controlling the width of an element's borders.
836
881
 
837
882
  Controlling the color of an element's borders.
838
883
 
839
- | Concept | CSS Rule | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
840
- | :-------------------- | :---------------------------- | :------------------ | :---------------------------------------------- | :----------------------------------------- |
841
- | **Color** | `border-color: currentColor` | `border-red-500` | `{ borderColor: 'red-500' }` | |
842
- | **Opacity** | `border-color: currentColor` | `border-red-500/50` | `{ borderColor: { color: 'red-500', op: 50 } }` | |
843
- | **CSS Var + Opacity** | `border-color: var(--c) / 50` | `border-(--c)/50` | `{ borderColor: { color: '--c', op: 50 } }` | CSS variables are auto-wrapped in `(...)`. |
844
- | **X/Y/T/R/B/L** | `border-color: currentColor` | `border-t-red-500` | `{ borderTColor: 'red-500' }` | Verbose keys to avoid conflict with Width. |
845
- | **Arbitrary** | `border-color: currentColor` | `border-[#50d71e]` | `{ borderColor: '#50d71e' }` | |
846
- | **Variable** | `border-color: var(--c)` | `border-(--c)` | `{ borderColor: '--c' }` | |
884
+ | Concept | CSS Rule | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
885
+ | :-------------------- | :------------------------------------------------------------------------- | :------------------ | :---------------------------------------------- | :----------------------------------------- |
886
+ | **Color** | `border-color: var(--color-red-500)` | `border-red-500` | `{ borderColor: 'red-500' }` | |
887
+ | **Opacity** | `border-color: color-mix(in oklab, var(--color-red-500) 50%, transparent)` | `border-red-500/50` | `{ borderColor: { color: 'red-500', op: 50 } }` | |
888
+ | **CSS Var + Opacity** | `border-color: var(--c) / 50` | `border-(--c)/50` | `{ borderColor: { color: '--c', op: 50 } }` | CSS variables are auto-wrapped in `(...)`. |
889
+ | **X/Y/T/R/B/L** | `border-top-color: var(--color-red-500)` | `border-t-red-500` | `{ borderTColor: 'red-500' }` | Verbose keys to avoid conflict with Width. |
890
+ | **Arbitrary** | `border-color: #50d71e` | `border-[#50d71e]` | `{ borderColor: '#50d71e' }` | |
891
+ | **Variable** | `border-color: var(--c)` | `border-(--c)` | `{ borderColor: '--c' }` | |
847
892
 
848
893
  ## Border Style
849
894
 
@@ -877,13 +922,13 @@ Utilities for controlling the border width between elements.
877
922
 
878
923
  Utilities for controlling the border color between elements.
879
924
 
880
- | Concept | CSS Rule | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
881
- | :-------------------- | :---------------------------- | :------------------ | :---------------------------------------------- | :----------------------------------------- |
882
- | **Color** | `border-color: currentColor` | `divide-red-500` | `{ divideColor: 'red-500' }` | |
883
- | **Opacity** | `border-color: currentColor` | `divide-red-500/50` | `{ divideColor: { color: 'red-500', op: 50 } }` | |
884
- | **CSS Var + Opacity** | `border-color: var(--c) / 50` | `divide-(--c)/50` | `{ divideColor: { color: '--c', op: 50 } }` | CSS variables are auto-wrapped in `(...)`. |
885
- | **Arbitrary** | `border-color: currentColor` | `divide-[#50d71e]` | `{ divideColor: '#50d71e' }` | |
886
- | **Variable** | `border-color: var(--c)` | `divide-(--c)` | `{ divideColor: '--c' }` | |
925
+ | Concept | CSS Rule | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
926
+ | :-------------------- | :------------------------------------------------------------------------- | :------------------ | :---------------------------------------------- | :----------------------------------------- |
927
+ | **Color** | `border-color: var(--color-red-500)` | `divide-red-500` | `{ divideColor: 'red-500' }` | Applied between children. |
928
+ | **Opacity** | `border-color: color-mix(in oklab, var(--color-red-500) 50%, transparent)` | `divide-red-500/50` | `{ divideColor: { color: 'red-500', op: 50 } }` | |
929
+ | **CSS Var + Opacity** | `border-color: var(--c) / 50` | `divide-(--c)/50` | `{ divideColor: { color: '--c', op: 50 } }` | CSS variables are auto-wrapped in `(...)`. |
930
+ | **Arbitrary** | `border-color: #50d71e` | `divide-[#50d71e]` | `{ divideColor: '#50d71e' }` | |
931
+ | **Variable** | `border-color: var(--c)` | `divide-(--c)` | `{ divideColor: '--c' }` | |
887
932
 
888
933
  ## Divide Style
889
934
 
@@ -911,11 +956,11 @@ Controlling the width of an element's outline.
911
956
 
912
957
  Controlling the color of an element's outline.
913
958
 
914
- | Concept | CSS Rule | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
915
- | :------------ | :---------------------------- | :------------------ | :---------------------------- | :--- |
916
- | **Color** | `outline-color: currentColor` | `outline-red-500` | `{ outlineColor: 'red-500' }` | |
917
- | **Arbitrary** | `outline-color: currentColor` | `outline-[#50d71e]` | `{ outlineColor: '#50d71e' }` | |
918
- | **Variable** | `outline-color: var(--c)` | `outline-(--c)` | `{ outlineColor: '--c' }` | |
959
+ | Concept | CSS Rule | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
960
+ | :------------ | :------------------------------------ | :------------------ | :---------------------------- | :--- |
961
+ | **Color** | `outline-color: var(--color-red-500)` | `outline-red-500` | `{ outlineColor: 'red-500' }` | |
962
+ | **Arbitrary** | `outline-color: #50d71e` | `outline-[#50d71e]` | `{ outlineColor: '#50d71e' }` | |
963
+ | **Variable** | `outline-color: var(--c)` | `outline-(--c)` | `{ outlineColor: '--c' }` | |
919
964
 
920
965
  ## Outline Style
921
966
 
@@ -2150,31 +2195,31 @@ Controlling box model sizing.
2150
2195
 
2151
2196
  Controlling display box type.
2152
2197
 
2153
- | Concept | CSS Rule | Tailwind v4 Class | `sz` Prop (Canonical) | `sz` Prop (Boolean Sugar) |
2154
- | :--------------------- | :---------------------------- | :------------------- | :---------------------------------- | :--------------------------- |
2155
- | **Block** | `display: block` | `block` | `{ display: 'block' }` | `{ block: true }` |
2156
- | **Inline Block** | `display: inline-block` | `inline-block` | `{ display: 'inline-block' }` | `{ inlineBlock: true }` |
2157
- | **Inline** | `display: inline` | `inline` | `{ display: 'inline' }` | `{ inline: true }` |
2158
- | **Flex** | `display: flex` | `flex` | `{ display: 'flex' }` | `{ flex: true }` |
2159
- | **Inline Flex** | `display: inline-flex` | `inline-flex` | `{ display: 'inline-flex' }` | `{ inlineFlex: true }` |
2160
- | **Grid** | `display: grid` | `grid` | `{ display: 'grid' }` | `{ grid: true }` |
2161
- | **Inline Grid** | `display: inline-grid` | `inline-grid` | `{ display: 'inline-grid' }` | `{ inlineGrid: true }` |
2162
- | **Contents** | `display: contents` | `contents` | `{ display: 'contents' }` | `{ contents: true }` |
2163
- | **Table** | `display: table` | `table` | `{ display: 'table' }` | `{ table: true }` |
2164
- | **Inline Table** | `display: inline-table` | `inline-table` | `{ display: 'inline-table' }` | `{ inlineTable: true }` |
2165
- | **Table Caption** | `display: table-caption` | `table-caption` | `{ display: 'table-caption' }` | `{ tableCaption: true }` |
2166
- | **Table Cell** | `display: table-cell` | `table-cell` | `{ display: 'table-cell' }` | `{ tableCell: true }` |
2167
- | **Table Column** | `display: table-column` | `table-column` | `{ display: 'table-column' }` | `{ tableColumn: true }` |
2168
- | **Table Column Group** | `display: table-column-group` | `table-column-group` | `{ display: 'table-column-group' }` | `{ tableColumnGroup: true }` |
2169
- | **Table Footer Group** | `display: table-footer-group` | `table-footer-group` | `{ display: 'table-footer-group' }` | `{ tableFooterGroup: true }` |
2170
- | **Table Header Group** | `display: table-header-group` | `table-header-group` | `{ display: 'table-header-group' }` | `{ tableHeaderGroup: true }` |
2171
- | **Table Row Group** | `display: table-row-group` | `table-row-group` | `{ display: 'table-row-group' }` | `{ tableRowGroup: true }` |
2172
- | **Table Row** | `display: table-row` | `table-row` | `{ display: 'table-row' }` | `{ tableRow: true }` |
2173
- | **Flow Root** | `display: flow-root` | `flow-root` | `{ display: 'flow-root' }` | `{ flowRoot: true }` |
2174
- | **List Item** | `display: list-item` | `list-item` | `{ display: 'list-item' }` | `{ listItem: true }` |
2175
- | **Hidden** | `display: none` | `hidden` | `{ display: 'none' }` | `{ hidden: true }` |
2176
- | **Screen Reader Only** | `position: absolute; ...` | `sr-only` | N/A | `{ srOnly: true }` |
2177
- | **Not Screen Reader** | `position: static; ...` | `not-sr-only` | N/A | `{ notSrOnly: true }` |
2198
+ | Concept | CSS Rule | Tailwind v4 Class | `sz` Prop |
2199
+ | :--------------------- | :---------------------------- | :------------------- | :---------------------------------- |
2200
+ | **Block** | `display: block` | `block` | `{ display: 'block' }` |
2201
+ | **Inline Block** | `display: inline-block` | `inline-block` | `{ display: 'inline-block' }` |
2202
+ | **Inline** | `display: inline` | `inline` | `{ display: 'inline' }` |
2203
+ | **Flex** | `display: flex` | `flex` | `{ display: 'flex' }` |
2204
+ | **Inline Flex** | `display: inline-flex` | `inline-flex` | `{ display: 'inline-flex' }` |
2205
+ | **Grid** | `display: grid` | `grid` | `{ display: 'grid' }` |
2206
+ | **Inline Grid** | `display: inline-grid` | `inline-grid` | `{ display: 'inline-grid' }` |
2207
+ | **Contents** | `display: contents` | `contents` | `{ display: 'contents' }` |
2208
+ | **Table** | `display: table` | `table` | `{ display: 'table' }` |
2209
+ | **Inline Table** | `display: inline-table` | `inline-table` | `{ display: 'inline-table' }` |
2210
+ | **Table Caption** | `display: table-caption` | `table-caption` | `{ display: 'table-caption' }` |
2211
+ | **Table Cell** | `display: table-cell` | `table-cell` | `{ display: 'table-cell' }` |
2212
+ | **Table Column** | `display: table-column` | `table-column` | `{ display: 'table-column' }` |
2213
+ | **Table Column Group** | `display: table-column-group` | `table-column-group` | `{ display: 'table-column-group' }` |
2214
+ | **Table Footer Group** | `display: table-footer-group` | `table-footer-group` | `{ display: 'table-footer-group' }` |
2215
+ | **Table Header Group** | `display: table-header-group` | `table-header-group` | `{ display: 'table-header-group' }` |
2216
+ | **Table Row Group** | `display: table-row-group` | `table-row-group` | `{ display: 'table-row-group' }` |
2217
+ | **Table Row** | `display: table-row` | `table-row` | `{ display: 'table-row' }` |
2218
+ | **Flow Root** | `display: flow-root` | `flow-root` | `{ display: 'flow-root' }` |
2219
+ | **List Item** | `display: list-item` | `list-item` | `{ display: 'list-item' }` |
2220
+ | **Hidden** | `display: none` | `hidden` | `{ display: 'none' }` |
2221
+ | **Screen Reader Only** | `position: absolute; ...` | `sr-only` | `{ srOnly: true }` |
2222
+ | **Not Screen Reader** | `position: static; ...` | `not-sr-only` | `{ notSrOnly: true }` |
2178
2223
 
2179
2224
  ## Floats
2180
2225
 
@@ -2205,10 +2250,10 @@ Controlling flow relative to floats.
2205
2250
 
2206
2251
  Controlling stacking contexts.
2207
2252
 
2208
- | Concept | CSS Rule | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
2209
- | :---------- | :------------------- | :---------------- | :------------------------- | :------------------------------ |
2210
- | **Isolate** | `isolation: isolate` | `isolate` | `{ isolation: 'isolate' }` | **Sugar**: `{ isolate: true }`. |
2211
- | **Auto** | `isolation: auto` | `isolation-auto` | `{ isolation: 'auto' }` | |
2253
+ | Concept | CSS Rule | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
2254
+ | :---------- | :------------------- | :---------------- | :------------------------- | :--- |
2255
+ | **Isolate** | `isolation: isolate` | `isolate` | `{ isolation: 'isolate' }` | |
2256
+ | **Auto** | `isolation: auto` | `isolation-auto` | `{ isolation: 'auto' }` | |
2212
2257
 
2213
2258
  ## Object Fit
2214
2259
 
@@ -2282,13 +2327,13 @@ Controlling scroll chaining.
2282
2327
 
2283
2328
  Controlling positioning.
2284
2329
 
2285
- | Concept | CSS Rule | Tailwind v4 Class | `sz` Prop (Canonical) | `sz` Prop (Boolean Sugar) |
2286
- | :----------- | :------------------- | :---------------- | :------------------------- | :------------------------ |
2287
- | **Static** | `position: static` | `static` | `{ position: 'static' }` | `{ static: true }` |
2288
- | **Fixed** | `position: fixed` | `fixed` | `{ position: 'fixed' }` | `{ fixed: true }` |
2289
- | **Absolute** | `position: absolute` | `absolute` | `{ position: 'absolute' }` | `{ absolute: true }` |
2290
- | **Relative** | `position: relative` | `relative` | `{ position: 'relative' }` | `{ relative: true }` |
2291
- | **Sticky** | `position: sticky` | `sticky` | `{ position: 'sticky' }` | `{ sticky: true }` |
2330
+ | Concept | CSS Rule | Tailwind v4 Class | `sz` Prop |
2331
+ | :----------- | :------------------- | :---------------- | :------------------------- |
2332
+ | **Static** | `position: static` | `static` | `{ position: 'static' }` |
2333
+ | **Fixed** | `position: fixed` | `fixed` | `{ position: 'fixed' }` |
2334
+ | **Absolute** | `position: absolute` | `absolute` | `{ position: 'absolute' }` |
2335
+ | **Relative** | `position: relative` | `relative` | `{ position: 'relative' }` |
2336
+ | **Sticky** | `position: sticky` | `sticky` | `{ position: 'sticky' }` |
2292
2337
 
2293
2338
  ## Top / Right / Bottom / Left (Placement)
2294
2339
 
@@ -2343,11 +2388,11 @@ Positioning mapped elements.
2343
2388
 
2344
2389
  Controlling visibility without layout change.
2345
2390
 
2346
- | Concept | CSS Rule | Tailwind v4 Class | `sz` Prop (Canonical) | `sz` Prop (Boolean) |
2347
- | :------------ | :--------------------- | :---------------- | :--------------------------- | :-------------------- |
2348
- | **Visible** | `visibility: visible` | `visible` | `{ visibility: 'visible' }` | `{ visible: true }` |
2349
- | **Invisible** | `visibility: hidden` | `invisible` | `{ visibility: 'hidden' }` | `{ invisible: true }` |
2350
- | **Collapse** | `visibility: collapse` | `collapse` | `{ visibility: 'collapse' }` | `{ collapse: true }` |
2391
+ | Concept | CSS Rule | Tailwind v4 Class | `sz` Prop |
2392
+ | :------------ | :--------------------- | :---------------- | :--------------------------- |
2393
+ | **Visible** | `visibility: visible` | `visible` | `{ visibility: 'visible' }` |
2394
+ | **Invisible** | `visibility: hidden` | `invisible` | `{ visibility: 'hidden' }` |
2395
+ | **Collapse** | `visibility: collapse` | `collapse` | `{ visibility: 'collapse' }` |
2351
2396
 
2352
2397
  ## Z-Index
2353
2398
 
@@ -3099,7 +3144,7 @@ Controlling the font size.
3099
3144
  | Concept | CSS Rule | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
3100
3145
  | :--------------- | :------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------ | :-------------------------------------- | :---------------------------------------------------------------- |
3101
3146
  | **Scale** | `font-size: (size); line-height: (leading)` | `text-xs`, `text-sm`, `text-base`, `text-lg`, `text-xl`, `text-2xl`, `text-3xl`, `text-4xl`, `text-5xl`, `text-6xl`, `text-7xl`, `text-8xl`, `text-9xl` | `{ text: 'xs' }`, `{ text: 'sm' }` etc. | Sets size & leading. |
3102
- | **Number** | `font-size: 16px` | `text-[16px]` | `{ text: '16px' }` | CamelCase `fontSize` also valid. |
3147
+ | **Number** | `font-size: 16px` | `text-[16px]` | `{ text: '16px' }` | `text` is the single canonical key. |
3103
3148
  | **Arbitrary** | `font-size: 1.5rem` | `text-[1.5rem]` | `{ text: '1.5rem' }` | |
3104
3149
  | **CSS Variable** | `font-size: var(--size)` | `text-(length:--size)` | `{ text: '--size' }` | **Sugar**: Auto-detects `--`. Type hint disambiguates from color. |
3105
3150
 
@@ -3109,11 +3154,11 @@ Controlling the font weight.
3109
3154
 
3110
3155
  | Concept | CSS Rule | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
3111
3156
  | :--------------- | :---------------------- | :--------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------ | :----------------------------------------------------------------- |
3112
- | **Keywords** | `font-weight: 100-900` | `font-thin`, `font-extralight`, `font-light`, `font-normal`, `font-medium`, `font-semibold`, `font-bold`, `font-extrabold`, `font-black` | `{ fontWeight: 'thin' }`, `{ fontWeight: 'extralight' }` etc. | |
3113
- | **Number** | `font-weight: 100-900` | `font-100`, `font-200`, `font-300`, `font-400`, `font-500`, `font-600`, `font-700`, `font-800`, `font-900` | `{ fontWeight: 100 }`, `{ fontWeight: 200 }` etc. | v4 shorthand. |
3114
- | **Alias** | (Sugar) | `font-bold` | `{ weight: 'bold' }` | Sugar for `fontWeight`. |
3115
- | **Arbitrary** | `font-weight: 550` | `font-[550]` | `{ fontWeight: 550 }` | |
3116
- | **CSS Variable** | `font-weight: var(--w)` | `font-(weight:--w)` | `{ fontWeight: '--w' }` | **Sugar**: Auto-detects `--`. Type hint disambiguates from family. |
3157
+ | **Keywords** | `font-weight: 100-900` | `font-thin`, `font-extralight`, `font-light`, `font-normal`, `font-medium`, `font-semibold`, `font-bold`, `font-extrabold`, `font-black` | `{ weight: 'thin' }`, `{ weight: 'extralight' }` etc. | |
3158
+ | **Number** | `font-weight: 100-900` | `font-100`, `font-200`, `font-300`, `font-400`, `font-500`, `font-600`, `font-700`, `font-800`, `font-900` | `{ weight: 100 }`, `{ weight: 200 }` etc. | v4 shorthand. |
3159
+ | **Alias** | (Sugar) | `font-bold` | `{ weight: 'bold' }` | Sugar for `weight`. |
3160
+ | **Arbitrary** | `font-weight: 550` | `font-[550]` | `{ weight: 550 }` | |
3161
+ | **CSS Variable** | `font-weight: var(--w)` | `font-(weight:--w)` | `{ weight: '--w' }` | **Sugar**: Auto-detects `--`. Type hint disambiguates from family. |
3117
3162
 
3118
3163
  ## Font Stretch
3119
3164
 
@@ -3152,12 +3197,12 @@ Controlling font-feature-settings. Added in Tailwind v4.2.
3152
3197
 
3153
3198
  Controlling the font style and smoothing.
3154
3199
 
3155
- | Concept | CSS Rule | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
3156
- | :-------------- | :------------------------------------ | :--------------------- | :------------------------------ | :----------------------------------------------------- |
3157
- | **Italic** | `font-style: italic` | `italic` | `{ italic: true }` | |
3158
- | **Not Italic** | `font-style: normal` | `not-italic` | `{ notItalic: true }` | `false` = NOOP; use `notItalic: true` to reset. |
3159
- | **Antialiased** | `-webkit-font-smoothing: antialiased` | `antialiased` | `{ antialiased: true }` | |
3160
- | **Subpixel** | `-webkit-font-smoothing: auto` | `subpixel-antialiased` | `{ subpixelAntialiased: true }` | `false` = NOOP; use `subpixelAntialiased` to override. |
3200
+ | Concept | CSS Rule | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
3201
+ | :-------------- | :------------------------------------ | :--------------------- | :------------------------------- | :--------------------------- |
3202
+ | **Italic** | `font-style: italic` | `italic` | `{ fontStyle: 'italic' }` | |
3203
+ | **Not Italic** | `font-style: normal` | `not-italic` | `{ fontStyle: 'normal' }` | Resets to upright. |
3204
+ | **Antialiased** | `-webkit-font-smoothing: antialiased` | `antialiased` | `{ fontSmoothing: 'grayscale' }` | Grayscale antialiasing. |
3205
+ | **Subpixel** | `-webkit-font-smoothing: auto` | `subpixel-antialiased` | `{ fontSmoothing: 'subpixel' }` | Subpixel (RGB) antialiasing. |
3161
3206
 
3162
3207
  ## Letter Spacing (Tracking)
3163
3208
 
@@ -3222,28 +3267,23 @@ Controlling the text color.
3222
3267
 
3223
3268
  Controlling the decoration of text.
3224
3269
 
3225
- | Concept | CSS Rule | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
3226
- | :----------------- | :------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------ | :---------------------------------- | :------------------------------------------------------------ |
3227
- | **Line (string)** | `text-decoration-line: underline` | `underline`, `overline`, `line-through`, `no-underline` | `{ decoration: 'underline' }` etc. | String key. `'none'` → `no-underline` (resets ALL decoration) |
3228
- | **Style** | `text-decoration-style: solid` | `decoration-solid`, `decoration-dashed`, `decoration-dotted`, `decoration-double`, `decoration-wavy` | `{ decorationStyle: 'solid' }` etc. | |
3229
- | **Thickness** | `text-decoration-thickness: auto` | `decoration-0`, `decoration-1`, `decoration-2`, `decoration-4`, `decoration-8`, `decoration-auto`, `decoration-from-font` | `{ decorationThickness: 1 }` etc. | |
3230
- | **Offset** | `text-underline-offset: auto` | `underline-offset-0`, `underline-offset-1`, `underline-offset-2`, `underline-offset-4`, `underline-offset-8`, `underline-offset-auto` | `{ underlineOffset: 1 }` etc. | |
3231
- | **Color** | `text-decoration-color: currentColor` | `decoration-blue-500` | `{ decorationColor: 'blue-500' }` | |
3232
- | **Arbitrary** | `text-decoration: (value)` | `decoration-[3px]` | `{ decorationThickness: '3px' }` | |
3233
- | **Var** | `text-decoration: (value)` | `decoration-(--v)` | `{ decorationThickness: '--v' }` | **Sugar**: Auto-detects `--`. |
3234
- | **Bool underline** | `text-decoration-line: underline` | `underline` | `{ underline: true }` | **Sugar**. `false` = NOOP. |
3235
- | **Bool overline** | `text-decoration-line: overline` | `overline` | `{ overline: true }` | **Sugar**. `false` = NOOP. |
3236
- | **Bool through** | `text-decoration-line: line-through` | `line-through` | `{ lineThrough: true }` | **Sugar**. `false` = NOOP. |
3237
- | **Bool reset** | `text-decoration-line: none` | `no-underline` | `{ noUnderline: true }` | **Sugar**. Resets ALL decoration. Use for responsive reset. |
3270
+ | Concept | CSS Rule | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
3271
+ | :---------------- | :--------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------ | :---------------------------------- | :------------------------------------------------------------ |
3272
+ | **Line (string)** | `text-decoration-line: underline` | `underline`, `overline`, `line-through`, `no-underline` | `{ decoration: 'underline' }` etc. | String key. `'none'` → `no-underline` (resets ALL decoration) |
3273
+ | **Style** | `text-decoration-style: solid` | `decoration-solid`, `decoration-dashed`, `decoration-dotted`, `decoration-double`, `decoration-wavy` | `{ decorationStyle: 'solid' }` etc. | |
3274
+ | **Thickness** | `text-decoration-thickness: auto` | `decoration-0`, `decoration-1`, `decoration-2`, `decoration-4`, `decoration-8`, `decoration-auto`, `decoration-from-font` | `{ decorationThickness: 1 }` etc. | |
3275
+ | **Offset** | `text-underline-offset: auto` | `underline-offset-0`, `underline-offset-1`, `underline-offset-2`, `underline-offset-4`, `underline-offset-8`, `underline-offset-auto` | `{ underlineOffset: 1 }` etc. | |
3276
+ | **Color** | `text-decoration-color: var(--color-blue-500)` | `decoration-blue-500` | `{ decorationColor: 'blue-500' }` | |
3277
+ | **Arbitrary** | `text-decoration: (value)` | `decoration-[3px]` | `{ decorationThickness: '3px' }` | |
3278
+ | **Var** | `text-decoration: (value)` | `decoration-(--v)` | `{ decorationThickness: '--v' }` | **Sugar**: Auto-detects `--`. |
3238
3279
 
3239
3280
  ## Text Transform
3240
3281
 
3241
3282
  Controlling the capitalization of text.
3242
3283
 
3243
- | Concept | CSS Rule | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
3244
- | :------------------ | :-------------------------- | :---------------------------------------------------- | :------------------------------------ | :------------------------------------- |
3245
- | **Transform** | `text-transform: uppercase` | `uppercase`, `lowercase`, `capitalize`, `normal-case` | `{ textTransform: 'uppercase' }` etc. | |
3246
- | **Boolean (Sugar)** | `text-transform: uppercase` | `uppercase` | `{ uppercase: true }` | **Sugar**. Overwrites `textTransform`. |
3284
+ | Concept | CSS Rule | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
3285
+ | :------------ | :-------------------------- | :---------------------------------------------------- | :------------------------------------------------------------------------------ | :--------------------- |
3286
+ | **Transform** | `text-transform: uppercase` | `uppercase`, `lowercase`, `capitalize`, `normal-case` | `{ textTransform: 'uppercase' }` (also `'lowercase'`, `'capitalize'`, `'none'`) | `'none'` → normal-case |
3247
3287
 
3248
3288
  ## Text Overflow & Whitespace
3249
3289
 
@@ -3313,7 +3353,7 @@ Static items are pre-computed at build time; conditional items use `_szMerge` at
3313
3353
  ```tsx
3314
3354
  <div
3315
3355
  sz={[
3316
- { flex: true, items: "center", p: 4 }, // always — extracted at build time
3356
+ { display: "flex", items: "center", p: 4 }, // always — extracted at build time
3317
3357
  isActive && { bg: "blue-500" }, // runtime conditional
3318
3358
  isDisabled && { opacity: 50, cursor: "not-allowed" },
3319
3359
  ]}
@@ -3355,10 +3395,10 @@ import { szv } from "csszyx";
3355
3395
 
3356
3396
  const buttonSz = szv({
3357
3397
  base: {
3358
- inlineFlex: true,
3398
+ display: "inline-flex",
3359
3399
  items: "center",
3360
3400
  rounded: "md",
3361
- fontWeight: "medium",
3401
+ weight: "medium",
3362
3402
  },
3363
3403
  variants: {
3364
3404
  variant: {
@@ -3443,20 +3483,21 @@ SSR-safe: on the server, returns class names without CSSOM access.
3443
3483
  For dynamic classes at runtime (the only runtime overhead):
3444
3484
 
3445
3485
  ```tsx
3446
- import { _sz, _szIf, _szSwitch, _szMerge } from '@csszyx/runtime';
3486
+ import { _sz, _szMerge } from '@csszyx/runtime';
3447
3487
 
3448
3488
  // Concatenate class strings
3449
3489
  <div className={_sz('base-class', conditionalClass)} />
3450
3490
 
3451
- // Conditional class
3452
- <div className={_szIf(isActive, 'active-class', 'inactive-class')} />
3491
+ // Conditional class — plain JS conditionals compose with _sz
3492
+ <div className={_sz('base-class', isActive && 'active-class')} />
3493
+ <div className={isActive ? 'active-class' : 'inactive-class'} />
3453
3494
 
3454
- // Switch/enum
3455
- <div className={_szSwitch(status, {
3495
+ // Switch/enum — a plain object lookup
3496
+ <div className={{
3456
3497
  loading: 'opacity-50',
3457
3498
  error: 'border-red-500',
3458
3499
  success: 'border-green-500',
3459
- })} />
3500
+ }[status]} />
3460
3501
 
3461
3502
  // Merge (last wins for conflicts)
3462
3503
  <div className={_szMerge(baseClasses, overrideClasses)} />
@@ -3471,6 +3512,69 @@ import { __szColorVar } from "csszyx/lite";
3471
3512
  // __szColorVar('#ff0000') → '#ff0000'
3472
3513
  ```
3473
3514
 
3515
+ ## Nested Element Routing — splitBox + class toolkit
3516
+
3517
+ When a caller passes one flat `className` to a component that renders nested elements, `splitBox` partitions it at the CSS box-model border line so each element gets the classes that act on it. Pure string functions, framework-agnostic. The class-token → box-role map is generated from the compiler's property tables (never drifts).
3518
+
3519
+ ```tsx
3520
+ import {
3521
+ splitBox,
3522
+ splitBoxSz,
3523
+ classify,
3524
+ has,
3525
+ pick,
3526
+ omit,
3527
+ stripSzProps,
3528
+ } from "@csszyx/runtime";
3529
+
3530
+ // Partition: outer = border-outward (margin/position/border/sizing/bg/shadow/transform/visibility),
3531
+ // inner = border-inward (padding/overflow/display/layout/gap/text/paint-inside/interactivity)
3532
+ const { outer, inner } = splitBox("m-4 px-2 md:flex");
3533
+ // outer: "m-4" inner: "px-2 md:flex"
3534
+
3535
+ // Override any default per call (BoxSelector = box-role | category | class-prefix | { category: value })
3536
+ splitBox("overflow-hidden p-4", { outer: ["overflow"] });
3537
+ // outer: "overflow-hidden" inner: "p-4"
3538
+
3539
+ // Toolkit: csszyx owns the truth (box-role/category), your project owns the rule
3540
+ classify("inset-ring-2"); // { role: 'inner', category: 'ring' }
3541
+ has("p-2 overflow-y-auto", "overflow"); // true
3542
+ pick("m-4 text-sm", "text"); // "text-sm"
3543
+ omit("p-2 overflow-y-auto flex", "overflow"); // "p-2 flex"
3544
+
3545
+ // A scroller scrolls only when the outer frame clips (project-owned dependency rule)
3546
+ const dep = has(outer, { overflow: "hidden" }) ? "overflow-y-auto h-full" : "";
3547
+
3548
+ // sz-OBJECT routing: splitBoxSz partitions an sz OBJECT (not a className) into
3549
+ // { outer, inner } sz objects — keeps a szv-based component sz-native and routes
3550
+ // each key to the same side its class would (splitBoxSz(x) ≡ splitBox(compile(x))).
3551
+ splitBoxSz({ m: 4, px: 2 }); // { outer: { m: 4 }, inner: { px: 2 } }
3552
+ splitBoxSz({ gap: 2, hover: { px: 1 }, md: { m: 4 } }); // variants route by inner prop; split when they disagree
3553
+ splitBoxSz({ grow: 2, self: "center" }, { outer: ["grow", "self"] }); // force flex-item utils to the frame
3554
+ // classifySzKey(key) / hasSz / pickSz / omitSz — the sz-object toolkit twins
3555
+
3556
+ // stripSzProps: drop sz before forwarding ...rest to the DOM (guards uncompiled files
3557
+ // whose raw sz would leak as sz="[object Object]"; dev-warns once on a raw-object sz)
3558
+ function Box({ sz, ...rest }) {
3559
+ return <div {...stripSzProps(rest)} />;
3560
+ }
3561
+ ```
3562
+
3563
+ ## Security — untrusted sz
3564
+
3565
+ csszyx is safe-by-default for AUTHORED sz (compiled to class strings at build time). When sz comes from UNTRUSTED input (JSON-driven UI, CMS, user data), it controls keys and values that reach the runtime CSS pipeline — purify it.
3566
+
3567
+ ```tsx
3568
+ import { dynamic, purifySz } from "@csszyx/dynamic";
3569
+ // stripSzProps is exported from '@csszyx/runtime'
3570
+
3571
+ // Untrusted sz → dynamic() runtime CSS: purify first (allowlist + value sanitize + proto guard)
3572
+ const className = dynamic(purifySz(untrustedSzFromJson));
3573
+ // strict (default) also strips url()/image-set()/@import/expression(); { strict:false } for trusted input
3574
+ ```
3575
+
3576
+ Guarantees on the runtime path: CSS is injected via atomic `insertRule` (a `}`/`</style>` rule-breakout throws, no 2nd rule/markup); declaration value + arbitrary property name are validated before injection; recursion depth is capped (`SzDepthError`). `dynamic()`/`_sz`/`_szMerge` return React-escaped-only strings — safe in `className={}`, NEVER interpolate into raw HTML. `stripSzProps(props)` keeps a raw `sz` object off the DOM. SSR mangle map is schema-validated (`isValidMangleMap`); `verifyMangleChecksumAsync` gives real integrity verification without the WASM core (tamper-detection, not authentication). The constructable-`adoptedStyleSheets` injection path is CSP-clean (no inline style text). Posture: treat all sz/DOM/config as untrusted by default.
3577
+
3474
3578
  ## SSR Hydration
3475
3579
 
3476
3580
  CSSzyx validates that server and client use the same mangle map:
@@ -3524,6 +3628,11 @@ In production, class names are mangled for maximum compression:
3524
3628
 
3525
3629
  Output: `<div class="z y x" />` — the CSS `.z { padding: 1rem }` etc. is injected automatically.
3526
3630
 
3631
+ Set `production: { mangle: false }` to keep readable class names — the supported way to
3632
+ inspect the emitted CSS, and recommended for hybrid apps that also ship non-csszyx
3633
+ stylesheets keyed on the original class names. To check what a single `sz` object
3634
+ compiles to without a build, run `csszyx explain "{ p: 4, bg: 'blue-500' }"`.
3635
+
3527
3636
  ### AST budget guard
3528
3637
 
3529
3638
  Files larger than 50 000 AST nodes throw `ASTBudgetExceededError` at
@@ -3541,6 +3650,19 @@ Or exclude the file from csszyx processing entirely before parsing:
3541
3650
  ...csszyx({ exclude: ['src/generated/**', /icon-dump\.tsx$/] })
3542
3651
  ```
3543
3652
 
3653
+ ## Monorepo Workspace Packages (compilePackages)
3654
+
3655
+ `/packages/` is hard-ignored by default (a published library ships pre-extracted
3656
+ CSS). To compile `sz` in a monorepo workspace package you author, opt it in by
3657
+ directory name — only `/packages/` is relaxed, `node_modules`/`.next` stay ignored:
3658
+
3659
+ ```js
3660
+ ...csszyx({ compilePackages: ['vui'] }) // compiles sz in /packages/vui/**
3661
+ ```
3662
+
3663
+ A `/packages/` file with `sz` that is not opted in is skipped silently (no CSS);
3664
+ csszyx warns at build end and lists those files.
3665
+
3544
3666
  ## Theme Auto-Scan (Custom Tokens → TypeScript Types)
3545
3667
 
3546
3668
  When using Tailwind v4 `@theme` blocks for custom design tokens, enable `build.scanCss`
@@ -3631,12 +3753,13 @@ of classes the developer has explicitly flagged as "not yet decided."
3631
3753
  | `inline-flex` | `{ display: 'inline-flex' }` |
3632
3754
  | `hidden` | `{ display: 'none' }` |
3633
3755
 
3634
- Manual authoring can still use `{ flex: true }`, `{ inlineFlex: true }`, etc.
3756
+ Boolean sugar like `{ flex: true }` was removed — use `{ display: 'flex' }`.
3635
3757
  The migrate command uses `display` so duplicate display utilities are visible as
3636
3758
  one semantic CSS property. Conflicting display utilities in the same variant
3637
3759
  scope, such as `block flex` or `md:block md:flex`, are left unresolved instead
3638
3760
  of guessed. `flex flex-1` is safe and migrates to
3639
- `{ display: 'flex', flex: '1' }`.
3761
+ `{ display: 'flex', flex: '1' }` (the `flex` shorthand stays — only the
3762
+ `flex: true` display alias was removed).
3640
3763
 
3641
3764
  ### `--resolve-todos` — apply the resolution map
3642
3765
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@csszyx/mcp-server",
3
- "version": "0.9.10",
3
+ "version": "0.10.1",
4
4
  "description": "Model Context Protocol (MCP) server for csszyx — enables AI agents to understand and generate sz props",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -30,9 +30,9 @@
30
30
  "dependencies": {
31
31
  "@modelcontextprotocol/sdk": "^1.29.0",
32
32
  "zod": "^3.23.8",
33
- "@csszyx/unplugin": "0.9.10",
34
- "@csszyx/cli": "0.9.10",
35
- "@csszyx/compiler": "0.9.10"
33
+ "@csszyx/compiler": "0.10.1",
34
+ "@csszyx/cli": "0.10.1",
35
+ "@csszyx/unplugin": "0.10.1"
36
36
  },
37
37
  "devDependencies": {
38
38
  "typescript": "^6.0.3",