@csszyx/mcp-server 0.9.9 → 0.10.0

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,29 @@ 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
+ { bgColor: 'red-500' } // ❌ tsc error — unknown key; canonical is { bg: 'red-500' }
252
+
253
+ // Forcing a brand-new Tailwind utility csszyx has no key for yet — opt out explicitly:
254
+ // @ts-expect-error - forward-compat utility not yet in csszyx
255
+ { someNewUtility: 'x' } // ✅ allowed; the runtime still emits the class
256
+ ```
257
+
258
+ Arbitrary variants are allowed by pattern: `{ '@container': {…} }`, `{ 'min-[320px]': {…} }`,
259
+ `{ '[&>span]': {…} }`. **Custom breakpoints are typed from your CSS** — define one once in
260
+ Tailwind `@theme` and csszyx auto-generates the type:
261
+
262
+ ```css
263
+ @theme { --breakpoint-tablet: 40rem; } /* → { tablet: { p: 6 } } now type-checks */
225
264
  ```
226
265
 
227
266
  ## Full Snippets Reference
@@ -241,7 +280,7 @@ Simple property-value mappings.
241
280
  | **Text Color** | `color: white` | `text-white` | `{ color: 'white' }` |
242
281
  | **Border Radius** | `border-radius: var(--radius-lg)` | `rounded-lg` | `{ rounded: 'lg' }` |
243
282
  | **Box Shadow** | `box-shadow: var(--shadow-xl)` | `shadow-xl` | `{ shadow: 'xl' }` |
244
- | **Flex Container** | `display: flex` | `flex` | `{ flex: true }` |
283
+ | **Flex Container** | `display: flex` | `flex` | `{ display: 'flex' }` |
245
284
 
246
285
  ## State Modifiers (Hover, Focus)
247
286
 
@@ -258,11 +297,11 @@ Handling pseudo-classes using nested objects or string prefixes.
258
297
 
259
298
  Handling media queries via properties.
260
299
 
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 } }` |
300
+ | Concept | CSS Rule | Tailwind v4 Class | `sz` Prop (Object Syntax) |
301
+ | :------------------- | :---------------------------------- | :---------------- | :---------------------------- |
302
+ | **Small Breakpoint** | `@media (width >= 40rem) { (etc) }` | `sm:grid-cols-3` | `{ sm: { gridCols: 3 } }` |
303
+ | **Medium Layout** | `@media (width >= 48rem) { (etc) }` | `md:flex` | `{ md: { display: 'flex' } }` |
304
+ | **Large Spacing** | `@media (width >= 64rem) { (etc) }` | `lg:px-8` | `{ lg: { px: 8 } }` |
266
305
 
267
306
  ## Dark Mode
268
307
 
@@ -341,9 +380,9 @@ Standard interactive states.
341
380
  | **Hover, Focus, Active** | `hover:bg-red focus:bg-blue active:bg-green` | `{ hover: { bg: 'red' }, focus: { bg: 'blue' }, active: { bg: 'green' } }` | |
342
381
  | **First/Last Child** | `first:pt-0 last:pb-0` | `{ first: { pt: 0 }, last: { pb: 0 } }` | |
343
382
  | **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 } }` | |
383
+ | **First of Type** | `first-of-type:block` | `{ firstOfType: { display: 'block' } }` | **Sugar**: CamelCase alias. |
384
+ | **Only Child** | `only:block` | `{ only: { display: 'block' } }` | |
385
+ | **Empty** | `empty:hidden` | `{ empty: { display: 'none' } }` | |
347
386
  | **Visited** | `visited:text-purple` | `{ visited: { color: 'purple' } }` | |
348
387
  | **Focus Within** | `focus-within:ring` | `{ focusWithin: { ring: true } }` | **Sugar**: CamelCase alias. |
349
388
  | **Focus Visible** | `focus-visible:ring` | `{ focusVisible: { ring: true } }` | **Sugar**: CamelCase alias. |
@@ -353,11 +392,11 @@ Standard interactive states.
353
392
 
354
393
  Styling based on descendants.
355
394
 
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 } } }` | |
395
+ | Concept | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
396
+ | :----------------- | :-------------------------- | :--------------------------------------------------- | :------------------------------------ |
397
+ | **Has Descendant** | `has-[img]:bg-blue` | `{ has: { img: { bg: 'blue' } } }` | **Sugar**: Nested selector. |
398
+ | **Has State** | `has-[:checked]:bg-blue` | `{ has: { checked: { bg: 'blue' } } }` | **Sugar**: Auto-detects pseudo-class. |
399
+ | **Arbitrary Has** | `has-[.custom-class]:block` | `{ has: { '.custom-class': { display: 'block' } } }` | |
361
400
 
