@tenphi/tasty 3.3.1 → 3.5.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 +147 -129
- package/dist/astro-ib7E7V4Y.js +307 -0
- package/dist/astro-ib7E7V4Y.js.map +1 -0
- package/dist/{babel-BUQGeOXA.d.ts → babel-DWCsafYw.d.ts} +2 -2
- package/dist/{collector-DUaHCcTS.d.ts → collector-BTVXj2hS.d.ts} +21 -3
- package/dist/{collector-BKqNBmzA.js → collector-C6TtL8HJ.js} +36 -13
- package/dist/collector-C6TtL8HJ.js.map +1 -0
- package/dist/{config-BCdCTIED.js → config-B3gPdCqd.js} +344 -209
- package/dist/config-B3gPdCqd.js.map +1 -0
- package/dist/{config-YsxGv4tq.d.ts → config-LfIDmVHx.d.ts} +132 -32
- package/dist/core/index.d.ts +5 -5
- package/dist/core/index.js +6 -6
- package/dist/{core-wxP3GHQu.js → core-Bq7w2kti.js} +219 -98
- package/dist/core-Bq7w2kti.js.map +1 -0
- package/dist/{css-writer-D64NY9AX.js → css-writer-Bgowobbx.js} +3 -3
- package/dist/{css-writer-D64NY9AX.js.map → css-writer-Bgowobbx.js.map} +1 -1
- package/dist/{format-rules-Bo_e2u7r.js → format-rules-rCZ37rqY.js} +2 -2
- package/dist/format-rules-rCZ37rqY.js.map +1 -0
- package/dist/{hydrate-CMKOuKAx.js → hydrate-CNOmZprz.js} +3 -3
- package/dist/{hydrate-CMKOuKAx.js.map → hydrate-CNOmZprz.js.map} +1 -1
- package/dist/{index-DhhUI0yi.d.ts → index-Bm9ltvdH.d.ts} +45 -16
- package/dist/{index-Cd45t5NM.d.ts → index-BmogEzTo.d.ts} +69 -15
- package/dist/index.d.ts +5 -5
- package/dist/index.js +9 -8
- package/dist/index.js.map +1 -1
- package/dist/{keyframes-D737PShJ.js → keyframes-DE-OE76F.js} +132 -21
- package/dist/keyframes-DE-OE76F.js.map +1 -0
- package/dist/{merge-styles-CU7JbEwg.d.ts → merge-styles-BcLl1u_d.d.ts} +2 -2
- package/dist/{merge-styles-CUIQcs5v.js → merge-styles-DuoZEsm9.js} +2 -2
- package/dist/{merge-styles-CUIQcs5v.js.map → merge-styles-DuoZEsm9.js.map} +1 -1
- package/dist/{resolve-recipes-Df1Ta-Q0.js → resolve-recipes-H9NqOQuP.js} +3 -3
- package/dist/{resolve-recipes-Df1Ta-Q0.js.map → resolve-recipes-H9NqOQuP.js.map} +1 -1
- package/dist/ssr/astro-client.js +1 -1
- package/dist/ssr/astro-middleware-extract-static.d.ts +11 -0
- package/dist/ssr/astro-middleware-extract-static.js +9 -0
- package/dist/ssr/astro-middleware-extract-static.js.map +1 -0
- package/dist/ssr/astro-middleware-extract.d.ts +11 -0
- package/dist/ssr/astro-middleware-extract.js +9 -0
- package/dist/ssr/astro-middleware-extract.js.map +1 -0
- package/dist/ssr/astro-middleware-static.d.ts +3 -1
- package/dist/ssr/astro-middleware-static.js +1 -1
- package/dist/ssr/astro-middleware.d.ts +3 -1
- package/dist/ssr/astro-middleware.js +1 -1
- package/dist/ssr/astro.d.ts +37 -3
- package/dist/ssr/astro.js +1 -156
- package/dist/ssr/index.d.ts +2 -2
- package/dist/ssr/index.js +3 -3
- package/dist/ssr/index.js.map +1 -1
- package/dist/ssr/next.d.ts +1 -1
- package/dist/ssr/next.js +4 -4
- package/dist/static/index.d.ts +2 -2
- package/dist/static/index.js +1 -1
- package/dist/zero/babel.d.ts +1 -1
- package/dist/zero/babel.js +4 -4
- package/dist/zero/babel.js.map +1 -1
- package/dist/zero/index.d.ts +1 -1
- package/dist/zero/index.js +1 -1
- package/dist/zero/next.d.ts +1 -1
- package/docs/README.md +13 -8
- package/docs/adoption.md +56 -31
- package/docs/ai-agents.md +78 -77
- package/docs/comparison.md +69 -40
- package/docs/configuration.md +97 -87
- package/docs/debug.md +4 -2
- package/docs/design-system.md +41 -22
- package/docs/dsl.md +225 -195
- package/docs/getting-started.md +60 -39
- package/docs/injector.md +63 -26
- package/docs/methodology.md +42 -36
- package/docs/migration-v3.md +49 -49
- package/docs/plugins.md +37 -33
- package/docs/react-api.md +65 -44
- package/docs/runtime-benchmarks.md +178 -0
- package/docs/ssr.md +99 -61
- package/docs/styles.md +1 -1
- package/docs/tasty-static.md +137 -104
- package/package.json +26 -8
- package/dist/collector-BKqNBmzA.js.map +0 -1
- package/dist/config-BCdCTIED.js.map +0 -1
- package/dist/core-wxP3GHQu.js.map +0 -1
- package/dist/format-rules-Bo_e2u7r.js.map +0 -1
- package/dist/keyframes-D737PShJ.js.map +0 -1
- package/dist/ssr/astro.js.map +0 -1
package/docs/dsl.md
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# Style DSL Reference
|
|
2
2
|
|
|
3
|
-
This is the Tasty
|
|
3
|
+
This is the language behind Tasty’s predictable state resolution. Every property can use a state map; later branches declare higher priority, and Tasty compiles them so only one generated selector can match.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
The same value syntax, state maps, tokens, units, extension semantics, and special declarations apply to React `tasty()`—whether rendered on the client or server—and build-time `tastyStatic()`.
|
|
6
|
+
|
|
7
|
+
For the React API (`tasty()`, style functions, component props), see [React API](react-api.md). For all enhanced style properties, see [Style Properties](styles.md). For global configuration, see [Configuration](configuration.md).
|
|
6
8
|
|
|
7
9
|
---
|
|
8
10
|
|
|
@@ -18,17 +20,28 @@ fill: { '': '#white', hovered: '#gray.05', 'theme=danger': '#red' }
|
|
|
18
20
|
|
|
19
21
|
#### State Key Types
|
|
20
22
|
|
|
21
|
-
| Syntax
|
|
22
|
-
|
|
23
|
-
| Boolean modifier
|
|
24
|
-
| Value modifier
|
|
25
|
-
| Pseudo-class
|
|
26
|
-
| Class selector
|
|
27
|
-
| Attribute selector | `[aria-expanded="true"]` | `[aria-expanded="true"]`
|
|
28
|
-
| Combined (AND)
|
|
29
|
-
| Combined (OR)
|
|
30
|
-
| Negated (NOT)
|
|
31
|
-
| Exclusive (XOR)
|
|
23
|
+
| Syntax | Example | Generated CSS |
|
|
24
|
+
| ------------------ | ------------------------ | ------------------------------------------------------------------------ |
|
|
25
|
+
| Boolean modifier | `hovered` | `[data-hovered]` |
|
|
26
|
+
| Value modifier | `theme=danger` | `[data-theme="danger"]` |
|
|
27
|
+
| Pseudo-class | `:hover` | `:hover` |
|
|
28
|
+
| Class selector | `.active` | `.active` |
|
|
29
|
+
| Attribute selector | `[aria-expanded="true"]` | `[aria-expanded="true"]` |
|
|
30
|
+
| Combined (AND) | `hovered & .active` | `[data-hovered].active` |
|
|
31
|
+
| Combined (OR) | `hovered \| focused` | `[data-hovered], [data-focused]` |
|
|
32
|
+
| Negated (NOT) | `!disabled` | `:not([data-disabled])` |
|
|
33
|
+
| Exclusive (XOR) | `hovered ^ focused` | `[data-hovered]:not([data-focused]), :not([data-hovered])[data-focused]` |
|
|
34
|
+
|
|
35
|
+
#### Automatic Native States
|
|
36
|
+
|
|
37
|
+
`disabled` and `checked` are built-in automatic states. On a Tasty component, they track the corresponding native prop or attribute, so these pairs express the same component state:
|
|
38
|
+
|
|
39
|
+
| Concise Tasty state | Explicit attribute selector |
|
|
40
|
+
| ------------------- | --------------------------- |
|
|
41
|
+
| `disabled` | `[disabled]` |
|
|
42
|
+
| `checked` | `[checked]` |
|
|
43
|
+
|
|
44
|
+
Prefer the concise form when authoring Tasty component styles. Explicit attribute selectors remain valid and can be useful when targeting arbitrary attributes or when CSS-shaped syntax makes an introductory example easier to recognize.
|
|
32
45
|
|
|
33
46
|
Operator precedence (highest to lowest): `!` (NOT) > `^` (XOR) > `|` (OR) > `&` (AND). Use parentheses to override: `hovered & (pressed ^ focused)`.
|
|
34
47
|
|
|
@@ -53,14 +66,18 @@ color: { '': '#text', hovered: '#accent' }
|
|
|
53
66
|
color: { hovered: '#accent', '': '#text' }
|
|
54
67
|
```
|
|
55
68
|
|
|
56
|
-
The bare `''` default still **receives negation** (it is turned off when a higher-priority state matches). For a value that must always apply as a guaranteed floor — even where a query is
|
|
69
|
+
The bare `''` default still **receives negation** (it is turned off when a higher-priority state matches). For a value that must always apply as a guaranteed floor — even where a query is _unknown_ — use the [`_` fallback floor](#_--fallback-floor) instead. The two can coexist: `''` is the negated default, `_` is the always-on floor. If a map contains only `_` and `''` (no other states), the `''` default is redundant — Tasty keeps the `_` value and drops `''` with a `REDUNDANT_DEFAULT_STATE` warning.
|
|
57
70
|
|
|
58
71
|
### Sub-element
|
|
59
72
|
|
|
60
73
|
Element styled using a capitalized key. Identified by `data-element` attribute:
|
|
61
74
|
|
|
62
75
|
```jsx
|
|
63
|
-
styles: {
|
|
76
|
+
styles: {
|
|
77
|
+
Title: {
|
|
78
|
+
preset: 'h3';
|
|
79
|
+
}
|
|
80
|
+
}
|
|
64
81
|
// Targets: <div data-element="Title">
|
|
65
82
|
```
|
|
66
83
|
|
|
@@ -70,19 +87,19 @@ Control how a sub-element selector attaches to the root selector using the `$` p
|
|
|
70
87
|
|
|
71
88
|
Examples below assume the sub-element key is **Cell** (i.e. `[data-element="Cell"]` in CSS):
|
|
72
89
|
|
|
73
|
-
| Pattern
|
|
74
|
-
|
|
75
|
-
|
|
|
76
|
-
| `>`
|
|
77
|
-
| `>Body>`
|
|
78
|
-
| `> Cell`
|
|
79
|
-
| `h1`
|
|
80
|
-
| `h1 >`
|
|
81
|
-
| `h1 *`
|
|
82
|
-
| `*`
|
|
83
|
-
| `&::before` | `::before`
|
|
84
|
-
| `@::before` | `[data-element="Cell"]::before`
|
|
85
|
-
| `>@.active` | `> [data-element="Cell"].active`
|
|
90
|
+
| Pattern | Result | Description |
|
|
91
|
+
| ----------- | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
92
|
+
| _(none)_ | `[data-element="Cell"]` | Descendant (default) |
|
|
93
|
+
| `>` | `> [data-element="Cell"]` | Direct child |
|
|
94
|
+
| `>Body>` | `> [data-element="Body"] > [data-element="Cell"]` | Chained elements |
|
|
95
|
+
| `> Cell` | `> [data-element="Cell"]` | Self-name shorthand — when the trailing element name matches the sub-element's own key, it acts as the placeholder (same as `@`); no duplication |
|
|
96
|
+
| `h1` | `h1` | Tag selector (no key injection) |
|
|
97
|
+
| `h1 >` | `h1 > [data-element="Cell"]` | Key is direct child of tag |
|
|
98
|
+
| `h1 *` | `h1 *` | Any descendant of tag |
|
|
99
|
+
| `*` | `*` | All descendants |
|
|
100
|
+
| `&::before` | `::before` | Root pseudo (no key); `&` is required |
|
|
101
|
+
| `@::before` | `[data-element="Cell"]::before` | Pseudo on the sub-element |
|
|
102
|
+
| `>@.active` | `> [data-element="Cell"].active` | Class on the sub-element |
|
|
86
103
|
|
|
87
104
|
Rules for key injection (`[data-element="..."]`):
|
|
88
105
|
|
|
@@ -111,7 +128,7 @@ const List = tasty({
|
|
|
111
128
|
Named color prefixed with `#` that maps to CSS custom properties. Supports opacity with `.N` suffix:
|
|
112
129
|
|
|
113
130
|
```jsx
|
|
114
|
-
fill: '#purple.5'
|
|
131
|
+
fill: '#purple.5'; // → oklch(from var(--purple-color) l c h / .5)
|
|
115
132
|
```
|
|
116
133
|
|
|
117
134
|
### Modifier
|
|
@@ -146,7 +163,7 @@ fill: '#purple.5';
|
|
|
146
163
|
```
|
|
147
164
|
|
|
148
165
|
The channels are copied over and the alpha slot is written, which asks nothing of
|
|
149
|
-
the color beyond
|
|
166
|
+
the color beyond _being_ a color. That means the suffix works on every one of
|
|
150
167
|
these:
|
|
151
168
|
|
|
152
169
|
- a token holding a `color-mix()`, a `light-dark()`, or a `color()` in a space
|
|
@@ -171,7 +188,7 @@ that expresses its disabled state once, in `color`, fades everything painted fro
|
|
|
171
188
|
`#current` below it with no further declarations.
|
|
172
189
|
|
|
173
190
|
The `color` style also publishes `--current-color` beside every color it sets,
|
|
174
|
-
for anything that needs the inherited color
|
|
191
|
+
for anything that needs the inherited color _as a color_ rather than as the
|
|
175
192
|
keyword — hand-authored CSS, or a place the keyword will not do. Read it as
|
|
176
193
|
`$current-color`. It is registered with `initial-value: currentcolor`, so where
|
|
177
194
|
nothing published it, it still resolves against each element's own color:
|
|
@@ -184,7 +201,7 @@ A color that already reads the color it inherits — `#current`, a `#current` fa
|
|
|
184
201
|
— is not published into it: resolving such a value a second time at a descendant
|
|
185
202
|
would fade it twice.
|
|
186
203
|
|
|
187
|
-
One case needs the wider floor to be considered. A token
|
|
204
|
+
One case needs the wider floor to be considered. A token _defined_ as `#current`
|
|
188
205
|
and then faded gives relative color syntax a `currentcolor` origin, which Safari
|
|
189
206
|
supports only from 18:
|
|
190
207
|
|
|
@@ -200,7 +217,7 @@ through `color-mix()`, which works from Safari 16.2 and composes like every othe
|
|
|
200
217
|
|
|
201
218
|
### `#current` composes instead
|
|
202
219
|
|
|
203
|
-
`#current` is the one exception, and the difference is deliberate. A token
|
|
220
|
+
`#current` is the one exception, and the difference is deliberate. A token _names_
|
|
204
221
|
a color, so fading it sets its alpha. `currentcolor` is the color an element
|
|
205
222
|
**inherits**, which an ancestor may already have faded — `#current.4` means "40%
|
|
206
223
|
of what reaches me":
|
|
@@ -266,22 +283,22 @@ configure({
|
|
|
266
283
|
},
|
|
267
284
|
});
|
|
268
285
|
|
|
269
|
-
fill: '#solid.5';
|
|
270
|
-
fill: '#adaptive.5';
|
|
286
|
+
fill: '#solid.5'; // → hsl(220 90% 50% / .5)
|
|
287
|
+
fill: '#adaptive.5'; // → oklch(from light-dark(…) l c h / .5)
|
|
271
288
|
```
|
|
272
289
|
|
|
273
290
|
---
|
|
274
291
|
|
|
275
292
|
## Built-in Units
|
|
276
293
|
|
|
277
|
-
| Unit | Description
|
|
278
|
-
|
|
279
|
-
| `x`
|
|
280
|
-
| `r`
|
|
281
|
-
| `cr` | Card border radius | `1cr`
|
|
282
|
-
| `bw` | Border width
|
|
283
|
-
| `ow` | Outline width
|
|
284
|
-
| `sf` | Stable fraction
|
|
294
|
+
| Unit | Description | Example | CSS Output |
|
|
295
|
+
| ---- | ------------------ | ------- | ------------------------------- |
|
|
296
|
+
| `x` | Gap multiplier | `2x` | `calc(var(--gap) * 2)` |
|
|
297
|
+
| `r` | Border radius | `1r` | `var(--radius)` |
|
|
298
|
+
| `cr` | Card border radius | `1cr` | `var(--card-radius)` |
|
|
299
|
+
| `bw` | Border width | `2bw` | `calc(var(--border-width) * 2)` |
|
|
300
|
+
| `ow` | Outline width | `1ow` | `var(--outline-width)` |
|
|
301
|
+
| `sf` | Stable fraction | `1sf` | `minmax(0, 1fr)` |
|
|
285
302
|
|
|
286
303
|
You can register additional custom units via [`configure()`](configuration.md#options).
|
|
287
304
|
|
|
@@ -326,7 +343,7 @@ const ElevatedCard = tasty({
|
|
|
326
343
|
|
|
327
344
|
**Post-merge recipes (`/` separator):**
|
|
328
345
|
|
|
329
|
-
Recipes listed after `/` are applied
|
|
346
|
+
Recipes listed after `/` are applied _after_ component styles using `mergeStyles`:
|
|
330
347
|
|
|
331
348
|
```jsx
|
|
332
349
|
const Input = tasty({
|
|
@@ -364,8 +381,8 @@ When a style property uses a state map, the merge behavior depends on whether th
|
|
|
364
381
|
const MyButton = tasty(Button, {
|
|
365
382
|
styles: {
|
|
366
383
|
fill: {
|
|
367
|
-
|
|
368
|
-
|
|
384
|
+
loading: '#yellow', // append new state
|
|
385
|
+
disabled: '#gray.20', // override existing state in place
|
|
369
386
|
},
|
|
370
387
|
},
|
|
371
388
|
});
|
|
@@ -375,7 +392,7 @@ const MyButton = tasty(Button, {
|
|
|
375
392
|
styles: {
|
|
376
393
|
fill: {
|
|
377
394
|
'': '#red',
|
|
378
|
-
|
|
395
|
+
hovered: '#blue',
|
|
379
396
|
},
|
|
380
397
|
},
|
|
381
398
|
});
|
|
@@ -400,8 +417,12 @@ fill: {
|
|
|
400
417
|
Use `null` inside a state map to remove a state, or `false` to block it entirely (tombstone):
|
|
401
418
|
|
|
402
419
|
```jsx
|
|
403
|
-
fill: {
|
|
404
|
-
|
|
420
|
+
fill: {
|
|
421
|
+
pressed: null;
|
|
422
|
+
} // removes pressed from the result
|
|
423
|
+
fill: {
|
|
424
|
+
disabled: false;
|
|
425
|
+
} // tombstone — no CSS for disabled, blocks recipe too
|
|
405
426
|
```
|
|
406
427
|
|
|
407
428
|
### Resetting Properties with `null` and `false`
|
|
@@ -409,36 +430,36 @@ fill: { disabled: false } // tombstone — no CSS for disabled, blocks recipe to
|
|
|
409
430
|
```jsx
|
|
410
431
|
const SimpleButton = tasty(Button, {
|
|
411
432
|
styles: {
|
|
412
|
-
fill: null,
|
|
433
|
+
fill: null, // discard parent's fill, let recipe fill in
|
|
413
434
|
border: false, // no border at all (tombstone — blocks recipe too)
|
|
414
435
|
},
|
|
415
436
|
});
|
|
416
437
|
```
|
|
417
438
|
|
|
418
|
-
| Value
|
|
419
|
-
|
|
420
|
-
| `undefined` | Not provided — parent preserved
|
|
421
|
-
| `null`
|
|
422
|
-
| `false`
|
|
439
|
+
| Value | Meaning | Recipe fills in? |
|
|
440
|
+
| ----------- | ------------------------------------ | ---------------- |
|
|
441
|
+
| `undefined` | Not provided — parent preserved | N/A |
|
|
442
|
+
| `null` | Intentional unset — parent discarded | Yes |
|
|
443
|
+
| `false` | Tombstone — blocks everything | No |
|
|
423
444
|
|
|
424
445
|
---
|
|
425
446
|
|
|
426
447
|
## Advanced States (`@` prefix)
|
|
427
448
|
|
|
428
|
-
| Prefix
|
|
429
|
-
|
|
430
|
-
| `@media`
|
|
431
|
-
| `@(...)`
|
|
432
|
-
| `@supports` | Feature/selector support
|
|
433
|
-
| `@root`
|
|
434
|
-
| `@parent`
|
|
435
|
-
| `@own`
|
|
436
|
-
| `@starting` | Entry animation
|
|
437
|
-
| `_`
|
|
438
|
-
| `:is()`
|
|
439
|
-
| `:has()`
|
|
440
|
-
| `:not()`
|
|
441
|
-
| `:where()`
|
|
449
|
+
| Prefix | Purpose | Example |
|
|
450
|
+
| ----------- | ----------------------------------------- | -------------------------- |
|
|
451
|
+
| `@media` | Media queries | `@media(w < 768px)` |
|
|
452
|
+
| `@(...)` | Container queries | `@(panel, w >= 300px)` |
|
|
453
|
+
| `@supports` | Feature/selector support | `@supports(display: grid)` |
|
|
454
|
+
| `@root` | Root element states | `@root(schema=dark)` |
|
|
455
|
+
| `@parent` | Parent/ancestor element states | `@parent(hovered)` |
|
|
456
|
+
| `@own` | Sub-element's own state | `@own(hovered)` |
|
|
457
|
+
| `@starting` | Entry animation | `@starting` |
|
|
458
|
+
| `_` | Fallback floor (always-on, never negated) | `_` |
|
|
459
|
+
| `:is()` | CSS `:is()` structural pseudo-class | `:is(fieldset > label)` |
|
|
460
|
+
| `:has()` | CSS `:has()` relational pseudo-class | `:has(> Icon)` |
|
|
461
|
+
| `:not()` | CSS `:not()` negation (prefer `!:is()`) | `:not(:first-child)` |
|
|
462
|
+
| `:where()` | CSS `:where()` (zero specificity) | `:where(Section)` |
|
|
442
463
|
|
|
443
464
|
> **Specificity.** All state selectors Tasty generates (modifiers, pseudo-classes,
|
|
444
465
|
> `:is()`/`:not()` groups, and `@root` / `@parent` context) are wrapped in
|
|
@@ -454,9 +475,9 @@ const SimpleButton = tasty(Button, {
|
|
|
454
475
|
Media queries support dimension shorthands and custom unit expansion:
|
|
455
476
|
|
|
456
477
|
| Shorthand | Expands to |
|
|
457
|
-
|
|
458
|
-
| `w`
|
|
459
|
-
| `h`
|
|
478
|
+
| --------- | ---------- |
|
|
479
|
+
| `w` | `width` |
|
|
480
|
+
| `h` | `height` |
|
|
460
481
|
|
|
461
482
|
```jsx
|
|
462
483
|
fill: {
|
|
@@ -467,14 +488,14 @@ fill: {
|
|
|
467
488
|
}
|
|
468
489
|
```
|
|
469
490
|
|
|
470
|
-
| Tasty syntax
|
|
471
|
-
|
|
472
|
-
| `@media(w < 768px)`
|
|
473
|
-
| `@media(600px <= w < 1200px)`
|
|
474
|
-
| `@media:print`
|
|
475
|
-
| `@media:screen`
|
|
491
|
+
| Tasty syntax | CSS output |
|
|
492
|
+
| ------------------------------------ | ------------------------------------- |
|
|
493
|
+
| `@media(w < 768px)` | `@media (width < 768px)` |
|
|
494
|
+
| `@media(600px <= w < 1200px)` | `@media (600px <= width < 1200px)` |
|
|
495
|
+
| `@media:print` | `@media print` |
|
|
496
|
+
| `@media:screen` | `@media screen` |
|
|
476
497
|
| `@media(prefers-color-scheme: dark)` | `@media (prefers-color-scheme: dark)` |
|
|
477
|
-
| `@media(prefers-reduced-motion)`
|
|
498
|
+
| `@media(prefers-reduced-motion)` | `@media (prefers-reduced-motion)` |
|
|
478
499
|
|
|
479
500
|
Custom units work inside media queries: `@media(w < 40x)` → `@media (width < calc(var(--gap) * 40))`.
|
|
480
501
|
|
|
@@ -484,12 +505,12 @@ In practice, define state aliases via `configure({ states })` and use `@mobile`
|
|
|
484
505
|
|
|
485
506
|
Container queries use the syntax `@(name, condition)` for named containers or `@(condition)` for the nearest ancestor container. Dimension shorthands (`w`, `h`, `is`, `bs`) are expanded the same way as `@media`.
|
|
486
507
|
|
|
487
|
-
| Shorthand | Expands to
|
|
488
|
-
|
|
489
|
-
| `w`
|
|
490
|
-
| `h`
|
|
491
|
-
| `is`
|
|
492
|
-
| `bs`
|
|
508
|
+
| Shorthand | Expands to |
|
|
509
|
+
| --------- | ------------- |
|
|
510
|
+
| `w` | `width` |
|
|
511
|
+
| `h` | `height` |
|
|
512
|
+
| `is` | `inline-size` |
|
|
513
|
+
| `bs` | `block-size` |
|
|
493
514
|
|
|
494
515
|
```jsx
|
|
495
516
|
const Panel = tasty({
|
|
@@ -502,14 +523,14 @@ const Panel = tasty({
|
|
|
502
523
|
});
|
|
503
524
|
```
|
|
504
525
|
|
|
505
|
-
| Tasty syntax
|
|
506
|
-
|
|
507
|
-
| `@(layout, w < 600px)`
|
|
508
|
-
| `@(w < 600px)`
|
|
509
|
-
| `@(layout, $variant=danger)`
|
|
510
|
-
| `@(layout, $compact)`
|
|
511
|
-
| `@(scroll-state(stuck: top))`
|
|
512
|
-
| `@(nav, scroll-state(stuck: top))` | `@container nav scroll-state(stuck: top)`
|
|
526
|
+
| Tasty syntax | CSS output |
|
|
527
|
+
| ---------------------------------- | ---------------------------------------------- |
|
|
528
|
+
| `@(layout, w < 600px)` | `@container layout (width < 600px)` |
|
|
529
|
+
| `@(w < 600px)` | `@container (width < 600px)` |
|
|
530
|
+
| `@(layout, $variant=danger)` | `@container layout style(--variant: "danger")` |
|
|
531
|
+
| `@(layout, $compact)` | `@container layout style(--compact)` |
|
|
532
|
+
| `@(scroll-state(stuck: top))` | `@container scroll-state(stuck: top)` |
|
|
533
|
+
| `@(nav, scroll-state(stuck: top))` | `@container nav scroll-state(stuck: top)` |
|
|
513
534
|
|
|
514
535
|
Container style queries use `$prop` (boolean) or `$prop=value` syntax, which maps to CSS `style(--prop)` or `style(--prop: "value")`.
|
|
515
536
|
|
|
@@ -517,10 +538,10 @@ Container style queries use `$prop` (boolean) or `$prop=value` syntax, which map
|
|
|
517
538
|
|
|
518
539
|
Feature queries test CSS property support. Use `$` as the first argument to test selector support:
|
|
519
540
|
|
|
520
|
-
| Tasty syntax
|
|
521
|
-
|
|
522
|
-
| `@supports(display: grid)`
|
|
523
|
-
| `@supports($, :has(*))`
|
|
541
|
+
| Tasty syntax | CSS output |
|
|
542
|
+
| --------------------------- | --------------------------------- |
|
|
543
|
+
| `@supports(display: grid)` | `@supports (display: grid)` |
|
|
544
|
+
| `@supports($, :has(*))` | `@supports selector(:has(*))` |
|
|
524
545
|
| `!@supports(display: grid)` | `@supports (not (display: grid))` |
|
|
525
546
|
|
|
526
547
|
```jsx
|
|
@@ -539,7 +560,7 @@ that assumption: `@supports(...)` and `@(...)` queries can be **unknown** (not
|
|
|
539
560
|
just true/false), and `not(unknown)` is also unknown — so a negated default
|
|
540
561
|
branch silently never applies. The classic case is `scroll-state`: a browser can
|
|
541
562
|
support `container-type: scroll-state` while a specific `scroll-state(...)` query
|
|
542
|
-
is unknown, leaving
|
|
563
|
+
is unknown, leaving _no_ branch active.
|
|
543
564
|
|
|
544
565
|
The `_` fallback floor solves this. Use `_` as a **standalone key** and its value
|
|
545
566
|
**always applies** as a guaranteed floor: it never receives negation, and
|
|
@@ -554,10 +575,14 @@ inset: {
|
|
|
554
575
|
```
|
|
555
576
|
|
|
556
577
|
```css
|
|
557
|
-
.t0.t0 {
|
|
578
|
+
.t0.t0 {
|
|
579
|
+
inset: 0...;
|
|
580
|
+
}
|
|
558
581
|
@container scroll-state(scrolled: block-end) {
|
|
559
582
|
@supports (container-type: scroll-state) {
|
|
560
|
-
.t0.t0 {
|
|
583
|
+
.t0.t0 {
|
|
584
|
+
inset: -80px...;
|
|
585
|
+
}
|
|
561
586
|
}
|
|
562
587
|
}
|
|
563
588
|
```
|
|
@@ -588,13 +613,13 @@ cause is always the same: **`''` is mutually exclusive (turned off by negation),
|
|
|
588
613
|
while `_` is always on (layered underneath).**
|
|
589
614
|
|
|
590
615
|
- **Unknown / invalid branches.** If a higher-priority branch sits behind a query
|
|
591
|
-
that can be
|
|
616
|
+
that can be _unknown_ (`@supports`, container, `scroll-state`) or uses a
|
|
592
617
|
selector the browser drops, the negated `''` default disappears along with it
|
|
593
618
|
(`not(unknown) = unknown`), leaving the property with no rule. The `_` floor
|
|
594
619
|
survives because it is an unconditional bare rule. This is the case `_` exists
|
|
595
620
|
for.
|
|
596
621
|
|
|
597
|
-
- **Empty / "unset" states.** A state can intentionally produce
|
|
622
|
+
- **Empty / "unset" states.** A state can intentionally produce _no_ output (an
|
|
598
623
|
empty value) to leave a property unset while that state is active. With `''`
|
|
599
624
|
this works — the default is negated away and nothing replaces it, so the
|
|
600
625
|
property unsets. With `_` the floor can never be turned off, so its value keeps
|
|
@@ -606,14 +631,14 @@ while `_` is always on (layered underneath).**
|
|
|
606
631
|
```
|
|
607
632
|
|
|
608
633
|
- **States with different output shapes.** Because `_` layers additively, when
|
|
609
|
-
different states emit
|
|
634
|
+
different states emit _different sets_ of declarations, the floor's
|
|
610
635
|
declarations bleed through wherever the winning state does not override the
|
|
611
636
|
same property — which can produce inconsistent combinations. A `''` default is
|
|
612
637
|
swapped out cleanly by its mutually-exclusive condition.
|
|
613
638
|
|
|
614
639
|
Rule of thumb: **use `''` for the normal default** (clean mutual exclusivity,
|
|
615
640
|
supports unsetting), and reach for `_` only when a value must survive an
|
|
616
|
-
|
|
641
|
+
_unknown_ higher-priority branch.
|
|
617
642
|
|
|
618
643
|
### `@root(...)` — Root Element States
|
|
619
644
|
|
|
@@ -629,13 +654,13 @@ color: {
|
|
|
629
654
|
}
|
|
630
655
|
```
|
|
631
656
|
|
|
632
|
-
| Tasty syntax
|
|
633
|
-
|
|
634
|
-
| `@root(schema=dark)`
|
|
635
|
-
| `@root(hovered)`
|
|
636
|
-
| `@root(.premium-user)` | `:root.premium-user`
|
|
637
|
-
| `@root([lang="en"])`
|
|
638
|
-
| `!@root(schema=dark)`
|
|
657
|
+
| Tasty syntax | CSS selector |
|
|
658
|
+
| ---------------------- | --------------------------------- |
|
|
659
|
+
| `@root(schema=dark)` | `:root[data-schema="dark"]` |
|
|
660
|
+
| `@root(hovered)` | `:root[data-hovered]` |
|
|
661
|
+
| `@root(.premium-user)` | `:root.premium-user` |
|
|
662
|
+
| `@root([lang="en"])` | `:root[lang="en"]` |
|
|
663
|
+
| `!@root(schema=dark)` | `:root:not([data-schema="dark"])` |
|
|
639
664
|
|
|
640
665
|
Root conditions are prepended to the component selector: `:root[data-schema="dark"] .t0.t0 { ... }`.
|
|
641
666
|
|
|
@@ -651,7 +676,7 @@ const Nav = tasty({
|
|
|
651
676
|
'': '#text',
|
|
652
677
|
'@own(:hover)': '#primary',
|
|
653
678
|
'@own(:focus-visible)': '#primary',
|
|
654
|
-
|
|
679
|
+
selected: '#primary', // root-level modifier
|
|
655
680
|
},
|
|
656
681
|
},
|
|
657
682
|
},
|
|
@@ -659,11 +684,11 @@ const Nav = tasty({
|
|
|
659
684
|
});
|
|
660
685
|
```
|
|
661
686
|
|
|
662
|
-
| Tasty syntax (inside sub-element) | CSS output
|
|
663
|
-
|
|
664
|
-
| `@own(:hover)`
|
|
665
|
-
| `@own(hovered)`
|
|
666
|
-
| `@own(theme=dark)`
|
|
687
|
+
| Tasty syntax (inside sub-element) | CSS output |
|
|
688
|
+
| --------------------------------- | ------------------------------------------------- |
|
|
689
|
+
| `@own(:hover)` | `:hover` on the sub-element selector |
|
|
690
|
+
| `@own(hovered)` | `[data-hovered]` on the sub-element selector |
|
|
691
|
+
| `@own(theme=dark)` | `[data-theme="dark"]` on the sub-element selector |
|
|
667
692
|
|
|
668
693
|
`@own()` is only valid inside sub-element styles. Using it on root styles emits a warning and is treated as a regular modifier.
|
|
669
694
|
|
|
@@ -681,9 +706,9 @@ const FadeIn = tasty({
|
|
|
681
706
|
});
|
|
682
707
|
```
|
|
683
708
|
|
|
684
|
-
| Tasty syntax | CSS output
|
|
685
|
-
|
|
686
|
-
| `@starting`
|
|
709
|
+
| Tasty syntax | CSS output |
|
|
710
|
+
| ------------ | ------------------------------------ |
|
|
711
|
+
| `@starting` | `@starting-style { .t0.t0 { ... } }` |
|
|
687
712
|
|
|
688
713
|
### `@parent(...)` — Parent Element States
|
|
689
714
|
|
|
@@ -694,22 +719,22 @@ const Highlight = tasty({
|
|
|
694
719
|
styles: {
|
|
695
720
|
fill: {
|
|
696
721
|
'': '#white',
|
|
697
|
-
'@parent(hovered)': '#gray.05',
|
|
698
|
-
'@parent(theme=dark, >)': '#dark-02',
|
|
722
|
+
'@parent(hovered)': '#gray.05', // Any ancestor has [data-hovered]
|
|
723
|
+
'@parent(theme=dark, >)': '#dark-02', // Direct parent has [data-theme="dark"]
|
|
699
724
|
},
|
|
700
725
|
},
|
|
701
726
|
});
|
|
702
727
|
```
|
|
703
728
|
|
|
704
|
-
| Syntax
|
|
705
|
-
|
|
706
|
-
| `@parent(hovered)`
|
|
707
|
-
| `!@parent(hovered)`
|
|
708
|
-
| `@parent(hovered, >)`
|
|
709
|
-
| `@parent(.active)`
|
|
710
|
-
| `@parent(hovered & focused)`
|
|
729
|
+
| Syntax | CSS Output |
|
|
730
|
+
| ------------------------------------- | -------------------------------------------------------------------- |
|
|
731
|
+
| `@parent(hovered)` | `:is([data-hovered] *)` |
|
|
732
|
+
| `!@parent(hovered)` | `:not([data-hovered] *)` |
|
|
733
|
+
| `@parent(hovered, >)` | `:is([data-hovered] > *)` (direct parent) |
|
|
734
|
+
| `@parent(.active)` | `:is(.active *)` |
|
|
735
|
+
| `@parent(hovered & focused)` | `:is([data-hovered][data-focused] *)` (same ancestor) |
|
|
711
736
|
| `@parent(hovered) & @parent(focused)` | `:is([data-hovered] *):is([data-focused] *)` (independent ancestors) |
|
|
712
|
-
| `@parent(hovered \| focused)`
|
|
737
|
+
| `@parent(hovered \| focused)` | `:is([data-hovered] *, [data-focused] *)` (OR inside single wrapper) |
|
|
713
738
|
|
|
714
739
|
For sub-elements, the parent check applies to the root element's ancestors:
|
|
715
740
|
|
|
@@ -740,38 +765,38 @@ const Card = tasty({
|
|
|
740
765
|
styles: {
|
|
741
766
|
display: {
|
|
742
767
|
'': 'block',
|
|
743
|
-
':has(> Icon)': 'grid',
|
|
744
|
-
':has(+ Icon)': 'grid',
|
|
745
|
-
':has(~ Icon)': 'grid',
|
|
746
|
-
':has(Icon +)': 'grid',
|
|
747
|
-
':has(Icon ~)': 'grid',
|
|
748
|
-
':is(fieldset > label)': 'inline',
|
|
749
|
-
'!:has(> Icon)': 'flex',
|
|
768
|
+
':has(> Icon)': 'grid', // has Icon as direct child
|
|
769
|
+
':has(+ Icon)': 'grid', // immediately followed by an Icon sibling
|
|
770
|
+
':has(~ Icon)': 'grid', // has an Icon sibling somewhere after
|
|
771
|
+
':has(Icon +)': 'grid', // immediately preceded by an Icon sibling (auto-completes to `Icon + *`)
|
|
772
|
+
':has(Icon ~)': 'grid', // has an Icon sibling somewhere before (auto-completes to `Icon ~ *`)
|
|
773
|
+
':is(fieldset > label)': 'inline', // is a label inside a fieldset (HTML tags)
|
|
774
|
+
'!:has(> Icon)': 'flex', // negation: no Icon child
|
|
750
775
|
},
|
|
751
776
|
},
|
|
752
777
|
});
|
|
753
778
|
```
|
|
754
779
|
|
|
755
|
-
| Syntax
|
|
756
|
-
|
|
757
|
-
| `:has(> Icon)`
|
|
758
|
-
| `:has(+ Icon)`
|
|
759
|
-
| `:has(~ Icon)`
|
|
760
|
-
| `:has(Icon +)`
|
|
761
|
-
| `:has(Icon ~)`
|
|
762
|
-
| `:has(>)`
|
|
763
|
-
| `:is(> Field + input)` | `:is(> [data-element="Field"] + input)` | Structural match
|
|
764
|
-
| `:has(button)`
|
|
765
|
-
| `!:has(> Icon)`
|
|
766
|
-
| `!:is(Panel)`
|
|
780
|
+
| Syntax | CSS Output | Meaning |
|
|
781
|
+
| ---------------------- | --------------------------------------- | --------------------------------------- |
|
|
782
|
+
| `:has(> Icon)` | `:has(> [data-element="Icon"])` | Has Icon as direct child |
|
|
783
|
+
| `:has(+ Icon)` | `:has(+ [data-element="Icon"])` | Immediately followed by an Icon sibling |
|
|
784
|
+
| `:has(~ Icon)` | `:has(~ [data-element="Icon"])` | Has an Icon sibling somewhere after |
|
|
785
|
+
| `:has(Icon +)` | `:has([data-element="Icon"] + *)` | Immediately preceded by an Icon sibling |
|
|
786
|
+
| `:has(Icon ~)` | `:has([data-element="Icon"] ~ *)` | Has an Icon sibling somewhere before |
|
|
787
|
+
| `:has(>)` | `:has(> *)` | Has any direct child |
|
|
788
|
+
| `:is(> Field + input)` | `:is(> [data-element="Field"] + input)` | Structural match |
|
|
789
|
+
| `:has(button)` | `:has(button)` | HTML tag (lowercase, unchanged) |
|
|
790
|
+
| `!:has(> Icon)` | `:not(:has(> [data-element="Icon"]))` | Negation (use `!`) |
|
|
791
|
+
| `!:is(Panel)` | `:not([data-element="Panel"])` | Negation (use `!:is`) |
|
|
767
792
|
|
|
768
793
|
Combine with other states using boolean logic (`&`, `|`, `!`, `^`):
|
|
769
794
|
|
|
770
795
|
```jsx
|
|
771
|
-
':has(> Icon) & hovered'
|
|
772
|
-
'@parent(hovered) & :has(> Icon)'
|
|
773
|
-
':has(> Icon) | :has(> Button)'
|
|
774
|
-
':has(> Icon) ^ :has(> Button)'
|
|
796
|
+
':has(> Icon) & hovered'; // AND: structural + data attribute
|
|
797
|
+
'@parent(hovered) & :has(> Icon)'; // AND: parent check + structural
|
|
798
|
+
':has(> Icon) | :has(> Button)'; // OR: either sub-element present
|
|
799
|
+
':has(> Icon) ^ :has(> Button)'; // XOR: exactly one present
|
|
775
800
|
```
|
|
776
801
|
|
|
777
802
|
> **Nesting limit:** The state key parser supports up to 2 levels of nested parentheses inside `:is()`, `:has()`, `:not()`, and `:where()` — e.g. `:has(Input:not(:disabled))` works, but 3+ levels like `:has(:is(:not(:hover)))` will not be tokenized correctly. This covers virtually all practical use cases.
|
|
@@ -807,7 +832,7 @@ const AnimatedGradient = tasty({
|
|
|
807
832
|
styles: {
|
|
808
833
|
'$gradient-angle': '0deg',
|
|
809
834
|
'#theme': 'okhst(280 80% 50%)',
|
|
810
|
-
|
|
835
|
+
image: 'linear-gradient($gradient-angle, #theme, #clear)',
|
|
811
836
|
transition: '$$gradient-angle 0.3s, ##theme 0.3s',
|
|
812
837
|
},
|
|
813
838
|
});
|
|
@@ -838,7 +863,7 @@ const Heading = tasty({
|
|
|
838
863
|
fontDisplay: 'swap',
|
|
839
864
|
},
|
|
840
865
|
},
|
|
841
|
-
|
|
866
|
+
font: '"Brand Sans", sans-serif',
|
|
842
867
|
},
|
|
843
868
|
});
|
|
844
869
|
```
|
|
@@ -858,20 +883,20 @@ Supply an array to register several variants of the same family:
|
|
|
858
883
|
|
|
859
884
|
### Supported descriptors
|
|
860
885
|
|
|
861
|
-
| Descriptor
|
|
862
|
-
|
|
863
|
-
| `src` (required)
|
|
864
|
-
| `fontWeight`
|
|
865
|
-
| `fontStyle`
|
|
866
|
-
| `fontStretch`
|
|
867
|
-
| `fontDisplay`
|
|
868
|
-
| `unicodeRange`
|
|
869
|
-
| `ascentOverride`
|
|
870
|
-
| `descentOverride`
|
|
871
|
-
| `lineGapOverride`
|
|
872
|
-
| `sizeAdjust`
|
|
873
|
-
| `fontFeatureSettings`
|
|
874
|
-
| `fontVariationSettings` | `font-variation-settings` | `string`
|
|
886
|
+
| Descriptor | CSS property | Type |
|
|
887
|
+
| ----------------------- | ------------------------- | --------------------------------------------------------- |
|
|
888
|
+
| `src` (required) | `src` | `string` |
|
|
889
|
+
| `fontWeight` | `font-weight` | `string \| number` |
|
|
890
|
+
| `fontStyle` | `font-style` | `string` |
|
|
891
|
+
| `fontStretch` | `font-stretch` | `string` |
|
|
892
|
+
| `fontDisplay` | `font-display` | `'auto' \| 'block' \| 'swap' \| 'fallback' \| 'optional'` |
|
|
893
|
+
| `unicodeRange` | `unicode-range` | `string` |
|
|
894
|
+
| `ascentOverride` | `ascent-override` | `string` |
|
|
895
|
+
| `descentOverride` | `descent-override` | `string` |
|
|
896
|
+
| `lineGapOverride` | `line-gap-override` | `string` |
|
|
897
|
+
| `sizeAdjust` | `size-adjust` | `string` |
|
|
898
|
+
| `fontFeatureSettings` | `font-feature-settings` | `string` |
|
|
899
|
+
| `fontVariationSettings` | `font-variation-settings` | `string` |
|
|
875
900
|
|
|
876
901
|
> Font-face rules are permanent — they are injected once and never cleaned up, matching how browsers handle `@font-face`.
|
|
877
902
|
|
|
@@ -899,18 +924,18 @@ const EmojiList = tasty({
|
|
|
899
924
|
|
|
900
925
|
### Supported descriptors
|
|
901
926
|
|
|
902
|
-
| Descriptor
|
|
903
|
-
|
|
904
|
-
| `system` (required) | `system`
|
|
905
|
-
| `symbols`
|
|
906
|
-
| `additiveSymbols`
|
|
907
|
-
| `prefix`
|
|
908
|
-
| `suffix`
|
|
909
|
-
| `negative`
|
|
910
|
-
| `range`
|
|
911
|
-
| `pad`
|
|
912
|
-
| `fallback`
|
|
913
|
-
| `speakAs`
|
|
927
|
+
| Descriptor | CSS property | Type |
|
|
928
|
+
| ------------------- | ------------------ | ---------------------------------------------------------------------------------------- |
|
|
929
|
+
| `system` (required) | `system` | `'cyclic' \| 'numeric' \| 'alphabetic' \| 'symbolic' \| 'additive' \| 'fixed' \| string` |
|
|
930
|
+
| `symbols` | `symbols` | `string` |
|
|
931
|
+
| `additiveSymbols` | `additive-symbols` | `string` |
|
|
932
|
+
| `prefix` | `prefix` | `string` |
|
|
933
|
+
| `suffix` | `suffix` | `string` |
|
|
934
|
+
| `negative` | `negative` | `string` |
|
|
935
|
+
| `range` | `range` | `string` |
|
|
936
|
+
| `pad` | `pad` | `string` |
|
|
937
|
+
| `fallback` | `fallback` | `string` |
|
|
938
|
+
| `speakAs` | `speak-as` | `string` |
|
|
914
939
|
|
|
915
940
|
> Counter-style rules are permanent — they are injected once and never cleaned up, matching how browsers handle `@counter-style`.
|
|
916
941
|
|
|
@@ -932,18 +957,18 @@ const Box = tasty({
|
|
|
932
957
|
styles: {
|
|
933
958
|
'@function': {
|
|
934
959
|
// simplest: one bare param + result (auto-calc, no explicit calc())
|
|
935
|
-
|
|
960
|
+
$$negative: { args: ['$value'], result: '(-1 * $value)' },
|
|
936
961
|
|
|
937
962
|
// typed param + default + return type + local variable
|
|
938
|
-
|
|
963
|
+
$$shadow: {
|
|
939
964
|
args: { '$shadow-color': { syntax: '<color>', default: 'inherit' } },
|
|
940
965
|
returns: '<color>',
|
|
941
|
-
|
|
966
|
+
$offset: '2px', // local variable
|
|
942
967
|
result: '$offset $offset ($shadow-color, black)',
|
|
943
968
|
},
|
|
944
969
|
},
|
|
945
|
-
|
|
946
|
-
|
|
970
|
+
margin: '$$negative(10px) top', // → margin: --negative(10px) 0 0 0
|
|
971
|
+
shadow: '$$shadow(#accent)', // → box-shadow: --shadow(var(--accent-color))
|
|
947
972
|
},
|
|
948
973
|
});
|
|
949
974
|
```
|
|
@@ -951,22 +976,27 @@ const Box = tasty({
|
|
|
951
976
|
Generated CSS:
|
|
952
977
|
|
|
953
978
|
```css
|
|
954
|
-
@function --negative(--value) {
|
|
979
|
+
@function --negative(--value) {
|
|
980
|
+
result: calc(-1 * var(--value));
|
|
981
|
+
}
|
|
955
982
|
@function --shadow(--shadow-color <color>: inherit) returns <color> {
|
|
956
983
|
--offset: 2px;
|
|
957
984
|
result: var(--offset) var(--offset) var(--shadow-color, black);
|
|
958
985
|
}
|
|
959
|
-
.t0 {
|
|
986
|
+
.t0 {
|
|
987
|
+
margin: --negative(10px) 0 0 0;
|
|
988
|
+
box-shadow: --shadow(var(--accent-color));
|
|
989
|
+
}
|
|
960
990
|
```
|
|
961
991
|
|
|
962
992
|
### Descriptor shape
|
|
963
993
|
|
|
964
|
-
| Field
|
|
965
|
-
|
|
966
|
-
| `result` (required) | `string`
|
|
967
|
-
| `args`
|
|
968
|
-
| `returns`
|
|
969
|
-
| `$name` keys
|
|
994
|
+
| Field | Type | Notes |
|
|
995
|
+
| ------------------- | ------------------------------------------------- | ---------------------------------------------------------------------------------------- |
|
|
996
|
+
| `result` (required) | `string` | The `result:` value, parsed through the Tasty DSL. |
|
|
997
|
+
| `args` | `string[]` \| `Record<string, FunctionParameter>` | Ordered parameters. Array form lists bare names; object form maps names to type/default. |
|
|
998
|
+
| `returns` | `string` | Optional return type, e.g. `'<color>'`. |
|
|
999
|
+
| `$name` keys | `string \| number` | Any `$name` key declares a local variable `--name` (value parsed). |
|
|
970
1000
|
|
|
971
1001
|
A `FunctionParameter` is one of: `true` (bare param), a string CSS type shorthand (`'<length>'`), or `{ syntax?: string; default?: string | number }`.
|
|
972
1002
|
|
|
@@ -988,4 +1018,4 @@ For a complete reference of all enhanced style properties — syntax, values, mo
|
|
|
988
1018
|
- **[Methodology](methodology.md)** — Recommended patterns: root + sub-elements, styleProps, tokens, wrapping
|
|
989
1019
|
- **[Configuration](configuration.md)** — Tokens, recipes, custom units, style handlers, TypeScript extensions
|
|
990
1020
|
- **[Style Properties](styles.md)** — Complete reference for all enhanced style properties
|
|
991
|
-
- **[
|
|
1021
|
+
- **[Build-Time Extraction (`tastyStatic`)](tasty-static.md)** — Static styling with the Babel plugin
|