@tenphi/tasty 3.10.0 → 3.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +403 -380
- package/dist/{babel-types-C_7kRP38.d.ts → babel-types-BKDTT4--.d.ts} +2 -2
- package/dist/chunks/async-storage-f9j9JRtq.js +37 -0
- package/dist/chunks/async-storage-f9j9JRtq.js.map +1 -0
- package/dist/chunks/{build-config-l2Ys-AJr.js → build-config-5d8WlQbq.js} +5 -5
- package/dist/chunks/{build-config-l2Ys-AJr.js.map → build-config-5d8WlQbq.js.map} +1 -1
- package/dist/chunks/{collector-CaNtsRtn.js → collector-CjCak4iE.js} +4 -4
- package/dist/chunks/{collector-CaNtsRtn.js.map → collector-CjCak4iE.js.map} +1 -1
- package/dist/chunks/{config-engine-CpExZO_T.js → config-engine-uOFYZidB.js} +4 -4
- package/dist/chunks/{config-engine-CpExZO_T.js.map → config-engine-uOFYZidB.js.map} +1 -1
- package/dist/chunks/config-file-DlZXJMj8.js +25 -0
- package/dist/chunks/config-file-DlZXJMj8.js.map +1 -0
- package/dist/chunks/{css-definitions-DAIJxzFY.js → css-definitions-ubrHP97m.js} +88 -77
- package/dist/chunks/css-definitions-ubrHP97m.js.map +1 -0
- package/dist/chunks/{debug-CKy49GEr.js → debug-CJ31U_WE.js} +6 -6
- package/dist/chunks/{debug-CKy49GEr.js.map → debug-CJ31U_WE.js.map} +1 -1
- package/dist/chunks/{dsl-DRhFWQzo.js → dsl-DYSJc1-T.js} +11 -6
- package/dist/chunks/dsl-DYSJc1-T.js.map +1 -0
- package/dist/chunks/{react-runtime-oi6i3ELR.js → react-runtime-CyyCI02N.js} +168 -61
- package/dist/chunks/react-runtime-CyyCI02N.js.map +1 -0
- package/dist/chunks/{runtime-engine-DoULyhPt.js → runtime-engine-BQmOu5-W.js} +53 -36
- package/dist/chunks/runtime-engine-BQmOu5-W.js.map +1 -0
- package/dist/chunks/{runtime-state-CZ2J5QiZ.js → runtime-state-DEsyUMU-.js} +14 -3
- package/dist/chunks/runtime-state-DEsyUMU-.js.map +1 -0
- package/dist/chunks/{style-engine-CeKsjtIr.js → style-engine-BX2LshDZ.js} +116 -56
- package/dist/chunks/style-engine-BX2LshDZ.js.map +1 -0
- package/dist/chunks/{zero-engine-fxKu8Jbn.js → zero-engine-Dw0P4yGM.js} +22 -7
- package/dist/chunks/zero-engine-Dw0P4yGM.js.map +1 -0
- package/dist/{collector-w4mBj5gd.d.ts → collector-CTU-nl0g.d.ts} +2 -2
- package/dist/{config-CrZOfNJ5.d.ts → config-CcxqSZI2.d.ts} +28 -9
- package/dist/core/index.d.ts +4 -4
- package/dist/core/index.js +7 -7
- package/dist/hydrate-CRyjjAyl.d.ts +22 -0
- package/dist/{index-CYJtAI9a.d.ts → index--GdqCPlp.d.ts} +58 -16
- package/dist/index.d.ts +4 -4
- package/dist/index.js +8 -8
- package/dist/{merge-styles-VXwJ0Frr.d.ts → merge-styles-DSkVc6CD.d.ts} +2 -2
- package/dist/ssr/astro.js +4 -4
- package/dist/ssr/client.d.ts +2 -0
- package/dist/ssr/client.js +2 -0
- package/dist/ssr/index.d.ts +3 -22
- package/dist/ssr/index.js +4 -4
- package/dist/ssr/index.js.map +1 -1
- package/dist/ssr/next-config.d.ts +1 -1
- package/dist/ssr/next-config.js +6 -17
- package/dist/ssr/next-config.js.map +1 -1
- package/dist/ssr/next.d.ts +1 -1
- package/dist/ssr/next.js +3 -3
- package/dist/static/index.d.ts +2 -2
- package/dist/{types-JEY8PVYP.d.ts → types-DJEM_BOJ.d.ts} +6 -6
- package/dist/zero/babel.d.ts +1 -1
- package/dist/zero/babel.js +30 -19
- package/dist/zero/babel.js.map +1 -1
- package/dist/zero/index.d.ts +24 -6
- package/dist/zero/index.js +4 -4
- package/dist/zero/index.js.map +1 -1
- package/dist/zero/next.d.ts +1 -1
- package/dist/zero/next.js +3 -7
- package/dist/zero/next.js.map +1 -1
- package/docs/README.md +4 -1
- package/docs/adoption.md +25 -17
- package/docs/ai-agents.md +39 -34
- package/docs/comparison.md +9 -8
- package/docs/configuration.md +174 -58
- package/docs/debug.md +3 -2
- package/docs/design-system.md +117 -15
- package/docs/dsl.md +145 -81
- package/docs/getting-started.md +81 -24
- package/docs/injector.md +41 -40
- package/docs/methodology.md +46 -21
- package/docs/migration-v3.md +3 -3
- package/docs/pipeline.md +19 -16
- package/docs/plugins.md +21 -41
- package/docs/react-api.md +61 -9
- package/docs/runtime-benchmarks.md +4 -4
- package/docs/ssr.md +92 -66
- package/docs/styles.md +33 -17
- package/docs/tasty-static.md +129 -58
- package/package.json +23 -9
- package/dist/chunks/async-storage-CG5HnUT5.js +0 -20
- package/dist/chunks/async-storage-CG5HnUT5.js.map +0 -1
- package/dist/chunks/css-definitions-DAIJxzFY.js.map +0 -1
- package/dist/chunks/dsl-DRhFWQzo.js.map +0 -1
- package/dist/chunks/react-runtime-oi6i3ELR.js.map +0 -1
- package/dist/chunks/runtime-engine-DoULyhPt.js.map +0 -1
- package/dist/chunks/runtime-state-CZ2J5QiZ.js.map +0 -1
- package/dist/chunks/style-engine-CeKsjtIr.js.map +0 -1
- package/dist/chunks/zero-engine-fxKu8Jbn.js.map +0 -1
package/docs/dsl.md
CHANGED
|
@@ -6,6 +6,8 @@ The same value syntax, state maps, tokens, units, extension semantics, and speci
|
|
|
6
6
|
|
|
7
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).
|
|
8
8
|
|
|
9
|
+
To autocomplete your own token and preset names, see [TypeScript autocomplete](getting-started.md#typescript-autocomplete); to check state keys, tokens, units, and values at lint time, see [ESLint plugin](getting-started.md#eslint-plugin).
|
|
10
|
+
|
|
9
11
|
---
|
|
10
12
|
|
|
11
13
|
## Dictionary
|
|
@@ -18,6 +20,15 @@ Object where keys represent states and values are the styles to apply:
|
|
|
18
20
|
fill: { '': '#white', hovered: '#gray.05', 'theme=danger': '#red' }
|
|
19
21
|
```
|
|
20
22
|
|
|
23
|
+
Define an alias for one component by adding an `'@name': '<state expression>'` key to its styles. It works like a global alias from [`configure({ states })`](configuration.md#options), scoped to that component:
|
|
24
|
+
|
|
25
|
+
```jsx
|
|
26
|
+
styles: {
|
|
27
|
+
'@narrow': '@media(w < 600px)',
|
|
28
|
+
fill: { '': '#surface', '@narrow': '#surface-mobile' },
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
21
32
|
#### State Key Types
|
|
22
33
|
|
|
23
34
|
| Syntax | Example | Generated CSS |
|
|
@@ -34,17 +45,20 @@ fill: { '': '#white', hovered: '#gray.05', 'theme=danger': '#red' }
|
|
|
34
45
|
|
|
35
46
|
#### Automatic Native States
|
|
36
47
|
|
|
37
|
-
`disabled` and `
|
|
48
|
+
`disabled`, `checked`, and `hidden` are built-in automatic states. A Tasty component sets `data-disabled`, `data-checked`, and `data-hidden` from the native `disabled`, `checked`, and `hidden` props (or `isDisabled`, `isChecked`, `isHidden`), so each pair below expresses the same component state:
|
|
38
49
|
|
|
39
|
-
| Concise Tasty state | Explicit attribute selector |
|
|
40
|
-
| ------------------- | --------------------------- |
|
|
41
|
-
| `disabled` | `[disabled]` |
|
|
42
|
-
| `checked` | `[checked]` |
|
|
50
|
+
| Concise Tasty state | Compiles to | Explicit attribute selector |
|
|
51
|
+
| ------------------- | ----------------- | --------------------------- |
|
|
52
|
+
| `disabled` | `[data-disabled]` | `[disabled]` |
|
|
53
|
+
| `checked` | `[data-checked]` | `[checked]` |
|
|
54
|
+
| `hidden` | `[data-hidden]` | `[hidden]` |
|
|
43
55
|
|
|
44
56
|
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.
|
|
45
57
|
|
|
46
58
|
Operator precedence (highest to lowest): `!` (NOT) > `^` (XOR) > `|` (OR) > `&` (AND). Use parentheses to override: `hovered & (pressed ^ focused)`.
|
|
47
59
|
|
|
60
|
+
A top-level comma is the same as `|`: `'hovered, focused'` is `'hovered | focused'`.
|
|
61
|
+
|
|
48
62
|
`^` (XOR) means "exactly one of" — `A ^ B` expands to `(A & !B) | (!A & B)`. This is useful for mutually exclusive states where exactly one should be active:
|
|
49
63
|
|
|
50
64
|
```jsx
|
|
@@ -76,9 +90,7 @@ Element styled using a capitalized key. Identified by `data-element` attribute:
|
|
|
76
90
|
|
|
77
91
|
```jsx
|
|
78
92
|
styles: {
|
|
79
|
-
Title: {
|
|
80
|
-
preset: 'h3';
|
|
81
|
-
}
|
|
93
|
+
Title: { preset: 'h3' },
|
|
82
94
|
}
|
|
83
95
|
// Targets: <div data-element="Title">
|
|
84
96
|
```
|
|
@@ -117,14 +129,16 @@ The `@` placeholder marks exactly where `[data-element="..."]` is injected, allo
|
|
|
117
129
|
const List = tasty({
|
|
118
130
|
styles: {
|
|
119
131
|
Item: {
|
|
120
|
-
$: '
|
|
121
|
-
|
|
132
|
+
$: '>@::after',
|
|
133
|
+
content: '""',
|
|
122
134
|
},
|
|
123
135
|
},
|
|
124
136
|
});
|
|
125
|
-
// → .
|
|
137
|
+
// → .t1x29r0l.t1x29r0l > [data-element="Item"]::after { content: ""; }
|
|
126
138
|
```
|
|
127
139
|
|
|
140
|
+
Keep conditions such as `:last-child` out of `$` and write them as `@own(:last-child)` in a state map; `tasty/no-state-in-selector` warns otherwise.
|
|
141
|
+
|
|
128
142
|
### Color Token
|
|
129
143
|
|
|
130
144
|
Named color prefixed with `#` that maps to CSS custom properties. Supports opacity with `.N` suffix:
|
|
@@ -157,6 +171,10 @@ fill: '#current', // → currentcolor
|
|
|
157
171
|
color: '(#primary, #secondary)', // Fallback syntax
|
|
158
172
|
```
|
|
159
173
|
|
|
174
|
+
`#white`, `#black`, `#border`, and `#outline` are registered by default (see [Default properties](#default-properties)), and `#clear` is a built-in alias for `transparent` (see [below](#clear-is-transparent)), so they work before you define any tokens.
|
|
175
|
+
|
|
176
|
+
A name that is also a hex color is read as a token with the literal as its fallback, in every color style: `color: '#ddd'` emits `var(--ddd-color, #ddd)`, and `#ddd.5` emits `oklch(from var(--ddd-color, #ddd) l c h / .5)`. A `#ddd` token, if you define one, wins over the literal.
|
|
177
|
+
|
|
160
178
|
The suffix sets the alpha on the token's color with CSS relative color syntax:
|
|
161
179
|
|
|
162
180
|
```jsx
|
|
@@ -182,6 +200,17 @@ Two properties follow from writing the alpha slot rather than compositing:
|
|
|
182
200
|
`/ var(--fade)` unchanged, so it works whether `$fade` holds `.5` or `50%` —
|
|
183
201
|
which is what `--*-opacity` properties are registered to accept.
|
|
184
202
|
|
|
203
|
+
### `#clear` is transparent
|
|
204
|
+
|
|
205
|
+
`#clear` is a built-in [replace token](#replace-tokens) for `transparent`.
|
|
206
|
+
Opacity suffixes are ignored: `#clear.5` and `#clear.$fade` both emit
|
|
207
|
+
`transparent`. The same rule applies to user-defined replace tokens resolving
|
|
208
|
+
to `transparent`, including aliases of `#clear`.
|
|
209
|
+
|
|
210
|
+
It does not read `--clear-color` or require an `@property` registration.
|
|
211
|
+
An explicit `configure({ replaceTokens: { '#clear': ... } })` overrides the
|
|
212
|
+
default alias and uses the replacement's usual opacity behavior.
|
|
213
|
+
|
|
185
214
|
### `#current` is the keyword
|
|
186
215
|
|
|
187
216
|
`#current` emits the `currentcolor` keyword, so it resolves against the element
|
|
@@ -219,7 +248,7 @@ through `color-mix()`, which works from Safari 16.2 and composes like every othe
|
|
|
219
248
|
|
|
220
249
|
### `#current` composes instead
|
|
221
250
|
|
|
222
|
-
`#current`
|
|
251
|
+
`#current` composes alpha, and the difference is deliberate. A token _names_
|
|
223
252
|
a color, so fading it sets its alpha. `currentcolor` is the color an element
|
|
224
253
|
**inherits**, which an ancestor may already have faded — `#current.4` means "40%
|
|
225
254
|
of what reaches me":
|
|
@@ -236,9 +265,10 @@ step. Because a `color-mix()` percentage cannot be a `<number>`, an opacity
|
|
|
236
265
|
custom property used as `#current.$fade` must hold a unitless number; a token
|
|
237
266
|
accepts either form.
|
|
238
267
|
|
|
239
|
-
|
|
240
|
-
round trip that a gamut-limited space would clamp.
|
|
241
|
-
|
|
268
|
+
Token fades always use `oklch(from …)`: it is unbounded, so a wide-gamut color
|
|
269
|
+
survives a round trip that a gamut-limited space would clamp. `#current` fades
|
|
270
|
+
mix `in oklab`. Nothing configures either — see
|
|
271
|
+
[Color space](configuration.md#color-space).
|
|
242
272
|
|
|
243
273
|
---
|
|
244
274
|
|
|
@@ -256,6 +286,9 @@ border: '1bw solid oklch(from #primary l c h / 50%)', // relative color syntax
|
|
|
256
286
|
shadow: '0 0 1x color-mix(in oklab, #dark 20%, transparent)',
|
|
257
287
|
```
|
|
258
288
|
|
|
289
|
+
Tasty also ships `okhsl()` and `okhst()`, which compile to `rgb()` at parse
|
|
290
|
+
time: `fill: 'okhsl(280 80% 50%)'` → `rgb(40% 38.5% 88.2%)`.
|
|
291
|
+
|
|
259
292
|
`light-dark()` is the one exception to "always a color": CSS lets it pick between
|
|
260
293
|
values of any type, so it is treated as a color only when its arguments are
|
|
261
294
|
colors. `padding: 'light-dark(1x, 2x)'` still reaches the padding slot.
|
|
@@ -293,16 +326,16 @@ fill: '#adaptive.5'; // → oklch(from light-dark(…) l c h / .5)
|
|
|
293
326
|
|
|
294
327
|
## Built-in Units
|
|
295
328
|
|
|
296
|
-
| Unit | Description | Example | CSS
|
|
297
|
-
| ---- | ------------------ | ------- |
|
|
298
|
-
| `x` |
|
|
299
|
-
| `r` | Border radius | `1r` | `
|
|
300
|
-
| `cr` | Card border radius | `1cr` | `
|
|
301
|
-
| `bw` | Border width | `2bw` | `
|
|
302
|
-
| `ow` | Outline width | `1ow` | `
|
|
303
|
-
| `sf` | Stable fraction | `1sf` | `minmax(0, 1fr)`
|
|
329
|
+
| Unit | Description | Default | Example | CSS output |
|
|
330
|
+
| ---- | ------------------ | ---------------- | ------- | ---------------- |
|
|
331
|
+
| `x` | Spacing step | `8px` | `2x` | `16px` |
|
|
332
|
+
| `r` | Border radius | `6px` | `1r` | `6px` |
|
|
333
|
+
| `cr` | Card border radius | `10px` | `1cr` | `10px` |
|
|
334
|
+
| `bw` | Border width | `1px` | `2bw` | `2px` |
|
|
335
|
+
| `ow` | Outline width | `3px` | `1ow` | `3px` |
|
|
336
|
+
| `sf` | Stable fraction | `minmax(0, Nfr)` | `1sf` | `minmax(0, 1fr)` |
|
|
304
337
|
|
|
305
|
-
|
|
338
|
+
Units compile to plain values. Change the scale with `configure({ units: { x: '4px' } })`, or point a unit at a custom property when it must change at runtime: with `units: { x: 'var(--gap)' }`, `2x` compiles to `calc(2 * var(--gap))`. Setting a `$gap` or `$radius` token does not change the units. Register additional units the same way via [`configure()`](configuration.md#options).
|
|
306
339
|
|
|
307
340
|
---
|
|
308
341
|
|
|
@@ -359,7 +392,7 @@ const CardWithPanel = tasty({
|
|
|
359
392
|
});
|
|
360
393
|
```
|
|
361
394
|
|
|
362
|
-
At each level, base recipes are collected in
|
|
395
|
+
At each level, base recipes are collected in the order listed (later recipes replace earlier values for the same property), then local styles are merged, then post recipes are merged. The merges with local styles and post recipes follow the [state-map extension and replacement rules](#extending-vs-replacing-state-maps).
|
|
363
396
|
|
|
364
397
|
Recipe definitions themselves cannot contain a `recipe` reference to another recipe: there is no recursive expansion or recipe inheritance. Compose names in the root or sub-element `recipe` property instead. See [Configuration — Recipes](configuration.md#recipes) for an unsupported nesting example.
|
|
365
398
|
|
|
@@ -439,12 +472,14 @@ fill: {
|
|
|
439
472
|
Use `null` inside a state map to remove a state, or `false` to block it entirely (tombstone):
|
|
440
473
|
|
|
441
474
|
```jsx
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
}
|
|
475
|
+
const MyButton = tasty(Button, {
|
|
476
|
+
styles: {
|
|
477
|
+
fill: {
|
|
478
|
+
hovered: null, // removes the parent's hovered state
|
|
479
|
+
disabled: false, // tombstone — no CSS for disabled, blocks recipe too
|
|
480
|
+
},
|
|
481
|
+
},
|
|
482
|
+
});
|
|
448
483
|
```
|
|
449
484
|
|
|
450
485
|
### Resetting Properties with `null` and `false`
|
|
@@ -494,7 +529,7 @@ const SimpleButton = tasty(Button, {
|
|
|
494
529
|
|
|
495
530
|
### `@media(...)` — Media Queries
|
|
496
531
|
|
|
497
|
-
Media queries support dimension shorthands
|
|
532
|
+
Media queries support dimension shorthands:
|
|
498
533
|
|
|
499
534
|
| Shorthand | Expands to |
|
|
500
535
|
| --------- | ---------- |
|
|
@@ -516,10 +551,17 @@ fill: {
|
|
|
516
551
|
| `@media(600px <= w < 1200px)` | `@media (600px <= width < 1200px)` |
|
|
517
552
|
| `@media:print` | `@media print` |
|
|
518
553
|
| `@media:screen` | `@media screen` |
|
|
554
|
+
| `@media:all` | `@media all` |
|
|
555
|
+
| `@media:speech` | `@media speech` |
|
|
519
556
|
| `@media(prefers-color-scheme: dark)` | `@media (prefers-color-scheme: dark)` |
|
|
520
557
|
| `@media(prefers-reduced-motion)` | `@media (prefers-reduced-motion)` |
|
|
558
|
+
| `@media(aspect-ratio > 16/9)` | `@media (aspect-ratio > 16/9)` |
|
|
521
559
|
|
|
522
|
-
|
|
560
|
+
Tasty units resolve to their literal length inside query conditions, built-in and configured units alike: `@media(w < 40x)` emits `@media (width < 320px)`. A query condition cannot read a custom property, so a unit configured as one (`units: { x: 'var(--gap)' }`) cannot be used there; write that length in `px`, `em`, or `rem`.
|
|
561
|
+
|
|
562
|
+
Units are only converted in features that take a length: `width`, `height`, `inline-size`, and `block-size`, with or without `min-`/`max-` (`@media(min-width: 40x)` converts too). Every other feature keeps its value as written, so `@media(min-resolution: 2x)` and `@media(resolution >= 2x)` reach the browser unchanged: there `x` is CSS's own resolution unit, an alias of `dppx`.
|
|
563
|
+
|
|
564
|
+
When it combines or orders conditions, Tasty compares bounds only within one unit. `@media(w >= 600px) & @media(w < 40em)` keeps both bounds, because a query `em` follows the user's font size, so `600px` and `40em` cannot be ordered at build time.
|
|
523
565
|
|
|
524
566
|
In practice, define state aliases via `configure({ states })` and use `@mobile` instead of writing the full query in every component.
|
|
525
567
|
|
|
@@ -545,28 +587,30 @@ const Panel = tasty({
|
|
|
545
587
|
});
|
|
546
588
|
```
|
|
547
589
|
|
|
548
|
-
| Tasty syntax | CSS output
|
|
549
|
-
| ---------------------------------- |
|
|
550
|
-
| `@(layout, w < 600px)` | `@container layout (width < 600px)`
|
|
551
|
-
| `@(w < 600px)` | `@container (width < 600px)`
|
|
552
|
-
| `@(layout, $variant=danger)` | `@container layout style(--variant:
|
|
553
|
-
| `@(layout, $compact)` | `@container layout style(--compact)`
|
|
554
|
-
| `@(scroll-state(stuck: top))` | `@container scroll-state(stuck: top)`
|
|
555
|
-
| `@(nav, scroll-state(stuck: top))` | `@container nav scroll-state(stuck: top)`
|
|
590
|
+
| Tasty syntax | CSS output |
|
|
591
|
+
| ---------------------------------- | -------------------------------------------- |
|
|
592
|
+
| `@(layout, w < 600px)` | `@container layout (width < 600px)` |
|
|
593
|
+
| `@(w < 600px)` | `@container (width < 600px)` |
|
|
594
|
+
| `@(layout, $variant=danger)` | `@container layout style(--variant: danger)` |
|
|
595
|
+
| `@(layout, $compact)` | `@container layout style(--compact)` |
|
|
596
|
+
| `@(scroll-state(stuck: top))` | `@container scroll-state(stuck: top)` |
|
|
597
|
+
| `@(nav, scroll-state(stuck: top))` | `@container nav scroll-state(stuck: top)` |
|
|
556
598
|
|
|
557
|
-
Container style queries use `$prop` (boolean) or `$prop=value` syntax, which maps to CSS `style(--prop)` or `style(--prop:
|
|
599
|
+
Container style queries use `$prop` (boolean) or `$prop=value` syntax, which maps to CSS `style(--prop)` or `style(--prop: value)`. The value is emitted unquoted, so it matches a custom property set to the identifier `danger`, not the string `"danger"`.
|
|
600
|
+
|
|
601
|
+
Other size features pass through as written: `@(aspect-ratio > 1)` → `@container (aspect-ratio > 1)`, `@(orientation: portrait)` → `@container (orientation: portrait)`. They need a container with `containerType: 'size'`, since an `inline-size` container has no block size to compare against.
|
|
558
602
|
|
|
559
603
|
### `@supports(...)` — Feature Queries
|
|
560
604
|
|
|
561
605
|
Feature queries test CSS property support and accept CSS support functions such
|
|
562
606
|
as `at-rule()`. Use `$` as the first argument to test selector support:
|
|
563
607
|
|
|
564
|
-
| Tasty syntax
|
|
565
|
-
|
|
|
566
|
-
| `@supports(display: grid)`
|
|
567
|
-
| `@supports($, :has(*))` | `@supports selector(:has(*))`
|
|
568
|
-
| `@supports(at-rule(@scope))` | `@supports (at-rule(@scope))`
|
|
569
|
-
| `!@supports(display: grid)`
|
|
608
|
+
| Tasty syntax | CSS output |
|
|
609
|
+
| ----------------------------- | ----------------------------------- |
|
|
610
|
+
| `@supports(display: grid)` | `@supports (display: grid)` |
|
|
611
|
+
| `@supports($, :has(*))` | `@supports selector(:has(*))` |
|
|
612
|
+
| `@supports(at-rule(@scope))` | `@supports (at-rule(@scope))` |
|
|
613
|
+
| `!@supports(display: grid)` | `@supports (not (display: grid))` |
|
|
570
614
|
| `!@supports(at-rule(@scope))` | `@supports (not (at-rule(@scope)))` |
|
|
571
615
|
|
|
572
616
|
```jsx
|
|
@@ -620,12 +664,12 @@ inset: {
|
|
|
620
664
|
|
|
621
665
|
```css
|
|
622
666
|
.t0.t0 {
|
|
623
|
-
inset: 0
|
|
667
|
+
inset: 0 auto auto auto;
|
|
624
668
|
}
|
|
625
|
-
@container scroll-state
|
|
626
|
-
@
|
|
669
|
+
@supports (container-type: scroll-state) {
|
|
670
|
+
@container scroll-state(scrolled: block-end) {
|
|
627
671
|
.t0.t0 {
|
|
628
|
-
inset: -
|
|
672
|
+
inset: -640px auto auto auto;
|
|
629
673
|
}
|
|
630
674
|
}
|
|
631
675
|
}
|
|
@@ -634,7 +678,7 @@ inset: {
|
|
|
634
678
|
The floor is emitted as a bare rule (no negated `@supports` / container branches),
|
|
635
679
|
so it always applies. When the override matches it wins because it is emitted
|
|
636
680
|
later and all state selectors share the same specificity (see the note on
|
|
637
|
-
`:where()`
|
|
681
|
+
`:where()` above).
|
|
638
682
|
|
|
639
683
|
Notes:
|
|
640
684
|
|
|
@@ -706,7 +750,7 @@ color: {
|
|
|
706
750
|
| `@root([lang="en"])` | `:root[lang="en"]` |
|
|
707
751
|
| `!@root(schema=dark)` | `:root:not([data-schema="dark"])` |
|
|
708
752
|
|
|
709
|
-
Root conditions are prepended to the component selector
|
|
753
|
+
Root conditions are prepended to the component selector inside `:where()`: `:where(:root[data-schema="dark"]) .t0.t0 { ... }`.
|
|
710
754
|
|
|
711
755
|
### `@own(...)` — Sub-element's Own State
|
|
712
756
|
|
|
@@ -752,11 +796,11 @@ const FadeIn = tasty({
|
|
|
752
796
|
|
|
753
797
|
| Tasty syntax | CSS output |
|
|
754
798
|
| ------------ | ------------------------------------ |
|
|
755
|
-
| `@starting` |
|
|
799
|
+
| `@starting` | `.t0.t0 { @starting-style { ... } }` |
|
|
756
800
|
|
|
757
801
|
### `@parent(...)` — Parent Element States
|
|
758
802
|
|
|
759
|
-
Style based on ancestor element attributes. Uses `:
|
|
803
|
+
Style based on ancestor element attributes. Uses `:where([selector] *)` / `:where(:not([selector] *))` for symmetric, composable parent checks. Boolean logic (`&`, `|`, `!`, `^`) is supported inside `@parent()`.
|
|
760
804
|
|
|
761
805
|
```jsx
|
|
762
806
|
const Highlight = tasty({
|
|
@@ -770,15 +814,15 @@ const Highlight = tasty({
|
|
|
770
814
|
});
|
|
771
815
|
```
|
|
772
816
|
|
|
773
|
-
| Syntax | CSS Output
|
|
774
|
-
| ------------------------------------- |
|
|
775
|
-
| `@parent(hovered)` | `:
|
|
776
|
-
| `!@parent(hovered)` | `:not([data-hovered] *)`
|
|
777
|
-
| `@parent(hovered, >)` | `:
|
|
778
|
-
| `@parent(.active)` | `:
|
|
779
|
-
| `@parent(hovered & focused)` | `:
|
|
780
|
-
| `@parent(hovered) & @parent(focused)` | `:
|
|
781
|
-
| `@parent(hovered \| focused)` | `:
|
|
817
|
+
| Syntax | CSS Output |
|
|
818
|
+
| ------------------------------------- | -------------------------------------------------------------------------- |
|
|
819
|
+
| `@parent(hovered)` | `:where([data-hovered] *)` |
|
|
820
|
+
| `!@parent(hovered)` | `:where(:not([data-hovered] *))` |
|
|
821
|
+
| `@parent(hovered, >)` | `:where([data-hovered] > *)` (direct parent) |
|
|
822
|
+
| `@parent(.active)` | `:where(.active *)` |
|
|
823
|
+
| `@parent(hovered & focused)` | `:where([data-hovered][data-focused] *)` (same ancestor) |
|
|
824
|
+
| `@parent(hovered) & @parent(focused)` | `:where([data-hovered] *):where([data-focused] *)` (independent ancestors) |
|
|
825
|
+
| `@parent(hovered \| focused)` | `:where([data-hovered] *, [data-focused] *)` (OR inside single wrapper) |
|
|
782
826
|
|
|
783
827
|
For sub-elements, the parent check applies to the root element's ancestors:
|
|
784
828
|
|
|
@@ -793,12 +837,12 @@ const Card = tasty({
|
|
|
793
837
|
},
|
|
794
838
|
},
|
|
795
839
|
});
|
|
796
|
-
// → .t0.t0:
|
|
840
|
+
// → .t0.t0:where([data-hovered] *) [data-element="Label"]
|
|
797
841
|
```
|
|
798
842
|
|
|
799
843
|
### `:is()`, `:has()` — CSS Structural Pseudo-classes
|
|
800
844
|
|
|
801
|
-
Use CSS structural pseudo-classes directly in state keys. Capitalized words become `[data-element="..."]` selectors; lowercase words are HTML tags. A trailing combinator (`>`, `+`, `~`) is auto-completed with `*`.
|
|
845
|
+
Use CSS structural pseudo-classes directly in state keys. Capitalized words become `[data-element="..."]` selectors; lowercase words are HTML tags. A trailing combinator (`>`, `+`, `~`) is auto-completed with `*`. Leading combinators (`> Icon`, `+ Icon`) are relative selectors and are valid only inside `:has()`.
|
|
802
846
|
|
|
803
847
|
`:where()` and `:not()` are also supported but rarely needed — use `:is()` and `!` negation instead.
|
|
804
848
|
|
|
@@ -821,18 +865,18 @@ const Card = tasty({
|
|
|
821
865
|
});
|
|
822
866
|
```
|
|
823
867
|
|
|
824
|
-
| Syntax
|
|
825
|
-
|
|
|
826
|
-
| `:has(> Icon)`
|
|
827
|
-
| `:has(+ Icon)`
|
|
828
|
-
| `:has(~ Icon)`
|
|
829
|
-
| `:has(Icon +)`
|
|
830
|
-
| `:has(Icon ~)`
|
|
831
|
-
| `:has(>)`
|
|
832
|
-
| `:is(
|
|
833
|
-
| `:has(button)`
|
|
834
|
-
| `!:has(> Icon)`
|
|
835
|
-
| `!:is(Panel)`
|
|
868
|
+
| Syntax | CSS Output | Meaning |
|
|
869
|
+
| -------------------- | ------------------------------------- | --------------------------------------- |
|
|
870
|
+
| `:has(> Icon)` | `:has(> [data-element="Icon"])` | Has Icon as direct child |
|
|
871
|
+
| `:has(+ Icon)` | `:has(+ [data-element="Icon"])` | Immediately followed by an Icon sibling |
|
|
872
|
+
| `:has(~ Icon)` | `:has(~ [data-element="Icon"])` | Has an Icon sibling somewhere after |
|
|
873
|
+
| `:has(Icon +)` | `:has([data-element="Icon"] + *)` | Immediately preceded by an Icon sibling |
|
|
874
|
+
| `:has(Icon ~)` | `:has([data-element="Icon"] ~ *)` | Has an Icon sibling somewhere before |
|
|
875
|
+
| `:has(>)` | `:has(> *)` | Has any direct child |
|
|
876
|
+
| `:is(Field + input)` | `:is([data-element="Field"] + input)` | Is an input right after a Field sibling |
|
|
877
|
+
| `:has(button)` | `:has(button)` | HTML tag (lowercase, unchanged) |
|
|
878
|
+
| `!:has(> Icon)` | `:not(:has(> [data-element="Icon"]))` | Negation (use `!`) |
|
|
879
|
+
| `!:is(Panel)` | `:not([data-element="Panel"])` | Negation (use `!:is`) |
|
|
836
880
|
|
|
837
881
|
Combine with other states using boolean logic (`&`, `|`, `!`, `^`):
|
|
838
882
|
|
|
@@ -894,6 +938,26 @@ Use explicit `@property` when you need non-default settings like `inherits: fals
|
|
|
894
938
|
},
|
|
895
939
|
```
|
|
896
940
|
|
|
941
|
+
### Default properties
|
|
942
|
+
|
|
943
|
+
Tasty registers these properties itself. Style handlers read their defaults from them rather than repeating the values inline, so styles work before you define any tokens, and defining a token replaces the initial value.
|
|
944
|
+
|
|
945
|
+
| Token | Initial value | Read by |
|
|
946
|
+
| ---------------------------------------------------- | ---------------------------------------- | -------------------------------------------------------------- |
|
|
947
|
+
| `#current` | `currentcolor` | anything reading `$current-color`, which `color` sets |
|
|
948
|
+
| `#white`, `#black` | `rgb(255 255 255)`, `rgb(0 0 0)` | your styles |
|
|
949
|
+
| `#border` | `currentcolor` | `border` with no color |
|
|
950
|
+
| `#outline` | `currentcolor` | `outline` with no color |
|
|
951
|
+
| `$gap`, `$radius`, `$border-width`, `$outline-width` | `8px`, `6px`, `1px`, `3px` | units you point at them, as in `units: { x: 'var(--gap)' }` |
|
|
952
|
+
| `$transition` | `80ms` | `transition` with no duration |
|
|
953
|
+
| `$sharp-radius` | `0px` | the sharp corners of `radius: 'leaf'` and `radius: 'backleaf'` |
|
|
954
|
+
| `$bold-font-weight` | `700` | `preset: 'name / strong'` |
|
|
955
|
+
| `$font-sans-fallback`, `$font-mono-fallback` | a `system-ui` and a `ui-monospace` stack | `font` and presets, after `$font-sans` / `$font-mono` |
|
|
956
|
+
|
|
957
|
+
Every rendering path emits them: the client injector, the SSR collector (`TastyRegistry`, `tastyIntegration()`, `runWithCollector()`), the `withTastyNext()` shared stylesheet, and the Babel plugin. CSS written with the programmatic API needs [`extractConfigProperties()`](tasty-static.md#programmatic-api), once per stylesheet. Server rendering without a collector, such as Astro without `tastyIntegration()`, emits none of them.
|
|
958
|
+
|
|
959
|
+
The initial values need `@property` support: Chrome and Edge 85+, Firefox 128+, Safari 16.4+. In older engines a handler that reads one of these tokens resolves to nothing unless your tokens define it: a `border` with no color draws no border, and a `transition` with no duration does not animate. To support those engines, define the tokens your styles rely on in `configure({ tokens })`.
|
|
960
|
+
|
|
897
961
|
---
|
|
898
962
|
|
|
899
963
|
## Font Face (`@font-face`)
|
|
@@ -954,7 +1018,7 @@ Define custom list markers via the CSS `@counter-style` at-rule. Keys are counte
|
|
|
954
1018
|
|
|
955
1019
|
```ts
|
|
956
1020
|
const EmojiList = tasty({
|
|
957
|
-
|
|
1021
|
+
as: 'ol',
|
|
958
1022
|
styles: {
|
|
959
1023
|
'@counter-style': {
|
|
960
1024
|
thumbs: {
|
|
@@ -1046,7 +1110,7 @@ Generated CSS:
|
|
|
1046
1110
|
|
|
1047
1111
|
A `FunctionParameter` is one of: `true` (bare param), a string CSS type shorthand (`'<length>'`), or `{ syntax?: string; default?: string | number }`.
|
|
1048
1112
|
|
|
1049
|
-
Functions can also be registered with the [`useFunction`](react-api.md#usefunction)
|
|
1113
|
+
Functions can also be registered with the [`useFunction`](react-api.md#usefunction) style function or globally via [`configure({ functions })`](configuration.md#functions) (the same map also accepts bare-key parse functions).
|
|
1050
1114
|
|
|
1051
1115
|
> `@function` is permanent — injected once and never cleaned up, like `@counter-style`. It is an experimental CSS feature ([Chrome 139+](https://developer.mozilla.org/en-US/docs/Web/CSS/@function)); unsupported browsers safely ignore the native rule, so use it as progressive enhancement — or enable the [`configure({ polyfills: { functions: true } })`](configuration.md#polyfills) inline polyfill to expand `$$name(...)` calls into plain CSS for full cross-browser support.
|
|
1052
1116
|
|
package/docs/getting-started.md
CHANGED
|
@@ -64,7 +64,7 @@ export default function App() {
|
|
|
64
64
|
|
|
65
65
|
`tasty()` returns a normal React component. The keys in each state map define priority: later branches win over earlier ones. Because `disabled` is last, a disabled button keeps its disabled styles even while the pointer is over it.
|
|
66
66
|
|
|
67
|
-
`disabled` and `
|
|
67
|
+
`disabled`, `checked`, and `hidden` are built-in automatic states. Tasty sets `data-disabled`, `data-checked`, and `data-hidden` from the matching native prop, so `disabled` in a state map is the concise form of `[disabled]` (it compiles to `[data-disabled]`).
|
|
68
68
|
|
|
69
69
|
Tasty compiles that priority into selectors that exclude one another. The hover, active, and disabled background rules cannot all match and ask the CSS cascade to decide the winner.
|
|
70
70
|
|
|
@@ -101,9 +101,9 @@ import App from './App';
|
|
|
101
101
|
createRoot(document.getElementById('root')!).render(<App />);
|
|
102
102
|
```
|
|
103
103
|
|
|
104
|
-
### Define shared tokens and
|
|
104
|
+
### Define shared tokens and units
|
|
105
105
|
|
|
106
|
-
Color tokens like `#primary` resolve to CSS custom properties
|
|
106
|
+
Color tokens like `#primary` and value tokens like `$sidebar-width` resolve to CSS custom properties (`var(--primary-color)`, `var(--sidebar-width)`). Define their values with `configure({ tokens })`:
|
|
107
107
|
|
|
108
108
|
```tsx
|
|
109
109
|
// src/tasty-config.ts
|
|
@@ -114,10 +114,7 @@ configure({
|
|
|
114
114
|
'#primary': 'oklch(55% 0.25 265)',
|
|
115
115
|
'#surface': '#fff',
|
|
116
116
|
'#text': '#111',
|
|
117
|
-
$
|
|
118
|
-
$radius: '4px',
|
|
119
|
-
'$border-width': '1px',
|
|
120
|
-
'$outline-width': '2px',
|
|
117
|
+
'$sidebar-width': '280px',
|
|
121
118
|
},
|
|
122
119
|
});
|
|
123
120
|
```
|
|
@@ -135,7 +132,20 @@ configure({
|
|
|
135
132
|
});
|
|
136
133
|
```
|
|
137
134
|
|
|
138
|
-
|
|
135
|
+
Tokens are injected as `:root` CSS custom properties when the first style is rendered, and every component using `#primary` follows them. You can also use standard CSS color values such as `rgb(...)`, `hsl(...)`, and named colors directly; the built-in `okhsl(...)` and `okhst(...)` functions are the recommended choice when you want authored colors that stay aligned with Tasty's design-system-oriented workflow.
|
|
136
|
+
|
|
137
|
+
Units work differently: `x`, `r`, `cr`, `bw`, and `ow` compile to plain values when styles are generated (`2x` → `16px` with the default `8px` step), so setting a `$gap` token does not change them. Change the scale with `configure({ units })`:
|
|
138
|
+
|
|
139
|
+
```tsx
|
|
140
|
+
configure({
|
|
141
|
+
units: {
|
|
142
|
+
x: '4px', // spacing step: 2x → 8px
|
|
143
|
+
r: '8px', // radius: 1r → 8px
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
When the scale has to change at runtime, for example per theme or density, point the unit at a custom property and define that property as a token. With `units: { x: 'var(--gap)' }` and `tokens: { $gap: '8px' }`, `2x` compiles to `calc(2 * var(--gap))`. See [Built-in Units](dsl.md#built-in-units) for the defaults.
|
|
139
149
|
|
|
140
150
|
> **Note:** `configure({ replaceTokens })` is a separate mechanism — it replaces tokens with literal values at parse time (baked into CSS). Use it for value aliases like `$card-padding: '4x'` that should be resolved during style generation, not for defining color or unit values. See [Configuration — Replace Tokens](configuration.md#replace-tokens-parse-time-substitution) for details.
|
|
141
151
|
|
|
@@ -145,7 +155,7 @@ See [Configuration](configuration.md) for the full `configure()` API — tokens,
|
|
|
145
155
|
|
|
146
156
|
## ESLint plugin
|
|
147
157
|
|
|
148
|
-
The ESLint plugin catches invalid style properties, bad token references, malformed state keys, and other mistakes at lint time — before they reach the browser.
|
|
158
|
+
The ESLint plugin catches invalid style properties, bad token references, malformed state keys, and other mistakes at lint time — before they reach the browser. It checks style objects and JSX style props alike, and runs under both ESLint 9+ and oxlint.
|
|
149
159
|
|
|
150
160
|
### Install
|
|
151
161
|
|
|
@@ -167,36 +177,80 @@ export default [
|
|
|
167
177
|
];
|
|
168
178
|
```
|
|
169
179
|
|
|
170
|
-
###
|
|
180
|
+
### Tell it about your design system
|
|
181
|
+
|
|
182
|
+
Without project configuration the plugin checks syntax and property names, but it cannot know which tokens, presets, or recipes exist. Add a `tasty.config.ts` at the project root to enable those checks for the names your `configure()` call defines:
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
// tasty.config.ts
|
|
186
|
+
export default {
|
|
187
|
+
tokens: ['#primary', '#surface', '#text', '$sidebar-width'],
|
|
188
|
+
states: ['@mobile', '@dark'],
|
|
189
|
+
presets: ['h1', 'h2', 't1', 't2'],
|
|
190
|
+
recipes: ['card'],
|
|
191
|
+
importSources: ['@my-org/ui'], // modules that re-export tasty()
|
|
192
|
+
};
|
|
193
|
+
```
|
|
171
194
|
|
|
172
|
-
|
|
195
|
+
Every list is optional; leaving one out skips that check. `importSources` matters when your design system re-exports `tasty()` from its own module: the plugin only lints calls whose import it can trace.
|
|
173
196
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
|
177
|
-
|
|
|
178
|
-
|
|
|
179
|
-
|
|
|
180
|
-
|
|
|
181
|
-
|
|
|
182
|
-
|
|
|
183
|
-
|
|
|
197
|
+
### What `recommended` catches
|
|
198
|
+
|
|
199
|
+
| Category | Rules | Examples |
|
|
200
|
+
| ------------------- | ------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------ |
|
|
201
|
+
| Property validation | `known-property`, `valid-boolean-property`, `valid-sub-element` | Flags typos like `pading` or invalid boolean usage |
|
|
202
|
+
| Value validation | `valid-value`, `valid-color-token`, `valid-custom-unit` | Catches `#nonexistent` tokens, bad unit syntax |
|
|
203
|
+
| State validation | `valid-state-key`, `no-nested-state-map`, `require-default-state` | Validates state key syntax, ensures `''` default exists |
|
|
204
|
+
| Structure | `valid-styles-structure`, `no-important`, `no-nested-selector` | Prevents `!important`, invalid nesting |
|
|
205
|
+
| Static mode | `static-no-dynamic-values`, `static-valid-selector` | Enforces build-time constraints in `tastyStatic()` |
|
|
206
|
+
| Style properties | `valid-preset`, `valid-recipe`, `valid-transition`, `valid-directional-modifier`, `valid-radius-shape` | Validates preset names, recipe references, transition syntax |
|
|
207
|
+
| Token usage | `no-raw-color-values`, `consistent-token-usage`, `no-raw-transition-duration` | Suggests `#color` tokens over raw colors, units over raw pixel lengths |
|
|
208
|
+
| Preferred syntax | `prefer-shorthand-property`, `prefer-auto-calc`, `prefer-hide`, `prefer-state-negation`, and others | Suggests `fill` over `backgroundColor`, `(...)` over `calc(...)` |
|
|
209
|
+
| Component props | `no-style-prop`, `no-classname-prop`, `no-styles-prop` | Points escape hatches toward `tokens`, `mods`, and the component's props |
|
|
210
|
+
| Dynamic styles | `no-runtime-styles-mutation`, `no-style-spread` | Warns on JavaScript values and spreads inside runtime style objects |
|
|
184
211
|
|
|
185
212
|
### Strict config
|
|
186
213
|
|
|
187
|
-
For stricter governance, use `tasty.configs.strict`. It adds
|
|
214
|
+
For stricter governance, use `tasty.configs.strict`. It adds existence checks for `$` custom properties and `@` state aliases, and validates state definitions in `configure()` and `tasty.config.ts`:
|
|
188
215
|
|
|
189
216
|
```js
|
|
190
217
|
export default [tasty.configs.strict];
|
|
191
218
|
```
|
|
192
219
|
|
|
220
|
+
See the [plugin README](https://github.com/tenphi/eslint-plugin-tasty) for every rule, the oxlint setup, and the experimental type-aware mode for JSX props.
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## TypeScript autocomplete
|
|
225
|
+
|
|
226
|
+
Component props are typed from their definitions without extra setup: `modProps` value lists become unions, `variant` is typed from the variant keys, and sub-elements are typed components. To get autocomplete for your own token, preset, and theme names inside style values, register them with module augmentation:
|
|
227
|
+
|
|
228
|
+
```ts
|
|
229
|
+
// tasty.d.ts
|
|
230
|
+
import '@tenphi/tasty';
|
|
231
|
+
|
|
232
|
+
declare module '@tenphi/tasty' {
|
|
233
|
+
interface TastyNamedColors {
|
|
234
|
+
primary: true;
|
|
235
|
+
surface: true;
|
|
236
|
+
text: true;
|
|
237
|
+
}
|
|
238
|
+
interface TastyPresetNames {
|
|
239
|
+
h1: true;
|
|
240
|
+
t2: true;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
See [Configuration → TypeScript](configuration.md#typescript) for every augmentation interface and how component props are typed.
|
|
246
|
+
|
|
193
247
|
---
|
|
194
248
|
|
|
195
249
|
## Editor support
|
|
196
250
|
|
|
197
|
-
**[VS Code Extension](https://github.com/tenphi/tasty-vscode-extension)** — Syntax highlighting for Tasty styles in TypeScript/TSX/JavaScript/JSX
|
|
251
|
+
**[VS Code Extension](https://github.com/tenphi/tasty-vscode-extension)** — Syntax highlighting for Tasty styles in TypeScript/TSX/JavaScript/JSX: color tokens, custom units, state keys, presets, and style properties inside `tasty()`, `tastyStatic()`, `styles` objects, and style functions. It also adds snippets for components, state maps, and `configure()`. Install the `.vsix` from the [latest GitHub release](https://github.com/tenphi/tasty-vscode-extension/releases/latest); the extension is not published to the marketplace.
|
|
198
252
|
|
|
199
|
-
**[Glaze](https://github.com/tenphi/glaze)** —
|
|
253
|
+
**[Glaze](https://github.com/tenphi/glaze)** — Not an editor tool, but the fastest way to fill `configure({ tokens })`: it generates light, dark, and high-contrast palettes from one hue in the OKHST color space, solves WCAG and APCA contrast, and exports them as Tasty tokens with `configure({ tokens: theme.tasty() })`. See [Glaze in the README](../README.md#glaze).
|
|
200
254
|
|
|
201
255
|
---
|
|
202
256
|
|
|
@@ -228,6 +282,7 @@ Both `tasty()` and `tastyStatic()` share the same DSL, tokens, units, and state
|
|
|
228
282
|
- **[Adoption Guide](adoption.md)** — Roll out Tasty inside an existing design system or platform team
|
|
229
283
|
- **[Comparison](comparison.md)** — Evaluate Tasty against other styling systems
|
|
230
284
|
- **[Configuration](configuration.md)** — Full `configure()` API: tokens, recipes, custom units, style handlers, TypeScript extensions
|
|
285
|
+
- **[Plugins & Extension Points](plugins.md)** — Add your own style properties, units, functions, and component props
|
|
231
286
|
- **[Style Properties](styles.md)** — Complete reference for all enhanced style properties
|
|
232
287
|
- **[Debug Utilities](debug.md)** — Inspect generated CSS and debug runtime behavior when styles do not look right
|
|
233
288
|
|
|
@@ -237,4 +292,6 @@ Both `tasty()` and `tastyStatic()` share the same DSL, tokens, units, and state
|
|
|
237
292
|
|
|
238
293
|
- Styles are missing on first render: make sure the file that calls `configure()` is imported before any `tasty()` component renders.
|
|
239
294
|
- Token or unit values are not what you expect: check your `configure({ tokens, units })` setup, then inspect the generated CSS variables with [Debug Utilities](debug.md).
|
|
295
|
+
- Changing a `$gap` or `$radius` token does not change `2x` or `1r`: units compile to plain values from `configure({ units })`. Point a unit at the custom property (`units: { x: 'var(--gap)' }`) if it should follow the token.
|
|
296
|
+
- The linter accepts unknown tokens or presets: list them in `tasty.config.ts` so the plugin knows what your design system defines.
|
|
240
297
|
- You need zero-runtime delivery: use server-only [`tasty()` with server rendering](ssr.md) to keep the full React API, or use [`tastyStatic()` build-time extraction](tasty-static.md) when styles must be compiled before rendering.
|