362
401
  ## Styling based on parent state (Groups)
363
402
 
@@ -369,76 +408,76 @@ Styling children based on parent `group` class.
369
408
  | **Group Focus** | `group-focus:text-white` | `{ group: { focus: { color: 'white' } } }` | |
370
409
  | **Group Active** | `group-active:text-white` | `{ group: { active: { color: 'white' } } }` | |
371
410
  | **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 } } } }` | |
411
+ | **Arbitrary Groups** | `group-[.is-published]:block` | `{ group: { '.is-published': { display: 'block' } } }` | |
412
+ | **Group Has** | `group-has-[a]:block` | `{ group: { has: { a: { display: 'block' } } } }` | |
374
413
  | **Group Data** | `group-data-[active]:text-blue` | `{ group: { data: { active: { color: 'blue' } } } }` | **Sugar**: Nested `data` key. |
375
414
  | **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. |
415
+ | **Group Data (value match)** | `group-data-[state=open]:block` | `{ group: { data: { 'state=open': { display: 'block' } } } }` | `=` in key → bracket form always. |
416
+ | **Group ARIA** | `group-aria-expanded:block` | `{ group: { aria: { expanded: { display: 'block' } } } }` | Standard states: bare form. |
417
+ | **Group ARIA (arbitrary)** | `group-aria-[current=page]:font-bold` | `{ group: { aria: { 'current=page': { weight: 'bold' } } } }` | Non-standard: bracket form. |
379
418
 
380
419
  ## Styling based on sibling state (Peers)
381
420
 
382
421
  Styling based on previous sibling `peer` class.
383
422
 
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. |
423
+ | Concept | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
424
+ | :-------------------------- | :---------------------------------- | :----------------------------------------------------------- | :----------------------------------- |
425
+ | **Peer Hover** | `peer-hover:text-white` | `{ peer: { hover: { color: 'white' } } }` | **Sugar**: Nested scope. |
426
+ | **Peer Checked** | `peer-checked:bg-blue` | `{ peer: { checked: { bg: 'blue' } } }` | |
427
+ | **Differentiating Peers** | `peer-checked/name:bg-blue` | `{ peer: { name: { checked: { bg: 'blue' } } } }` | **Sugar**: Scope name as nested key. |
428
+ | **Arbitrary Peers** | `peer-[.is-dirty]:block` | `{ peer: { '.is-dirty': { display: 'block' } } }` | |
429
+ | **Peer Data** | `peer-data-[active]:text-blue` | `{ peer: { data: { active: { color: 'blue' } } } }` | **Sugar**: Nested `data` key. |
430
+ | **Peer Data (value match)** | `peer-data-[state=open]:block` | `{ peer: { data: { 'state=open': { display: 'block' } } } }` | `=` in key → bracket form always. |
431
+ | **Peer ARIA** | `peer-aria-checked:bg-blue` | `{ peer: { aria: { checked: { bg: 'blue' } } } }` | Standard states: bare form. |
432
+ | **Peer ARIA (arbitrary)** | `peer-aria-[invalid=true]:text-red` | `{ peer: { aria: { 'invalid=true': { color: 'red' } } } }` | Non-standard: bracket form. |
394
433
 
395
434
  ## :not()
396
435
 
397
436
  Inverse conditions.
398
437
 
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 } } } }` | |
438
+ | Concept | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
439
+ | :--------------- | :---------------------------------- | :---------------------------------------------------------------- | :--- |
440
+ | **Not Hover** | `not-hover:opacity-75` | `{ not: { hover: { opacity: 75 } } }` | |
441
+ | **Not First** | `not-first:mt-4` | `{ not: { first: { mt: 4 } } }` | |
442
+ | **Not Supports** | `not-supports-[display:grid]:block` | `{ not: { supports: { 'display:grid': { display: 'block' } } } }` | |
404
443
 
405
444
  ## Pseudo-elements
406
445
 
407
446
  Advanced content styling.
