@csszyx/mcp-server 0.11.11 → 0.13.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/README.md +10 -9
- package/dist/index.d.mts +38 -1
- package/dist/index.mjs +98 -2
- package/llms-full.txt +115 -57
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -53,15 +53,16 @@ The server speaks MCP over stdio, so any MCP-compatible client uses the same
|
|
|
53
53
|
|
|
54
54
|
## Tools
|
|
55
55
|
|
|
56
|
-
| Tool
|
|
57
|
-
|
|
|
58
|
-
| `csszyx_expand`
|
|
59
|
-
| `csszyx_batch`
|
|
60
|
-
| `csszyx_reverse`
|
|
61
|
-
| `csszyx_validate`
|
|
62
|
-
| `csszyx_lookup`
|
|
63
|
-
| `csszyx_migrate`
|
|
64
|
-
| `csszyx_theme`
|
|
56
|
+
| Tool | Purpose |
|
|
57
|
+
| ------------------------ | ----------------------------------------------------------------------------------------------- |
|
|
58
|
+
| `csszyx_expand` | Expand one `sz` object into a Tailwind class string |
|
|
59
|
+
| `csszyx_batch` | Expand many `sz` objects in one call |
|
|
60
|
+
| `csszyx_reverse` | Convert a Tailwind class string back into an `sz` object |
|
|
61
|
+
| `csszyx_validate` | Validate an `sz` object; reports unknown props and CSS-name mistakes |
|
|
62
|
+
| `csszyx_lookup` | Look up how a CSS property/keyword maps to an `sz` key |
|
|
63
|
+
| `csszyx_migrate` | Rewrite a JSX/TSX snippet's `className` attributes into `sz` props |
|
|
64
|
+
| `csszyx_theme` | Parse `@theme` CSS blocks and categorize design tokens |
|
|
65
|
+
| `csszyx_compile_preview` | Compile a whole source module and report the classes, diagnostics, and leftover runtime helpers |
|
|
65
66
|
|
|
66
67
|
## Resources
|
|
67
68
|
|
package/dist/index.d.mts
CHANGED
|
@@ -8,7 +8,7 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
|
8
8
|
* understand and generate csszyx sz props.
|
|
9
9
|
*
|
|
10
10
|
* Architecture follows Tier-1 Proxy patterns:
|
|
11
|
-
* -
|
|
11
|
+
* - 8 Stateless Sandbox tools (no destructive edits)
|
|
12
12
|
* - 3 Reference Data Endpoints
|
|
13
13
|
* - Unified Single Source of Truth via @csszyx/compiler and @csszyx/unplugin
|
|
14
14
|
*
|
|
@@ -33,6 +33,8 @@ declare const TOOLS: ({
|
|
|
33
33
|
injectTodos?: undefined;
|
|
34
34
|
items?: undefined;
|
|
35
35
|
css?: undefined;
|
|
36
|
+
source?: undefined;
|
|
37
|
+
filename?: undefined;
|
|
36
38
|
};
|
|
37
39
|
required: string[];
|
|
38
40
|
};
|
|
@@ -53,6 +55,8 @@ declare const TOOLS: ({
|
|
|
53
55
|
injectTodos?: undefined;
|
|
54
56
|
items?: undefined;
|
|
55
57
|
css?: undefined;
|
|
58
|
+
source?: undefined;
|
|
59
|
+
filename?: undefined;
|
|
56
60
|
};
|
|
57
61
|
required: string[];
|
|
58
62
|
};
|
|
@@ -73,6 +77,8 @@ declare const TOOLS: ({
|
|
|
73
77
|
injectTodos?: undefined;
|
|
74
78
|
items?: undefined;
|
|
75
79
|
css?: undefined;
|
|
80
|
+
source?: undefined;
|
|
81
|
+
filename?: undefined;
|
|
76
82
|
};
|
|
77
83
|
required: string[];
|
|
78
84
|
};
|
|
@@ -99,6 +105,8 @@ declare const TOOLS: ({
|
|
|
99
105
|
query?: undefined;
|
|
100
106
|
items?: undefined;
|
|
101
107
|
css?: undefined;
|
|
108
|
+
source?: undefined;
|
|
109
|
+
filename?: undefined;
|
|
102
110
|
};
|
|
103
111
|
required: string[];
|
|
104
112
|
};
|
|
@@ -122,6 +130,8 @@ declare const TOOLS: ({
|
|
|
122
130
|
customMap?: undefined;
|
|
123
131
|
injectTodos?: undefined;
|
|
124
132
|
css?: undefined;
|
|
133
|
+
source?: undefined;
|
|
134
|
+
filename?: undefined;
|
|
125
135
|
};
|
|
126
136
|
required: string[];
|
|
127
137
|
};
|
|
@@ -142,6 +152,33 @@ declare const TOOLS: ({
|
|
|
142
152
|
customMap?: undefined;
|
|
143
153
|
injectTodos?: undefined;
|
|
144
154
|
items?: undefined;
|
|
155
|
+
source?: undefined;
|
|
156
|
+
filename?: undefined;
|
|
157
|
+
};
|
|
158
|
+
required: string[];
|
|
159
|
+
};
|
|
160
|
+
} | {
|
|
161
|
+
name: string;
|
|
162
|
+
description: string;
|
|
163
|
+
inputSchema: {
|
|
164
|
+
type: "object";
|
|
165
|
+
properties: {
|
|
166
|
+
source: {
|
|
167
|
+
type: "string";
|
|
168
|
+
description: string;
|
|
169
|
+
};
|
|
170
|
+
filename: {
|
|
171
|
+
type: "string";
|
|
172
|
+
description: string;
|
|
173
|
+
};
|
|
174
|
+
sz?: undefined;
|
|
175
|
+
classes?: undefined;
|
|
176
|
+
query?: undefined;
|
|
177
|
+
code?: undefined;
|
|
178
|
+
customMap?: undefined;
|
|
179
|
+
injectTodos?: undefined;
|
|
180
|
+
items?: undefined;
|
|
181
|
+
css?: undefined;
|
|
145
182
|
};
|
|
146
183
|
required: string[];
|
|
147
184
|
};
|
package/dist/index.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
6
6
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
7
7
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
8
8
|
import { ListToolsRequestSchema, CallToolRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
9
|
-
import { sortStrings, SPECIAL_VARIANTS, KNOWN_VARIANTS, PROPERTY_MAP, transform, SUGGESTION_MAP, BOOLEAN_SHORTHANDS, REMOVED_BOOLEAN_SUGAR } from '@csszyx/compiler';
|
|
9
|
+
import { sortStrings, SPECIAL_VARIANTS, KNOWN_VARIANTS, PROPERTY_MAP, transform, transformSourceCode, SUGGESTION_MAP, BOOLEAN_SHORTHANDS, REMOVED_BOOLEAN_SUGAR } from '@csszyx/compiler';
|
|
10
10
|
import { z } from 'zod';
|
|
11
11
|
import { migrateSource, classNameToSzObject } from '@csszyx/cli';
|
|
12
12
|
import { parseThemeBlocks, hasTokens } from '@csszyx/unplugin';
|
|
@@ -333,6 +333,80 @@ function handleBatch(input) {
|
|
|
333
333
|
};
|
|
334
334
|
}
|
|
335
335
|
|
|
336
|
+
const compilePreviewSchema = z.object({
|
|
337
|
+
source: z.string().describe(
|
|
338
|
+
"A source module to compile. Include the code AROUND the sz prop \u2014 imports, the factory call, the component \u2014 since that is what decides whether csszyx can compile it."
|
|
339
|
+
),
|
|
340
|
+
filename: z.string().optional().describe(
|
|
341
|
+
"Filename to attribute diagnostics to. The extension selects the parser, so use .tsx for JSX. Defaults to preview.tsx."
|
|
342
|
+
)
|
|
343
|
+
});
|
|
344
|
+
const RUNTIME_HELPERS = [
|
|
345
|
+
["_sz", "usesRuntime"],
|
|
346
|
+
["_szMerge", "usesMerge"],
|
|
347
|
+
["_szPart", "usesSzPart"],
|
|
348
|
+
["szcn", "usesSzcn"],
|
|
349
|
+
["__szvPick", "usesSzvPick"],
|
|
350
|
+
["__szvPick1", "usesSzvPick1"],
|
|
351
|
+
["__szColorVar", "usesColorVar"],
|
|
352
|
+
["__szSpacingVar", "usesSpacingVar"],
|
|
353
|
+
["__szUnitVar", "usesUnitVar"]
|
|
354
|
+
];
|
|
355
|
+
function restoreEnv(name, value) {
|
|
356
|
+
if (value === void 0) {
|
|
357
|
+
delete process.env[name];
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
process.env[name] = value;
|
|
361
|
+
}
|
|
362
|
+
function compileWithDiagnostics(source, filename) {
|
|
363
|
+
const warnings = [];
|
|
364
|
+
const originalWarn = console.warn;
|
|
365
|
+
const originalNodeEnv = process.env.NODE_ENV;
|
|
366
|
+
const originalQuiet = process.env.CSSZYX_QUIET_SZ_WARNINGS;
|
|
367
|
+
console.warn = (...args) => {
|
|
368
|
+
warnings.push(args.map(String).join(" "));
|
|
369
|
+
};
|
|
370
|
+
process.env.NODE_ENV = "development";
|
|
371
|
+
delete process.env.CSSZYX_QUIET_SZ_WARNINGS;
|
|
372
|
+
try {
|
|
373
|
+
return { result: transformSourceCode(source, filename), warnings };
|
|
374
|
+
} finally {
|
|
375
|
+
console.warn = originalWarn;
|
|
376
|
+
restoreEnv("NODE_ENV", originalNodeEnv);
|
|
377
|
+
restoreEnv("CSSZYX_QUIET_SZ_WARNINGS", originalQuiet);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
function handleCompilePreview(input) {
|
|
381
|
+
const { result, warnings } = compileWithDiagnostics(
|
|
382
|
+
input.source,
|
|
383
|
+
input.filename ?? "preview.tsx"
|
|
384
|
+
);
|
|
385
|
+
return {
|
|
386
|
+
content: [
|
|
387
|
+
{
|
|
388
|
+
type: "text",
|
|
389
|
+
text: JSON.stringify(
|
|
390
|
+
{
|
|
391
|
+
transformed: result.transformed,
|
|
392
|
+
code: result.code,
|
|
393
|
+
// Emission order, not sorted: this is the order the
|
|
394
|
+
// element actually carries, and reordering it would
|
|
395
|
+
// hide a cascade question the preview is asked about.
|
|
396
|
+
classes: [...result.classes],
|
|
397
|
+
diagnostics: [...result.diagnostics, ...warnings],
|
|
398
|
+
runtimeHelpers: RUNTIME_HELPERS.filter(
|
|
399
|
+
([, flag]) => result[flag] === true
|
|
400
|
+
).map(([name]) => name)
|
|
401
|
+
},
|
|
402
|
+
null,
|
|
403
|
+
2
|
|
404
|
+
)
|
|
405
|
+
}
|
|
406
|
+
]
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
|
|
336
410
|
const expandSchema = z.object({
|
|
337
411
|
sz: z.record(z.string(), z.any()).describe(
|
|
338
412
|
"The sz prop object to expand. Example: { p: 4, bg: 'blue-500', hover: { bg: 'blue-700' } }"
|
|
@@ -773,6 +847,24 @@ const TOOLS = [
|
|
|
773
847
|
},
|
|
774
848
|
required: ["css"]
|
|
775
849
|
}
|
|
850
|
+
},
|
|
851
|
+
{
|
|
852
|
+
name: "csszyx_compile_preview",
|
|
853
|
+
description: "Compile a whole SOURCE MODULE and report what csszyx made of it: the rewritten code, the classes, the diagnostics, and the runtime helpers left over. Use this when the question is about the code AROUND the sz prop \u2014 whether a szv factory precompiles, whether a value stays dynamic, whether a key is unknown. For a bare sz object, use csszyx_expand instead.",
|
|
854
|
+
inputSchema: {
|
|
855
|
+
type: "object",
|
|
856
|
+
properties: {
|
|
857
|
+
source: {
|
|
858
|
+
type: "string",
|
|
859
|
+
description: "A source module, including its imports and component. Example: export const A = () => <div sz={{ p: 4 }} />;"
|
|
860
|
+
},
|
|
861
|
+
filename: {
|
|
862
|
+
type: "string",
|
|
863
|
+
description: "Filename to attribute diagnostics to. The extension selects the parser, so use .tsx for JSX. Defaults to preview.tsx."
|
|
864
|
+
}
|
|
865
|
+
},
|
|
866
|
+
required: ["source"]
|
|
867
|
+
}
|
|
776
868
|
}
|
|
777
869
|
];
|
|
778
870
|
const TOOL_HANDLERS = {
|
|
@@ -782,7 +874,11 @@ const TOOL_HANDLERS = {
|
|
|
782
874
|
csszyx_lookup: { schema: lookupSchema, handler: handleLookup },
|
|
783
875
|
csszyx_migrate: { schema: migrateSchema, handler: handleMigrate },
|
|
784
876
|
csszyx_batch: { schema: batchSchema, handler: handleBatch },
|
|
785
|
-
csszyx_theme: { schema: themeSchema, handler: handleTheme }
|
|
877
|
+
csszyx_theme: { schema: themeSchema, handler: handleTheme },
|
|
878
|
+
csszyx_compile_preview: {
|
|
879
|
+
schema: compilePreviewSchema,
|
|
880
|
+
handler: handleCompilePreview
|
|
881
|
+
}
|
|
786
882
|
};
|
|
787
883
|
server.server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
788
884
|
return { tools: TOOLS };
|
package/llms-full.txt
CHANGED
|
@@ -263,7 +263,8 @@ compile error, so typos and legacy CSS-property names are caught by `tsc` (run
|
|
|
263
263
|
```
|
|
264
264
|
|
|
265
265
|
Arbitrary variants are allowed by pattern: `{ '@container': {…} }`, `{ 'min-[320px]': {…} }`,
|
|
266
|
-
`{ '[&>span]': {…} }`.
|
|
266
|
+
`{ '[&>span]': {…} }`. A STRING value under any variant key is a ready-made utility the
|
|
267
|
+
variant prefixes with `:` — `{ 'data-[open]': 'sr-only' }` → `data-[open]:sr-only`. **Custom breakpoints are typed from your CSS** — define one once in
|
|
267
268
|
Tailwind `@theme` and csszyx auto-generates the type:
|
|
268
269
|
|
|
269
270
|
```css
|
|
@@ -366,14 +367,15 @@ value position — `p`, `bg`, `m`, `w`, `text`, …, not just font-size):
|
|
|
366
367
|
|
|
367
368
|
Handling `group-*`, `peer-*`, and arbitrary variants.
|
|
368
369
|
|
|
369
|
-
| Concept
|
|
370
|
-
|
|
|
371
|
-
| **Group Hover**
|
|
372
|
-
| **Peer Focus**
|
|
373
|
-
| **Data Attribute**
|
|
374
|
-
| **ARIA Attribute**
|
|
375
|
-
| **Arbitrary Variant**
|
|
376
|
-
| **Important Modifier**
|
|
370
|
+
| Concept | CSS Rule | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
|
|
371
|
+
| :------------------------------ | :------------------------------------ | :------------------------ | :------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
372
|
+
| **Group Hover** | `.group:hover .group-hover:text-blue` | `group-hover:text-blue` | `{ group: { hover: { color: 'blue' } } }` | **Sugar**: Nested `group` key acts as modifier scope. |
|
|
373
|
+
| **Peer Focus** | `.peer:focus ~ .peer-focus:text-blue` | `peer-focus:text-blue` | `{ peer: { focus: { color: 'blue' } } }` | **Sugar**: Nested `peer` key. |
|
|
374
|
+
| **Data Attribute** | `&[data-active] (etc)` | `data-[active]:text-blue` | `{ data: { active: { color: 'blue' } } }` | **Sugar**: Maps to `data-[key]`. |
|
|
375
|
+
| **ARIA Attribute** | `&[aria-expanded="true"] (etc)` | `aria-expanded:text-blue` | `{ aria: { expanded: { color: 'blue' } } }` | **Sugar**: Maps to `aria-[key]`. |
|
|
376
|
+
| **Arbitrary Variant** | `& > span` | `[&>span]:text-blue` | `{ '[& > span]': { color: 'blue' } }` | |
|
|
377
|
+
| **Important Modifier** | `color: red !important` | `text-red-500!` | `{ color: 'red-500!' }` | **New**: Trailing `!` in value maps to trailing `!` in class. |
|
|
378
|
+
| **Important + arbitrary value** | `font-size: 14px !important` | `text-[14px]!` | `{ text: '14px!' }` | The `!` sits **outside** the brackets. A value that needs brackets is decided without the `!` attached, so `14px` is still recognised as a length. |
|
|
377
379
|
|
|
378
380
|
## Style Conflict Management
|
|
379
381
|
|
|
@@ -515,6 +517,34 @@ Data and state attributes.
|
|
|
515
517
|
| **Open** | `open:bg-white` | `{ open: { bg: 'white' } }` | |
|
|
516
518
|
| **Inert** | `inert:opacity-50` | `{ inert: { opacity: 50 } }` | |
|
|
517
519
|
|
|
520
|
+
## Variant String Shorthand
|
|
521
|
+
|
|
522
|
+
A **string value under a variant key** is a ready-made Tailwind utility to
|
|
523
|
+
prefix — the variant chains onto it with `:`. Use it when the utility has no
|
|
524
|
+
sz key spelling (e.g. `sr-only`) or when you already have the exact class.
|
|
525
|
+
Works for every variant form, not just the simple names.
|
|
526
|
+
|
|
527
|
+
| Concept | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
|
|
528
|
+
| :------------------------- | :------------------------------------- | :---------------------------------------------- | :----------------------------------------------- |
|
|
529
|
+
| **Known variant** | `hover:translate-x-full` | `{ hover: 'translate-x-full' }` | |
|
|
530
|
+
| **Arbitrary data variant** | `data-[ending-style]:translate-x-full` | `{ 'data-[ending-style]': 'translate-x-full' }` | |
|
|
531
|
+
| **Bare data variant** | `data-open:sr-only` | `{ 'data-open': 'sr-only' }` | Attribute presence (Tailwind v4). |
|
|
532
|
+
| **Bare ARIA state** | `aria-checked:opacity-50` | `{ 'aria-checked': 'opacity-50' }` | Built-in ARIA set only; others use `[...]`. |
|
|
533
|
+
| **Scope compound** | `group-hover:translate-x-full` | `{ 'group-hover': 'translate-x-full' }` | Also `peer-*`, `not-*` with a known state. |
|
|
534
|
+
| **Arbitrary selector** | `[&>li]:translate-x-full` | `{ '[&>li]': 'translate-x-full' }` | |
|
|
535
|
+
| **Arbitrary breakpoint** | `min-[900px]:flex` | `{ 'min-[900px]': 'flex' }` | Also `max-[...]`, `supports-[...]`, `has-[...]`. |
|
|
536
|
+
|
|
537
|
+
The string is emitted verbatim after the variant — csszyx does not validate it
|
|
538
|
+
against Tailwind's utility set, so prefer sz keys where one exists. A key that
|
|
539
|
+
is not a variant keeps its property meaning: `{ 'not-italic': true }` is still
|
|
540
|
+
the font-style utility.
|
|
541
|
+
|
|
542
|
+
Negative utility keywords under a variant place the minus on the utility:
|
|
543
|
+
`{ hover: { translateX: '-full' } }` → `hover:-translate-x-full`. Arbitrary
|
|
544
|
+
negative values keep the sign inside the bracket:
|
|
545
|
+
`{ 'data-[starting-style]': { translateX: '-100%' } }` →
|
|
546
|
+
`data-[starting-style]:translate-x-[-100%]`.
|
|
547
|
+
|
|
518
548
|
## Helper Variants (Child/Descendants)
|
|
519
549
|
|
|
520
550
|
Mapping for common descendant patterns.
|
|
@@ -1145,7 +1175,7 @@ Controlling the box shadow of an element.
|
|
|
1145
1175
|
| **Inset Ring** | `box-shadow: inset (etc)` | `inset-ring`, `inset-ring-1` | `{ insetRing: 1 }` | |
|
|
1146
1176
|
| **None** | `box-shadow: 0 0 #0000` | `shadow-none` | `{ shadow: 'none' }` | |
|
|
1147
1177
|
| **Inset None** | `box-shadow: inset 0 0 #0000` | `inset-shadow-none` | `{ insetShadow: 'none' }` | |
|
|
1148
|
-
| **Ring None** | `box-shadow: 0 0 #0000` | `ring-
|
|
1178
|
+
| **Ring None** | `box-shadow: 0 0 #0000` | `ring-0` | `{ ring: 'none' }` | Tailwind spells the zero ring `ring-0`; `ring-none` styles nothing. |
|
|
1149
1179
|
| **Color** | `box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05)` | `shadow-blue-500`, `inset-shadow-blue-500`, `ring-blue-500` | `{ shadowColor: 'blue-500' }` | |
|
|
1150
1180
|
| **Color + Opacity** | `--tw-shadow-color: (value) / 50%` | `shadow-blue-500/50` | `{ shadowColor: { color: 'blue-500', op: 50 } }` | |
|
|
1151
1181
|
| **Size + Opacity** | `box-shadow: (size) / 12.5%` | `shadow-sm/12.5`, `shadow-2xl/50` | `{ shadow: 'sm/12.5' }`, `{ shadow: '2xl/50' }` | **TW 4.3.3**: fractional opacity on named sizes. |
|
|
@@ -1222,35 +1252,29 @@ Controlling the masking of an element with images, gradients, and CSS properties
|
|
|
1222
1252
|
|
|
1223
1253
|
> **Source:** [Tailwind CSS v4.1 Documentation](https://tailwindcss.com/docs/mask-image)
|
|
1224
1254
|
|
|
1255
|
+
**Migration (breaking):** the flat stop keys were removed — `maskFrom`/`maskTo`
|
|
1256
|
+
moved into their layer (`{ maskLinear: { from } }` etc.), `maskShape` moved to
|
|
1257
|
+
`{ maskRadial: { shape } }`, `maskVia` was removed with no equivalent (Tailwind
|
|
1258
|
+
has no via stop for masks), and gradient layer values on `mask`
|
|
1259
|
+
(`{ mask: 'linear-45' }`) moved to the layer keys. `mask` carries only a direct
|
|
1260
|
+
mask-image: `none`, `url(…)`, a CSS variable, or an arbitrary value.
|
|
1261
|
+
|
|
1225
1262
|
### mask-image: Gradient Masks
|
|
1226
1263
|
|
|
1227
|
-
| Concept | CSS Rule | Tailwind v4 Class
|
|
1228
|
-
| :------------- | :-------------------------------------------- |
|
|
1229
|
-
| **None** | `mask-image: none` | `mask-none`
|
|
1230
|
-
| **Linear** | `mask-image: linear-gradient(45deg, ...)` | `mask-linear-45`
|
|
1231
|
-
| **Linear Neg** | `mask-image: linear-gradient(-45deg, ...)` | `-mask-linear-45`
|
|
1232
|
-
| **Radial** | `mask-image: radial-gradient(...)` | `mask-radial`
|
|
1233
|
-
| **Conic** | `mask-image: conic-gradient(from 90deg, ...)` | `mask-conic-90`
|
|
1234
|
-
|
|
1235
|
-
### mask-image: Direction Keywords
|
|
1236
|
-
|
|
1237
|
-
| Concept | CSS Rule | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
|
|
1238
|
-
| :------------------ | :-------------------------------------------------- | :------------------ | :------------------------- | :--- |
|
|
1239
|
-
| **To Top** | `mask-image: linear-gradient(to top, ...)` | `mask-linear-to-t` | `{ mask: 'linear-to-t' }` | |
|
|
1240
|
-
| **To Top Right** | `mask-image: linear-gradient(to top right, ...)` | `mask-linear-to-tr` | `{ mask: 'linear-to-tr' }` | |
|
|
1241
|
-
| **To Right** | `mask-image: linear-gradient(to right, ...)` | `mask-linear-to-r` | `{ mask: 'linear-to-r' }` | |
|
|
1242
|
-
| **To Bottom Right** | `mask-image: linear-gradient(to bottom right, ...)` | `mask-linear-to-br` | `{ mask: 'linear-to-br' }` | |
|
|
1243
|
-
| **To Bottom** | `mask-image: linear-gradient(to bottom, ...)` | `mask-linear-to-b` | `{ mask: 'linear-to-b' }` | |
|
|
1244
|
-
| **To Bottom Left** | `mask-image: linear-gradient(to bottom left, ...)` | `mask-linear-to-bl` | `{ mask: 'linear-to-bl' }` | |
|
|
1245
|
-
| **To Left** | `mask-image: linear-gradient(to left, ...)` | `mask-linear-to-l` | `{ mask: 'linear-to-l' }` | |
|
|
1246
|
-
| **To Top Left** | `mask-image: linear-gradient(to top left, ...)` | `mask-linear-to-tl` | `{ mask: 'linear-to-tl' }` | |
|
|
1264
|
+
| Concept | CSS Rule | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
|
|
1265
|
+
| :------------- | :-------------------------------------------- | :---------------------- | :--------------------------------- | :------------------------- |
|
|
1266
|
+
| **None** | `mask-image: none` | `mask-none` | `{ mask: 'none' }` | |
|
|
1267
|
+
| **Linear** | `mask-image: linear-gradient(45deg, ...)` | `mask-linear-45` | `{ maskLinear: { angle: 45 } }` | Angle in degrees. |
|
|
1268
|
+
| **Linear Neg** | `mask-image: linear-gradient(-45deg, ...)` | `-mask-linear-45` | `{ maskLinear: { angle: -45 } }` | Negative angle prefix `-`. |
|
|
1269
|
+
| **Radial** | `mask-image: radial-gradient(...)` | `mask-radial-at-center` | `{ maskRadial: { at: 'center' } }` | |
|
|
1270
|
+
| **Conic** | `mask-image: conic-gradient(from 90deg, ...)` | `mask-conic-90` | `{ maskConic: { angle: 90 } }` | |
|
|
1247
1271
|
|
|
1248
1272
|
### mask-image: Shape Modifiers (Radial)
|
|
1249
1273
|
|
|
1250
|
-
| Concept | CSS Rule | Tailwind v4 Class | `sz` Prop (Object Syntax)
|
|
1251
|
-
| :---------- | :-------------------------------- | :---------------- |
|
|
1252
|
-
| **Circle** | `--tw-mask-radial-shape: circle` | `mask-circle` | `{
|
|
1253
|
-
| **Ellipse** | `--tw-mask-radial-shape: ellipse` | `mask-ellipse` | `{
|
|
1274
|
+
| Concept | CSS Rule | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
|
|
1275
|
+
| :---------- | :-------------------------------- | :---------------- | :------------------------------------- | :-------------------- |
|
|
1276
|
+
| **Circle** | `--tw-mask-radial-shape: circle` | `mask-circle` | `{ maskRadial: { shape: 'circle' } }` | For radial gradients. |
|
|
1277
|
+
| **Ellipse** | `--tw-mask-radial-shape: ellipse` | `mask-ellipse` | `{ maskRadial: { shape: 'ellipse' } }` | Default shape. |
|
|
1254
1278
|
|
|
1255
1279
|
### mask-image: Arbitrary Values
|
|
1256
1280
|
|
|
@@ -1443,12 +1467,12 @@ Controls how multiple masks are combined.
|
|
|
1443
1467
|
|
|
1444
1468
|
Control the color stops used in mask gradient functions.
|
|
1445
1469
|
|
|
1446
|
-
| Concept
|
|
1447
|
-
|
|
|
1448
|
-
| **
|
|
1449
|
-
| **
|
|
1450
|
-
| **
|
|
1451
|
-
| **Variable** | `mask-from
|
|
1470
|
+
| Concept | CSS Rule | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
|
|
1471
|
+
| :------------- | :------------------------------- | :---------------------------------------------- | :-------------------------------------------------------------- | :------------------------------------- |
|
|
1472
|
+
| **Layer stop** | `--tw-mask-linear-from-position` | `mask-linear-from-20%` | `{ maskLinear: { from: '20%' } }` | Also `maskRadial` / `maskConic`. |
|
|
1473
|
+
| **Side stop** | `--tw-mask-bottom-from-position` | `mask-b-from-20%` | `{ maskLinear: { b: { from: '20%' } } }` | Sides: `t r b l x y`. |
|
|
1474
|
+
| **Colour** | `--tw-mask-bottom-from-color` | `mask-b-from-red-500/30` | `{ maskLinear: { b: { from: { color: 'red-500', op: 30 } } } }` | Position and colour are separate vars. |
|
|
1475
|
+
| **Variable** | position vs colour | `mask-b-from-(--c)` / `mask-b-from-(color:--c)` | `{ from: { at: '--c' } }` / `{ from: { color: '--c' } }` | A bare var reads as a POSITION. |
|
|
1452
1476
|
|
|
1453
1477
|
|
|
1454
1478
|
# Filters
|
|
@@ -3384,7 +3408,7 @@ Controlling the font weight.
|
|
|
3384
3408
|
| Concept | CSS Rule | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
|
|
3385
3409
|
| :--------------- | :---------------------- | :--------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------- | :----------------------------------------------------------------- |
|
|
3386
3410
|
| **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. | |
|
|
3387
|
-
| **Number** | `font-weight: 100-900` | `font-100`, `font-200`, `font-300`, `font-400`, `font-500`, `font-600`, `font-700`, `font-800`, `font-900`
|
|
3411
|
+
| **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 spells weights through `--font-weight-*`, so a NUMBER brackets. |
|
|
3388
3412
|
| **Alias** | (Sugar) | `font-bold` | `{ weight: 'bold' }` | Sugar for `weight`. |
|
|
3389
3413
|
| **Arbitrary** | `font-weight: 550` | `font-[550]` | `{ weight: 550 }` | |
|
|
3390
3414
|
| **CSS Variable** | `font-weight: var(--w)` | `font-(weight:--w)` | `{ weight: '--w' }` | **Sugar**: Auto-detects `--`. Type hint disambiguates from family. |
|
|
@@ -3417,10 +3441,10 @@ Controlling numeric glyphs.
|
|
|
3417
3441
|
|
|
3418
3442
|
Controlling font-feature-settings. Added in Tailwind v4.2.
|
|
3419
3443
|
|
|
3420
|
-
| Concept | CSS Rule | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note
|
|
3421
|
-
| :------------ | :-------------------------------- | :------------------------- | :----------------------------- |
|
|
3422
|
-
| **Normal** | `font-feature-settings: normal` | `font-features-normal`
|
|
3423
|
-
| **Arbitrary** | `font-feature-settings: "liga" 1` | `font-features-["liga"_1]` | `{ fontFeatures: '"liga" 1' }` |
|
|
3444
|
+
| Concept | CSS Rule | Tailwind v4 Class | `sz` Prop (Object Syntax) | Note |
|
|
3445
|
+
| :------------ | :-------------------------------- | :------------------------- | :----------------------------- | :-------------------------------------------------------------------------------- |
|
|
3446
|
+
| **Normal** | `font-feature-settings: normal` | `font-features-[normal]` | `{ fontFeatures: 'normal' }` | Tailwind's `font-features-*` is functional-only; the bare keyword styles nothing. |
|
|
3447
|
+
| **Arbitrary** | `font-feature-settings: "liga" 1` | `font-features-["liga"_1]` | `{ fontFeatures: '"liga" 1' }` | |
|
|
3424
3448
|
|
|
3425
3449
|
## Font Style & Smoothing
|
|
3426
3450
|
|
|
@@ -3632,10 +3656,12 @@ sz={{ scale: shrunk ? 75 : 100 }} // inline prop ternary — both lit
|
|
|
3632
3656
|
- Use `sz={var}` when no override needed (simpler)
|
|
3633
3657
|
- Use `sz={{ ...var, key: val }}` only when overriding/adding
|
|
3634
3658
|
- Variables in array elements, ternary branches, and chained initializers all resolve at build time
|
|
3659
|
+
- `build.importedStaticSz` (default TRUE) compiles a static sz object imported from another module — v1 covers a direct `sz={binding}` from a NAMED import of an `export const` object literal, written relative or through a project alias (read from the bundler's `resolve.alias` and tsconfig `compilerOptions.paths`); barrels, package specifiers, absolute specifiers, namespace/default imports and nested positions keep the runtime fallback and report it; every lane defaults to on so nothing needs wiring, and turning it OFF must be done in all of them — `csszyxTurbopack({}, { importedStaticSz: false })` PLUS `csszyx next prebuild --no-imported-static-sz` PLUS `csszyx next watch --no-imported-static-sz` for dev — because a mismatch fails the production build on the config hash
|
|
3660
|
+
- A shared sz object is a build-time value: the compiler reads it where it is DECLARED. Reassigning the binding (`let s = {...}; s = {...}`) is detected and falls back to the runtime; a property write (`s.p = 8`) is NOT detected and the class still describes the declared value. Declare once, never write to it — vary with spread overrides or `szv()`
|
|
3635
3661
|
- `sz={{ key: cond ? a : b }}` — both literal branches compile to static classes; a runtime branch uses a CSS variable. An opposite `undefined`, `null`, `false`, or `''` branch omits the utility and variable value (`0` remains valid)
|
|
3636
3662
|
- When runtime `sz` values emit inline CSS variables beside one direct object-literal JSX spread, or one conditional whose branches are object literals, the compiler injects the variables into every spread branch's `style` so both authored and generated values survive. Keep unresolved or multiple spreads' style explicit; csszyx warns when it cannot prove a single-evaluation merge is safe
|
|
3637
3663
|
- `sz={{ ...(cond ? a : b), static: val }}` — conditional spread hoist: compiler resolves both branches at build time
|
|
3638
|
-
- Imported variables / function call results fall back to `_sz()` runtime —
|
|
3664
|
+
- Imported variables / function call results fall back to `_sz()` runtime — a build-time compiler warning explains the fallback reason and suggests `szv()` or `dynamic()`. An expression rooted in an IMPORT collects no classes at all, so that warning is kept in production builds and survives `quiet: 'nudges'`; every other unresolved shape — including a forwarded `sz` prop, whose classes the caller contributes — stays dev-only advice
|
|
3639
3665
|
|
|
3640
3666
|
Full guide: `/docs/reusing-styles`
|
|
3641
3667
|
|
|
@@ -3698,13 +3724,10 @@ unset"; same-file `const` references and `const` object spreads are followed.
|
|
|
3698
3724
|
For styles from JSON / API / CMS / form renderer schemas:
|
|
3699
3725
|
|
|
3700
3726
|
```tsx
|
|
3701
|
-
import { dynamic
|
|
3727
|
+
import { dynamic } from "@csszyx/dynamic";
|
|
3702
3728
|
// or: import { dynamic } from 'csszyx/dynamic';
|
|
3703
3729
|
|
|
3704
|
-
//
|
|
3705
|
-
await preloadManifest("/csszyx-manifest.json");
|
|
3706
|
-
|
|
3707
|
-
// Apply runtime sz object — CSS injected only for missing classes
|
|
3730
|
+
// Apply runtime sz object — CSS is injected for classes not already built
|
|
3708
3731
|
const cls = dynamic({
|
|
3709
3732
|
p: 4,
|
|
3710
3733
|
bg: "white",
|
|
@@ -3713,6 +3736,25 @@ const cls = dynamic({
|
|
|
3713
3736
|
});
|
|
3714
3737
|
```
|
|
3715
3738
|
|
|
3739
|
+
**The manifest is opt-in.** `csszyx-manifest.json` lets `dynamic()` skip injecting
|
|
3740
|
+
rules the built CSS already has, but it carries the WHOLE class census to answer
|
|
3741
|
+
questions about the few classes `dynamic()` renders — measured on a 668-class
|
|
3742
|
+
census it costs ~2 kB gz to spare a few hundred bytes of injection, and only wins
|
|
3743
|
+
once most of the app is styled at runtime. `build.emitManifest` defaults to
|
|
3744
|
+
`false`; a missing manifest is not an error, `dynamic()` just injects its own
|
|
3745
|
+
rules and the styles are identical.
|
|
3746
|
+
|
|
3747
|
+
If you enable it, preload it — `dynamic()` is synchronous and the fetch is not,
|
|
3748
|
+
so an unawaited manifest arrives after the first paint has already injected
|
|
3749
|
+
everything and the build pays both costs:
|
|
3750
|
+
|
|
3751
|
+
```tsx
|
|
3752
|
+
import { dynamicReport, preloadManifest } from "@csszyx/dynamic";
|
|
3753
|
+
|
|
3754
|
+
await preloadManifest(); // before the first render
|
|
3755
|
+
console.log(dynamicReport().summary); // development: did it pay off here?
|
|
3756
|
+
```
|
|
3757
|
+
|
|
3716
3758
|
**Build-time extraction (Layer-1 prescan):** When `dynamic()` receives a static literal or
|
|
3717
3759
|
module-level const, the compiler extracts classes at build time → Tailwind generates CSS
|
|
3718
3760
|
ahead of time → no runtime injection needed. Works in Astro SSR without `client:*`.
|
|
@@ -3981,14 +4023,19 @@ no tsconfig change needed.
|
|
|
3981
4023
|
|
|
3982
4024
|
## Production Build (Mangling)
|
|
3983
4025
|
|
|
3984
|
-
|
|
4026
|
+
Class-name mangling is **opt-in** (`production.mangle` defaults to `false`) and is an
|
|
4027
|
+
obfuscation feature, not a size optimization: the runtime mangle map the page must ship
|
|
4028
|
+
costs more bytes than the shorter class names save, so enable it only when the original
|
|
4029
|
+
utility names should not be readable in the production bundle.
|
|
3985
4030
|
|
|
3986
4031
|
```js
|
|
3987
4032
|
// vite.config.ts (production)
|
|
3988
4033
|
...csszyx({ production: { mangle: true } })
|
|
3989
4034
|
```
|
|
3990
4035
|
|
|
3991
|
-
Output: `<div class="z y x" />` — the CSS `.z { padding: 1rem }` etc. is injected
|
|
4036
|
+
Output: `<div class="z y x" />` — the CSS `.z { padding: 1rem }` etc. is injected
|
|
4037
|
+
automatically. `production.mangleMapDelivery` chooses where the runtime map ships
|
|
4038
|
+
(`'both'` default, `'html'`, or `'bundle'`; vite/rollup lanes).
|
|
3992
4039
|
|
|
3993
4040
|
If an `sz`-generated utility also appears as a static string or template quasi in a
|
|
3994
4041
|
source-level `class` or `className` attribute/property (including a `clsx(...)`
|
|
@@ -4002,8 +4049,8 @@ adapter keeps class names readable (and warns when `production.mangle: true` is
|
|
|
4002
4049
|
explicit) because normal esbuild write-to-disk builds do not expose mutable final
|
|
4003
4050
|
assets; source transforms and safelist generation still run.
|
|
4004
4051
|
|
|
4005
|
-
|
|
4006
|
-
inspect the emitted CSS. To check what a single `sz` object
|
|
4052
|
+
With mangling left at its default (off), a build keeps readable class names — the
|
|
4053
|
+
supported way to inspect the emitted CSS. To check what a single `sz` object
|
|
4007
4054
|
compiles to without a build, run `csszyx explain "{ p: 4, bg: 'blue-500' }"`.
|
|
4008
4055
|
|
|
4009
4056
|
### AST budget guard
|
|
@@ -4043,8 +4090,19 @@ ignored unless a path points into them:
|
|
|
4043
4090
|
A non-`/packages/` lib INSIDE the build root needs no config (compiled + scanned
|
|
4044
4091
|
automatically); `compileSources` is for `/packages/` or sources OUTSIDE the root.
|
|
4045
4092
|
|
|
4046
|
-
|
|
4047
|
-
|
|
4093
|
+
The option used to be `compilePackages`, which took package NAMES. An old config
|
|
4094
|
+
still passing that name is never read, so every effect below silently does not
|
|
4095
|
+
happen. csszyx now warns on any plugin option it does not recognize, naming the
|
|
4096
|
+
replacement.
|
|
4097
|
+
|
|
4098
|
+
A `/packages/` file using csszyx that is not opted in is skipped (no CSS); csszyx
|
|
4099
|
+
warns at build end and lists those files.
|
|
4100
|
+
|
|
4101
|
+
Opting in is also what makes the cross-module `szv` precompile work inside the
|
|
4102
|
+
package: the prescan is what records a module's exported factories, so a skipped
|
|
4103
|
+
module keeps them out of the registry and every importer — its own siblings
|
|
4104
|
+
included — falls back to the runtime path. That case warns in production builds
|
|
4105
|
+
too; a skip affecting only one file's own classes stays development-only.
|
|
4048
4106
|
|
|
4049
4107
|
## Theme Auto-Scan (Custom Tokens → TypeScript Types)
|
|
4050
4108
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@csszyx/mcp-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.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": "^4.4.3",
|
|
33
|
-
"@csszyx/
|
|
34
|
-
"@csszyx/
|
|
35
|
-
"@csszyx/
|
|
33
|
+
"@csszyx/compiler": "0.13.0",
|
|
34
|
+
"@csszyx/cli": "0.13.0",
|
|
35
|
+
"@csszyx/unplugin": "0.13.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@types/node": "^22.20.1",
|