408
447
 
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 } }` | |
448
+ | Concept | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
449
+ | :--------------- | :---------------------- | :---------------------------------------------- | :-------------------- |
450
+ | **Before/After** | `before:content-['']` | `{ before: { (etc) } }` | Defaults used. |
451
+ | **Placeholder** | `placeholder:text-gray` | `{ placeholder: { color: 'gray' } }` | |
452
+ | **File** | `file:border` | `{ file: { border: true } }` | |
453
+ | **Marker** | `marker:text-blue` | `{ marker: { color: 'blue' } }` | |
454
+ | **Selection** | `selection:bg-pink` | `{ selection: { bg: 'pink' } }` | |
455
+ | **First Line** | `first-line:uppercase` | `{ firstLine: { textTransform: 'uppercase' } }` | **Sugar**: CamelCase. |
456
+ | **First Letter** | `first-letter:text-7xl` | `{ firstLetter: { text: '7xl' } }` | **Sugar**: CamelCase. |
457
+ | **Backdrop** | `backdrop:blur` | `{ backdrop: { blur: true } }` | |
419
458
 
420
459
  ## Media & Feature Queries
421
460
 
422
461
  Environment-based styling.
423
462
 
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 } }` | |
463
+ | Concept | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
464
+ | :-------------------- | :-------------------------------- | :------------------------------------------------------------ | :---------------------------- |
465
+ | **Breakpoints** | `md:block lg:flex` | `{ md: { display: 'block' }, lg: { display: 'flex' } }` | |
466
+ | **Container Queries** | `@md:block @lg:flex` | `{ '@md': { display: 'block' }, '@lg': { display: 'flex' } }` | **Note**: String key for `@`. |
467
+ | **Reduced Motion** | `motion-reduce:hidden` | `{ motionReduce: { display: 'none' } }` | **Sugar**: CamelCase. |
468
+ | **Prefers Contrast** | `contrast-more:border` | `{ contrastMore: { border: true } }` | |
469
+ | **Forced Colors** | `forced-colors:border-gray` | `{ forcedColors: { borderColor: 'gray' } }` | **Sugar**: CamelCase. |
470
+ | **Inverted Colors** | `inverted-colors:invert` | `{ invertedColors: { invert: true } }` | **Sugar**: CamelCase. |
471
+ | **Pointer** | `pointer-coarse:p-4` | `{ pointerCoarse: { p: 4 } }` | **Sugar**: CamelCase. |
472
+ | **Any-Pointer** | `any-pointer-fine:cursor-pointer` | `{ anyPointerFine: { cursor: 'pointer' } }` | v4.1. **Sugar**: CamelCase. |
473
+ | **User Valid** | `user-valid:border-green-500` | `{ userValid: { borderColor: 'green-500' } }` | v4.1. Form validation state. |
474
+ | **User Invalid** | `user-invalid:border-red-500` | `{ userInvalid: { borderColor: 'red-500' } }` | v4.1. Form validation state. |
475
+ | **Details Content** | `details-content:block` | `{ detailsContent: { display: 'block' } }` | v4.1. **Sugar**: CamelCase. |
476
+ | **Print** | `print:hidden` | `{ print: { display: 'none' } }` | |
477
+ | **Orientation** | `portrait:hidden` | `{ portrait: { display: 'none' } }` | |
478
+ | **Scripting** | `noscript:block` | `{ noscript: { display: 'block' } }` | |
479
+ | **Supports** | `supports-[display:grid]:grid` | `{ supports: { 'display:grid': { display: 'grid' } } }` | |
480
+ | **Starting Style** | `starting:opacity-0` | `{ starting: { opacity: 0 } }` | |
442
481
 
443
482
  ## Attribute Selectors
444
483
 
@@ -476,8 +515,8 @@ Targeting specific screen sizes and container states.
476
515
  | Concept | CSS Rule | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
477
516
  | :----------------------- | :-------------------------- | :------------------------ | :------------------------------------------------ | :---------------------------------- |
478
517
  | **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. |
518
+ | **Breakpoint Range** | `768px <= width < 1280px` | `md:max-xl:flex` | `{ md: { maxXl: { display: 'flex' } } }` | **Sugar**: CamelCase for `max-xl`. |
519
+ | **Single Breakpoint** | `md only` | `md:max-lg:flex` | `{ md: { maxLg: { display: 'flex' } } }` | Target specific range. |
481
520
  | **Custom Breakpoint** | `@media (min-width: 320px)` | `min-[320px]:text-center` | `{ min: { '[320px]': { textAlign: 'center' } } }` | Arbitrary one-off breakpoint. |
482
521
  | **Max-Width Breakpoint** | `@media (max-width: 600px)` | `max-[600px]:bg-sky-300` | `{ max: { '[600px]': { bg: 'sky-300' } } }` | |
483
522
 
@@ -497,15 +536,15 @@ The `container` class sets `width: 100%` and applies responsive max-widths.
497
536
 
498
537
  The `@container` class enables CSS container queries.
499
538
 
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' }` | |
539
+ | Concept | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
540
+ | :----------------------- | :------------------- | :----------------------------------------------- | :---------------------------------------- |
541
+ | **Mark Container** | `@container` | `{ '@container': true }` | |
542
+ | **Named Container** | `@container/sidebar` | `{ '@container': 'sidebar' }` | **Sugar**: Value string = container name. |
543
+ | **Container Breakpoint** | `@md:flex` | `{ '@md': { display: 'flex' } }` | **Note**: `@` prefix in key. |
544
+ | **Named Query** | `@md/sidebar:block` | `{ '@md': { sidebar: { display: 'block' } } }` | **Sugar**: Nest name inside query key. |
545
+ | **Container Range** | `@sm:@max-md:block` | `{ '@sm': { '@maxMd': { display: 'block' } } }` | |
546
+ | **Arbitrary Query** | `@min-[475px]:flex` | `{ '@min': { '[475px]': { display: 'flex' } } }` | |
547
+ | **Container Units** | `w-[50cqw]` | `{ w: '50cqw' }` | |
509
548
 
510
549
  ## Dark mode
511
550
 
@@ -836,14 +875,14 @@ Controlling the width of an element's borders.
836
875
 
837
876
  Controlling the color of an element's borders.
838
877
 
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' }` | |
878
+ | Concept | CSS Rule | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
879
+ | :-------------------- | :------------------------------------------------------------------------- | :------------------ | :---------------------------------------------- | :----------------------------------------- |
880
+ | **Color** | `border-color: var(--color-red-500)` | `border-red-500` | `{ borderColor: 'red-500' }` | |
881
+ | **Opacity** | `border-color: color-mix(in oklab, var(--color-red-500) 50%, transparent)` | `border-red-500/50` | `{ borderColor: { color: 'red-500', op: 50 } }` | |
882
+ | **CSS Var + Opacity** | `border-color: var(--c) / 50` | `border-(--c)/50` | `{ borderColor: { color: '--c', op: 50 } }` | CSS variables are auto-wrapped in `(...)`. |
883
+ | **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. |
884
+ | **Arbitrary** | `border-color: #50d71e` | `border-[#50d71e]` | `{ borderColor: '#50d71e' }` | |
885
+ | **Variable** | `border-color: var(--c)` | `border-(--c)` | `{ borderColor: '--c' }` | |
847
886
 
848
887
  ## Border Style
849
888
 
@@ -877,13 +916,13 @@ Utilities for controlling the border width between elements.
877
916
 
878
917
  Utilities for controlling the border color between elements.
879
918
 
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' }` | |
919
+ | Concept | CSS Rule | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
920
+ | :-------------------- | :------------------------------------------------------------------------- | :------------------ | :---------------------------------------------- | :----------------------------------------- |
921
+ | **Color** | `border-color: var(--color-red-500)` | `divide-red-500` | `{ divideColor: 'red-500' }` | Applied between children. |
922
+ | **Opacity** | `border-color: color-mix(in oklab, var(--color-red-500) 50%, transparent)` | `divide-red-500/50` | `{ divideColor: { color: 'red-500', op: 50 } }` | |
923
+ | **CSS Var + Opacity** | `border-color: var(--c) / 50` | `divide-(--c)/50` | `{ divideColor: { color: '--c', op: 50 } }` | CSS variables are auto-wrapped in `(...)`. |
924
+ | **Arbitrary** | `border-color: #50d71e` | `divide-[#50d71e]` | `{ divideColor: '#50d71e' }` | |
925
+ | **Variable** | `border-color: var(--c)` | `divide-(--c)` | `{ divideColor: '--c' }` | |
887
926
 
888
927
  ## Divide Style
889
928
 
@@ -911,11 +950,11 @@ Controlling the width of an element's outline.
911
950
 
912
951
  Controlling the color of an element's outline.
913
952
 
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' }` | |
953
+ | Concept | CSS Rule | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
954
+ | :------------ | :------------------------------------ | :------------------ | :---------------------------- | :--- |
955
+ | **Color** | `outline-color: var(--color-red-500)` | `outline-red-500` | `{ outlineColor: 'red-500' }` | |
956
+ | **Arbitrary** | `outline-color: #50d71e` | `outline-[#50d71e]` | `{ outlineColor: '#50d71e' }` | |
957
+ | **Variable** | `outline-color: var(--c)` | `outline-(--c)` | `{ outlineColor: '--c' }` | |
919
958
 
920
959
  ## Outline Style
921
960
 
@@ -2150,31 +2189,31 @@ Controlling box model sizing.
2150
2189
 
2151
2190
  Controlling display box type.
2152
2191
 
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 }` |
2192
+ | Concept | CSS Rule | Tailwind v4 Class | `sz` Prop |
2193
+ | :--------------------- | :---------------------------- | :------------------- | :---------------------------------- |
2194
+ | **Block** | `display: block` | `block` | `{ display: 'block' }` |
2195
+ | **Inline Block** | `display: inline-block` | `inline-block` | `{ display: 'inline-block' }` |
2196
+ | **Inline** | `display: inline` | `inline` | `{ display: 'inline' }` |
2197
+ | **Flex** | `display: flex` | `flex` | `{ display: 'flex' }` |
2198
+ | **Inline Flex** | `display: inline-flex` | `inline-flex` | `{ display: 'inline-flex' }` |
2199
+ | **Grid** | `display: grid` | `grid` | `{ display: 'grid' }` |
2200
+ | **Inline Grid** | `display: inline-grid` | `inline-grid` | `{ display: 'inline-grid' }` |
2201
+ | **Contents** | `display: contents` | `contents` | `{ display: 'contents' }` |
2202
+ | **Table** | `display: table` | `table` | `{ display: 'table' }` |
2203
+ | **Inline Table** | `display: inline-table` | `inline-table` | `{ display: 'inline-table' }` |
2204
+ | **Table Caption** | `display: table-caption` | `table-caption` | `{ display: 'table-caption' }` |
2205
+ | **Table Cell** | `display: table-cell` | `table-cell` | `{ display: 'table-cell' }` |
2206
+ | **Table Column** | `display: table-column` | `table-column` | `{ display: 'table-column' }` |
2207
+ | **Table Column Group** | `display: table-column-group` | `table-column-group` | `{ display: 'table-column-group' }` |
2208
+ | **Table Footer Group** | `display: table-footer-group` | `table-footer-group` | `{ display: 'table-footer-group' }` |
2209
+ | **Table Header Group** | `display: table-header-group` | `table-header-group` | `{ display: 'table-header-group' }` |
2210
+ | **Table Row Group** | `display: table-row-group` | `table-row-group` | `{ display: 'table-row-group' }` |
2211
+ | **Table Row** | `display: table-row` | `table-row` | `{ display: 'table-row' }` |
2212
+ | **Flow Root** | `display: flow-root` | `flow-root` | `{ display: 'flow-root' }` |
2213
+ | **List Item** | `display: list-item` | `list-item` | `{ display: 'list-item' }` |
2214
+ | **Hidden** | `display: none` | `hidden` | `{ display: 'none' }` |
2215
+ | **Screen Reader Only** | `position: absolute; ...` | `sr-only` | `{ srOnly: true }` |
2216
+ | **Not Screen Reader** | `position: static; ...` | `not-sr-only` | `{ notSrOnly: true }` |
2178
2217
 
2179
2218
  ## Floats
2180
2219
 
@@ -2205,10 +2244,10 @@ Controlling flow relative to floats.
2205
2244
 
2206
2245
  Controlling stacking contexts.
2207
2246
 
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' }` | |
2247
+ | Concept | CSS Rule | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
2248
+ | :---------- | :------------------- | :---------------- | :------------------------- | :--- |
2249
+ | **Isolate** | `isolation: isolate` | `isolate` | `{ isolation: 'isolate' }` | |
2250
+ | **Auto** | `isolation: auto` | `isolation-auto` | `{ isolation: 'auto' }` | |
2212
2251
 
2213
2252
  ## Object Fit
2214
2253
 
@@ -2282,13 +2321,13 @@ Controlling scroll chaining.
2282
2321
 
2283
2322
  Controlling positioning.
2284
2323
 
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 }` |
2324
+ | Concept | CSS Rule | Tailwind v4 Class | `sz` Prop |
2325
+ | :----------- | :------------------- | :---------------- | :------------------------- |
2326
+ | **Static** | `position: static` | `static` | `{ position: 'static' }` |
2327
+ | **Fixed** | `position: fixed` | `fixed` | `{ position: 'fixed' }` |
2328
+ | **Absolute** | `position: absolute` | `absolute` | `{ position: 'absolute' }` |
2329
+ | **Relative** | `position: relative` | `relative` | `{ position: 'relative' }` |
2330
+ | **Sticky** | `position: sticky` | `sticky` | `{ position: 'sticky' }` |
2292
2331
 
2293
2332
  ## Top / Right / Bottom / Left (Placement)
2294
2333
 
@@ -2343,11 +2382,11 @@ Positioning mapped elements.
2343
2382
 
2344
2383
  Controlling visibility without layout change.
2345
2384
 
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 }` |
2385
+ | Concept | CSS Rule | Tailwind v4 Class | `sz` Prop |
2386
+ | :------------ | :--------------------- | :---------------- | :--------------------------- |
2387
+ | **Visible** | `visibility: visible` | `visible` | `{ visibility: 'visible' }` |
2388
+ | **Invisible** | `visibility: hidden` | `invisible` | `{ visibility: 'hidden' }` |
2389
+ | **Collapse** | `visibility: collapse` | `collapse` | `{ visibility: 'collapse' }` |
2351
2390
 
2352
2391
  ## Z-Index
2353
2392
 
@@ -3099,7 +3138,7 @@ Controlling the font size.
3099
3138
  | Concept | CSS Rule | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
3100
3139
  | :--------------- | :------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------ | :-------------------------------------- | :---------------------------------------------------------------- |
3101
3140
  | **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. |
3141
+ | **Number** | `font-size: 16px` | `text-[16px]` | `{ text: '16px' }` | `text` is the single canonical key. |
3103
3142
  | **Arbitrary** | `font-size: 1.5rem` | `text-[1.5rem]` | `{ text: '1.5rem' }` | |
3104
3143
  | **CSS Variable** | `font-size: var(--size)` | `text-(length:--size)` | `{ text: '--size' }` | **Sugar**: Auto-detects `--`. Type hint disambiguates from color. |
3105
3144
 
@@ -3109,11 +3148,11 @@ Controlling the font weight.
3109
3148
 
3110
3149
  | Concept | CSS Rule | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
3111
3150
  | :--------------- | :---------------------- | :--------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------ | :----------------------------------------------------------------- |
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. |
3151
+ | **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. | |
3152
+ | **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. |
3153
+ | **Alias** | (Sugar) | `font-bold` | `{ weight: 'bold' }` | Sugar for `weight`. |
3154
+ | **Arbitrary** | `font-weight: 550` | `font-[550]` | `{ weight: 550 }` | |
3155
+ | **CSS Variable** | `font-weight: var(--w)` | `font-(weight:--w)` | `{ weight: '--w' }` | **Sugar**: Auto-detects `--`. Type hint disambiguates from family. |
3117
3156
 
3118
3157
  ## Font Stretch
3119
3158
 
@@ -3152,12 +3191,12 @@ Controlling font-feature-settings. Added in Tailwind v4.2.
3152
3191
 
3153
3192
  Controlling the font style and smoothing.
3154
3193
 
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. |
3194
+ | Concept | CSS Rule | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
3195
+ | :-------------- | :------------------------------------ | :--------------------- | :------------------------------- | :--------------------------- |
3196
+ | **Italic** | `font-style: italic` | `italic` | `{ fontStyle: 'italic' }` | |
3197
+ | **Not Italic** | `font-style: normal` | `not-italic` | `{ fontStyle: 'normal' }` | Resets to upright. |
3198
+ | **Antialiased** | `-webkit-font-smoothing: antialiased` | `antialiased` | `{ fontSmoothing: 'grayscale' }` | Grayscale antialiasing. |
3199
+ | **Subpixel** | `-webkit-font-smoothing: auto` | `subpixel-antialiased` | `{ fontSmoothing: 'subpixel' }` | Subpixel (RGB) antialiasing. |
3161
3200
 
3162
3201
  ## Letter Spacing (Tracking)
3163
3202
 
@@ -3222,28 +3261,23 @@ Controlling the text color.
3222
3261
 
3223
3262
  Controlling the decoration of text.
3224
3263
 
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. |
3264
+ | Concept | CSS Rule | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
3265
+ | :---------------- | :--------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------ | :---------------------------------- | :------------------------------------------------------------ |
3266
+ | **Line (string)** | `text-decoration-line: underline` | `underline`, `overline`, `line-through`, `no-underline` | `{ decoration: 'underline' }` etc. | String key. `'none'` → `no-underline` (resets ALL decoration) |
3267
+ | **Style** | `text-decoration-style: solid` | `decoration-solid`, `decoration-dashed`, `decoration-dotted`, `decoration-double`, `decoration-wavy` | `{ decorationStyle: 'solid' }` etc. | |
3268
+ | **Thickness** | `text-decoration-thickness: auto` | `decoration-0`, `decoration-1`, `decoration-2`, `decoration-4`, `decoration-8`, `decoration-auto`, `decoration-from-font` | `{ decorationThickness: 1 }` etc. | |
3269
+ | **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. | |
3270
+ | **Color** | `text-decoration-color: var(--color-blue-500)` | `decoration-blue-500` | `{ decorationColor: 'blue-500' }` | |
3271
+ | **Arbitrary** | `text-decoration: (value)` | `decoration-[3px]` | `{ decorationThickness: '3px' }` | |
3272
+ | **Var** | `text-decoration: (value)` | `decoration-(--v)` | `{ decorationThickness: '--v' }` | **Sugar**: Auto-detects `--`. |
3238
3273
 
3239
3274
  ## Text Transform
3240
3275
 
3241
3276
  Controlling the capitalization of text.
3242
3277
 
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`. |
3278
+ | Concept | CSS Rule | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
3279
+ | :------------ | :-------------------------- | :---------------------------------------------------- | :------------------------------------------------------------------------------ | :--------------------- |
3280
+ | **Transform** | `text-transform: uppercase` | `uppercase`, `lowercase`, `capitalize`, `normal-case` | `{ textTransform: 'uppercase' }` (also `'lowercase'`, `'capitalize'`, `'none'`) | `'none'` → normal-case |
3247
3281
 
3248
3282
  ## Text Overflow & Whitespace
3249
3283
 
@@ -3313,7 +3347,7 @@ Static items are pre-computed at build time; conditional items use `_szMerge` at
3313
3347
  ```tsx
3314
3348
  <div
3315
3349
  sz={[
3316
- { flex: true, items: "center", p: 4 }, // always — extracted at build time
3350
+ { display: "flex", items: "center", p: 4 }, // always — extracted at build time
3317
3351
  isActive && { bg: "blue-500" }, // runtime conditional
3318
3352
  isDisabled && { opacity: 50, cursor: "not-allowed" },
3319
3353
  ]}
@@ -3355,10 +3389,10 @@ import { szv } from "csszyx";
3355
3389
 
3356
3390
  const buttonSz = szv({
3357
3391
  base: {
3358
- inlineFlex: true,
3392
+ display: "inline-flex",
3359
3393
  items: "center",
3360
3394
  rounded: "md",
3361
- fontWeight: "medium",
3395
+ weight: "medium",
3362
3396
  },
3363
3397
  variants: {
3364
3398
  variant: {
@@ -3443,20 +3477,21 @@ SSR-safe: on the server, returns class names without CSSOM access.
3443
3477
  For dynamic classes at runtime (the only runtime overhead):
3444
3478
 
3445
3479
  ```tsx
3446
- import { _sz, _szIf, _szSwitch, _szMerge } from '@csszyx/runtime';
3480
+ import { _sz, _szMerge } from '@csszyx/runtime';
3447
3481
 
3448
3482
  // Concatenate class strings
3449
3483
  <div className={_sz('base-class', conditionalClass)} />
3450
3484
 
3451
- // Conditional class
3452
- <div className={_szIf(isActive, 'active-class', 'inactive-class')} />
3485
+ // Conditional class — plain JS conditionals compose with _sz
3486
+ <div className={_sz('base-class', isActive && 'active-class')} />
3487
+ <div className={isActive ? 'active-class' : 'inactive-class'} />
3453
3488
 
3454
- // Switch/enum
3455
- <div className={_szSwitch(status, {
3489
+ // Switch/enum — a plain object lookup
3490
+ <div className={{
3456
3491
  loading: 'opacity-50',
3457
3492
  error: 'border-red-500',
3458
3493
  success: 'border-green-500',
3459
- })} />
3494
+ }[status]} />
3460
3495
 
3461
3496
  // Merge (last wins for conflicts)
3462
3497
  <div className={_szMerge(baseClasses, overrideClasses)} />
@@ -3471,6 +3506,51 @@ import { __szColorVar } from "csszyx/lite";
3471
3506
  // __szColorVar('#ff0000') → '#ff0000'
3472
3507
  ```
3473
3508
 
3509
+ ## Nested Element Routing — splitBox + class toolkit
3510
+
3511
+ 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).
3512
+
3513
+ ```tsx
3514
+ import { splitBox, classify, has, pick, omit, stripSzProps } from '@csszyx/runtime';
3515
+
3516
+ // Partition: outer = border-outward (margin/position/border/sizing/bg/shadow/transform/visibility),
3517
+ // inner = border-inward (padding/overflow/display/layout/gap/text/paint-inside/interactivity)
3518
+ const { outer, inner } = splitBox('m-4 px-2 md:flex');
3519
+ // outer: "m-4" inner: "px-2 md:flex"
3520
+
3521
+ // Override any default per call (BoxSelector = box-role | category | class-prefix | { category: value })
3522
+ splitBox('overflow-hidden p-4', { outer: ['overflow'] });
3523
+ // outer: "overflow-hidden" inner: "p-4"
3524
+
3525
+ // Toolkit: csszyx owns the truth (box-role/category), your project owns the rule
3526
+ classify('inset-ring-2'); // { role: 'inner', category: 'ring' }
3527
+ has('p-2 overflow-y-auto', 'overflow'); // true
3528
+ pick('m-4 text-sm', 'text'); // "text-sm"
3529
+ omit('p-2 overflow-y-auto flex', 'overflow'); // "p-2 flex"
3530
+
3531
+ // A scroller scrolls only when the outer frame clips (project-owned dependency rule)
3532
+ const dep = has(outer, { overflow: 'hidden' }) ? 'overflow-y-auto h-full' : '';
3533
+
3534
+ // stripSzProps: drop sz before forwarding ...rest to the DOM (guards uncompiled files
3535
+ // whose raw sz would leak as sz="[object Object]"; dev-warns once on a raw-object sz)
3536
+ function Box({ sz, ...rest }) { return <div {...stripSzProps(rest)} />; }
3537
+ ```
3538
+
3539
+ ## Security — untrusted sz
3540
+
3541
+ 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.
3542
+
3543
+ ```tsx
3544
+ import { dynamic, purifySz } from '@csszyx/dynamic';
3545
+ // stripSzProps is exported from '@csszyx/runtime'
3546
+
3547
+ // Untrusted sz → dynamic() runtime CSS: purify first (allowlist + value sanitize + proto guard)
3548
+ const className = dynamic(purifySz(untrustedSzFromJson));
3549
+ // strict (default) also strips url()/image-set()/@import/expression(); { strict:false } for trusted input
3550
+ ```
3551
+
3552
+ 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.
3553
+
3474
3554
  ## SSR Hydration
3475
3555
 
3476
3556
  CSSzyx validates that server and client use the same mangle map:
@@ -3524,6 +3604,11 @@ In production, class names are mangled for maximum compression:
3524
3604
 
3525
3605
  Output: `<div class="z y x" />` — the CSS `.z { padding: 1rem }` etc. is injected automatically.
3526
3606
 
3607
+ Set `production: { mangle: false }` to keep readable class names — the supported way to
3608
+ inspect the emitted CSS, and recommended for hybrid apps that also ship non-csszyx
3609
+ stylesheets keyed on the original class names. To check what a single `sz` object
3610
+ compiles to without a build, run `csszyx explain "{ p: 4, bg: 'blue-500' }"`.
3611
+
3527
3612
  ### AST budget guard
3528
3613
 
3529
3614
  Files larger than 50 000 AST nodes throw `ASTBudgetExceededError` at
@@ -3541,6 +3626,19 @@ Or exclude the file from csszyx processing entirely before parsing:
3541
3626
  ...csszyx({ exclude: ['src/generated/**', /icon-dump\.tsx$/] })
3542
3627
  ```
3543
3628
 
3629
+ ## Monorepo Workspace Packages (compilePackages)
3630
+
3631
+ `/packages/` is hard-ignored by default (a published library ships pre-extracted
3632
+ CSS). To compile `sz` in a monorepo workspace package you author, opt it in by
3633
+ directory name — only `/packages/` is relaxed, `node_modules`/`.next` stay ignored:
3634
+
3635
+ ```js
3636
+ ...csszyx({ compilePackages: ['vui'] }) // compiles sz in /packages/vui/**
3637
+ ```
3638
+
3639
+ A `/packages/` file with `sz` that is not opted in is skipped silently (no CSS);
3640
+ csszyx warns at build end and lists those files.
3641
+
3544
3642
  ## Theme Auto-Scan (Custom Tokens → TypeScript Types)
3545
3643
 
3546
3644
  When using Tailwind v4 `@theme` blocks for custom design tokens, enable `build.scanCss`
@@ -3631,12 +3729,13 @@ of classes the developer has explicitly flagged as "not yet decided."
3631
3729
  | `inline-flex` | `{ display: 'inline-flex' }` |
3632
3730
  | `hidden` | `{ display: 'none' }` |
3633
3731
 
3634
- Manual authoring can still use `{ flex: true }`, `{ inlineFlex: true }`, etc.
3732
+ Boolean sugar like `{ flex: true }` was removed — use `{ display: 'flex' }`.
3635
3733
  The migrate command uses `display` so duplicate display utilities are visible as
3636
3734
  one semantic CSS property. Conflicting display utilities in the same variant
3637
3735
  scope, such as `block flex` or `md:block md:flex`, are left unresolved instead
3638
3736
  of guessed. `flex flex-1` is safe and migrates to
3639
- `{ display: 'flex', flex: '1' }`.
3737
+ `{ display: 'flex', flex: '1' }` (the `flex` shorthand stays — only the
3738
+ `flex: true` display alias was removed).
3640
3739
 
3641
3740
  ### `--resolve-todos` — apply the resolution map
3642
3741
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@csszyx/mcp-server",
3
- "version": "0.9.9",
3
+ "version": "0.10.0",
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/cli": "0.9.9",
34
- "@csszyx/unplugin": "0.9.9",
35
- "@csszyx/compiler": "0.9.9"
33
+ "@csszyx/cli": "0.10.0",
34
+ "@csszyx/unplugin": "0.10.0",
35
+ "@csszyx/compiler": "0.10.0"
36
36
  },
37
37
  "devDependencies": {
38
38
  "typescript": "^6.0.3",