@tenphi/tasty 0.10.1 → 0.11.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 +79 -5
- package/dist/config.d.ts +32 -9
- package/dist/config.js +51 -10
- package/dist/config.js.map +1 -1
- package/dist/core/index.d.ts +3 -4
- package/dist/index.d.ts +3 -4
- package/dist/injector/injector.js +1 -7
- package/dist/injector/injector.js.map +1 -1
- package/dist/parser/classify.js.map +1 -1
- package/dist/plugins/types.d.ts +9 -2
- package/dist/ssr/collector.js +11 -1
- package/dist/ssr/collector.js.map +1 -1
- package/dist/styles/types.d.ts +14 -1
- package/dist/tasty.d.ts +6 -6
- package/dist/utils/typography.d.ts +24 -13
- package/dist/utils/typography.js +6 -16
- package/dist/utils/typography.js.map +1 -1
- package/dist/zero/babel.d.ts +10 -4
- package/dist/zero/babel.js +6 -1
- package/dist/zero/babel.js.map +1 -1
- package/docs/adoption.md +286 -0
- package/docs/comparison.md +413 -0
- package/docs/configuration.md +41 -10
- package/docs/debug.md +1 -1
- package/docs/design-system.md +401 -0
- package/docs/{usage.md → dsl.md} +254 -409
- package/docs/getting-started.md +201 -0
- package/docs/injector.md +2 -2
- package/docs/methodology.md +501 -0
- package/docs/runtime.md +291 -0
- package/docs/ssr.md +11 -1
- package/docs/styles.md +2 -2
- package/docs/tasty-static.md +25 -3
- package/package.json +7 -4
- package/dist/tokens/typography.d.ts +0 -19
- package/dist/tokens/typography.js +0 -237
- package/dist/tokens/typography.js.map +0 -1
package/docs/{usage.md → dsl.md}
RENAMED
|
@@ -1,155 +1,8 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Style DSL Reference
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
This is the Tasty style language reference — the value syntax, state mappings, tokens, units, extending semantics, and special declarations that apply to both runtime `tasty()` and build-time `tastyStatic()`.
|
|
4
4
|
|
|
5
|
-
For
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## Quick Start
|
|
10
|
-
|
|
11
|
-
### Creating Your First Component
|
|
12
|
-
|
|
13
|
-
```jsx
|
|
14
|
-
import { tasty } from '@tenphi/tasty';
|
|
15
|
-
|
|
16
|
-
// Basic styled component
|
|
17
|
-
const Card = tasty({
|
|
18
|
-
as: 'div',
|
|
19
|
-
styles: {
|
|
20
|
-
padding: '4x',
|
|
21
|
-
fill: '#white',
|
|
22
|
-
border: true,
|
|
23
|
-
radius: true,
|
|
24
|
-
},
|
|
25
|
-
styleProps: ['padding', 'fill'], // Expose styles as props
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
// Usage
|
|
29
|
-
<Card>Hello World</Card>
|
|
30
|
-
<Card padding="6x" fill="#gray.05">Custom Card</Card>
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
### Extending Existing Components
|
|
34
|
-
|
|
35
|
-
> **Best Practice:** Always prefer creating styled wrappers over using the `styles` prop directly.
|
|
36
|
-
|
|
37
|
-
```jsx
|
|
38
|
-
// Recommended
|
|
39
|
-
const PrimaryButton = tasty(Button, {
|
|
40
|
-
styles: {
|
|
41
|
-
fill: '#purple',
|
|
42
|
-
color: '#white',
|
|
43
|
-
padding: '2x 4x',
|
|
44
|
-
},
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
// Avoid
|
|
48
|
-
<Button styles={{ fill: '#purple' }}>Click me</Button>
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
#### Extending vs. Replacing State Maps
|
|
52
|
-
|
|
53
|
-
When a style property uses a state map, the merge behavior depends on whether the child provides a `''` (default) key:
|
|
54
|
-
|
|
55
|
-
- **No `''` key** — extend mode: parent states are preserved, child adds/overrides
|
|
56
|
-
- **Has `''` key** — replace mode: child defines everything from scratch
|
|
57
|
-
|
|
58
|
-
```jsx
|
|
59
|
-
// Parent has: fill: { '': '#white', hovered: '#blue', disabled: '#gray' }
|
|
60
|
-
|
|
61
|
-
// Extend — no '' key, parent states preserved
|
|
62
|
-
const MyButton = tasty(Button, {
|
|
63
|
-
styles: {
|
|
64
|
-
fill: {
|
|
65
|
-
'loading': '#yellow', // append new state
|
|
66
|
-
'disabled': '#gray.20', // override existing state in place
|
|
67
|
-
},
|
|
68
|
-
},
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
// Replace — has '' key, parent states dropped
|
|
72
|
-
const MyButton = tasty(Button, {
|
|
73
|
-
styles: {
|
|
74
|
-
fill: {
|
|
75
|
-
'': '#red',
|
|
76
|
-
'hovered': '#blue',
|
|
77
|
-
},
|
|
78
|
-
},
|
|
79
|
-
});
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
Use `'@inherit'` to pull a parent state value. In extend mode it repositions the state; in replace mode it cherry-picks it:
|
|
83
|
-
|
|
84
|
-
```jsx
|
|
85
|
-
// Extend mode: reposition disabled to end (highest CSS priority)
|
|
86
|
-
fill: {
|
|
87
|
-
'loading': '#yellow',
|
|
88
|
-
disabled: '@inherit',
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Replace mode: cherry-pick disabled from parent
|
|
92
|
-
fill: {
|
|
93
|
-
'': '#red',
|
|
94
|
-
disabled: '@inherit',
|
|
95
|
-
}
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
Use `null` inside a state map to remove a state, or `false` to block it entirely (tombstone):
|
|
99
|
-
|
|
100
|
-
```jsx
|
|
101
|
-
fill: { pressed: null } // removes pressed from the result
|
|
102
|
-
fill: { disabled: false } // tombstone — no CSS for disabled, blocks recipe too
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
#### Resetting Properties with `null` and `false`
|
|
106
|
-
|
|
107
|
-
```jsx
|
|
108
|
-
const SimpleButton = tasty(Button, {
|
|
109
|
-
styles: {
|
|
110
|
-
fill: null, // discard parent's fill, let recipe fill in
|
|
111
|
-
border: false, // no border at all (tombstone — blocks recipe too)
|
|
112
|
-
},
|
|
113
|
-
});
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
| Value | Meaning | Recipe fills in? |
|
|
117
|
-
|-------|---------|-----------------|
|
|
118
|
-
| `undefined` | Not provided — parent preserved | N/A |
|
|
119
|
-
| `null` | Intentional unset — parent discarded | Yes |
|
|
120
|
-
| `false` | Tombstone — blocks everything | No |
|
|
121
|
-
|
|
122
|
-
### Essential Patterns
|
|
123
|
-
|
|
124
|
-
```jsx
|
|
125
|
-
// State-based styling
|
|
126
|
-
const InteractiveCard = tasty({
|
|
127
|
-
styles: {
|
|
128
|
-
fill: {
|
|
129
|
-
'': '#white',
|
|
130
|
-
'hovered': '#gray.05',
|
|
131
|
-
'pressed': '#gray.10',
|
|
132
|
-
},
|
|
133
|
-
},
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
// Using design tokens
|
|
137
|
-
const TokenCard = tasty({
|
|
138
|
-
styles: {
|
|
139
|
-
fill: '#surface', // Color token
|
|
140
|
-
color: '#text', // Color token
|
|
141
|
-
padding: '2x', // Custom unit (gap × 2)
|
|
142
|
-
radius: '1r', // Custom unit (border-radius)
|
|
143
|
-
border: '1bw solid #border', // Border width token
|
|
144
|
-
},
|
|
145
|
-
});
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
---
|
|
149
|
-
|
|
150
|
-
## Configuration
|
|
151
|
-
|
|
152
|
-
For tokens, recipes, custom units, style handlers, and other global settings, see **[Configuration](configuration.md)**.
|
|
5
|
+
For the runtime React API (`tasty()`, hooks, component props), see [Runtime API](runtime.md). For all enhanced style properties, see [Style Properties](styles.md). For global configuration, see [Configuration](configuration.md).
|
|
153
6
|
|
|
154
7
|
---
|
|
155
8
|
|
|
@@ -202,43 +55,7 @@ mods={{ hovered: true, theme: 'danger' }}
|
|
|
202
55
|
|
|
203
56
|
---
|
|
204
57
|
|
|
205
|
-
##
|
|
206
|
-
|
|
207
|
-
### Component Creation
|
|
208
|
-
|
|
209
|
-
```jsx
|
|
210
|
-
// Create new element
|
|
211
|
-
const Box = tasty({
|
|
212
|
-
as: 'div',
|
|
213
|
-
styles: { /* styles */ },
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
// Extend existing component
|
|
217
|
-
const StyledButton = tasty(Button, {
|
|
218
|
-
styles: { /* additional styles */ },
|
|
219
|
-
});
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
### Style Props
|
|
223
|
-
|
|
224
|
-
Use `styleProps` to expose style properties as direct component props:
|
|
225
|
-
|
|
226
|
-
```jsx
|
|
227
|
-
const FlexibleBox = tasty({
|
|
228
|
-
as: 'div',
|
|
229
|
-
styles: {
|
|
230
|
-
display: 'flex',
|
|
231
|
-
padding: '2x',
|
|
232
|
-
},
|
|
233
|
-
styleProps: ['gap', 'align', 'placeContent', 'fill'],
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
<FlexibleBox gap="2x" align="center" fill="#surface">
|
|
237
|
-
Content
|
|
238
|
-
</FlexibleBox>
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
### Color Tokens & Opacity
|
|
58
|
+
## Color Tokens & Opacity
|
|
242
59
|
|
|
243
60
|
```jsx
|
|
244
61
|
color: '#purple', // Full opacity
|
|
@@ -249,7 +66,9 @@ fill: '#current.5', // → color-mix(in oklab, currentcolor 50%, transpa
|
|
|
249
66
|
color: '(#primary, #secondary)', // Fallback syntax
|
|
250
67
|
```
|
|
251
68
|
|
|
252
|
-
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Built-in Units
|
|
253
72
|
|
|
254
73
|
| Unit | Description | Example | CSS Output |
|
|
255
74
|
|------|-------------|---------|------------|
|
|
@@ -264,9 +83,11 @@ color: '(#primary, #secondary)', // Fallback syntax
|
|
|
264
83
|
|
|
265
84
|
You can register additional custom units via [`configure()`](configuration.md#options).
|
|
266
85
|
|
|
267
|
-
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Replace Tokens
|
|
268
89
|
|
|
269
|
-
Tokens defined via [`configure({
|
|
90
|
+
Tokens defined via [`configure({ replaceTokens })`](configuration.md#replace-tokens-parse-time-substitution) are replaced at parse time and baked into the generated CSS:
|
|
270
91
|
|
|
271
92
|
```jsx
|
|
272
93
|
const Card = tasty({
|
|
@@ -278,7 +99,9 @@ const Card = tasty({
|
|
|
278
99
|
});
|
|
279
100
|
```
|
|
280
101
|
|
|
281
|
-
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Recipes
|
|
282
105
|
|
|
283
106
|
Apply predefined style bundles (defined via [`configure({ recipes })`](configuration.md#recipes)) using the `recipe` style property:
|
|
284
107
|
|
|
@@ -323,7 +146,82 @@ const Custom = tasty({
|
|
|
323
146
|
});
|
|
324
147
|
```
|
|
325
148
|
|
|
326
|
-
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Extending vs. Replacing State Maps
|
|
152
|
+
|
|
153
|
+
When a style property uses a state map, the merge behavior depends on whether the child provides a `''` (default) key:
|
|
154
|
+
|
|
155
|
+
- **No `''` key** — extend mode: parent states are preserved, child adds/overrides
|
|
156
|
+
- **Has `''` key** — replace mode: child defines everything from scratch
|
|
157
|
+
|
|
158
|
+
```jsx
|
|
159
|
+
// Parent has: fill: { '': '#white', hovered: '#blue', disabled: '#gray' }
|
|
160
|
+
|
|
161
|
+
// Extend — no '' key, parent states preserved
|
|
162
|
+
const MyButton = tasty(Button, {
|
|
163
|
+
styles: {
|
|
164
|
+
fill: {
|
|
165
|
+
'loading': '#yellow', // append new state
|
|
166
|
+
'disabled': '#gray.20', // override existing state in place
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// Replace — has '' key, parent states dropped
|
|
172
|
+
const MyButton = tasty(Button, {
|
|
173
|
+
styles: {
|
|
174
|
+
fill: {
|
|
175
|
+
'': '#red',
|
|
176
|
+
'hovered': '#blue',
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
});
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Use `'@inherit'` to pull a parent state value. In extend mode it repositions the state; in replace mode it cherry-picks it:
|
|
183
|
+
|
|
184
|
+
```jsx
|
|
185
|
+
// Extend mode: reposition disabled to end (highest CSS priority)
|
|
186
|
+
fill: {
|
|
187
|
+
'loading': '#yellow',
|
|
188
|
+
disabled: '@inherit',
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Replace mode: cherry-pick disabled from parent
|
|
192
|
+
fill: {
|
|
193
|
+
'': '#red',
|
|
194
|
+
disabled: '@inherit',
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Use `null` inside a state map to remove a state, or `false` to block it entirely (tombstone):
|
|
199
|
+
|
|
200
|
+
```jsx
|
|
201
|
+
fill: { pressed: null } // removes pressed from the result
|
|
202
|
+
fill: { disabled: false } // tombstone — no CSS for disabled, blocks recipe too
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Resetting Properties with `null` and `false`
|
|
206
|
+
|
|
207
|
+
```jsx
|
|
208
|
+
const SimpleButton = tasty(Button, {
|
|
209
|
+
styles: {
|
|
210
|
+
fill: null, // discard parent's fill, let recipe fill in
|
|
211
|
+
border: false, // no border at all (tombstone — blocks recipe too)
|
|
212
|
+
},
|
|
213
|
+
});
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
| Value | Meaning | Recipe fills in? |
|
|
217
|
+
|-------|---------|-----------------|
|
|
218
|
+
| `undefined` | Not provided — parent preserved | N/A |
|
|
219
|
+
| `null` | Intentional unset — parent discarded | Yes |
|
|
220
|
+
| `false` | Tombstone — blocks everything | No |
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## Advanced States (`@` prefix)
|
|
327
225
|
|
|
328
226
|
| Prefix | Purpose | Example |
|
|
329
227
|
|--------|---------|---------|
|
|
@@ -339,7 +237,156 @@ const Custom = tasty({
|
|
|
339
237
|
| `:not()` | CSS `:not()` negation (prefer `!:is()`) | `:not(:first-child)` |
|
|
340
238
|
| `:where()` | CSS `:where()` (zero specificity) | `:where(Section)` |
|
|
341
239
|
|
|
342
|
-
|
|
240
|
+
### `@media(...)` — Media Queries
|
|
241
|
+
|
|
242
|
+
Media queries support dimension shorthands and custom unit expansion:
|
|
243
|
+
|
|
244
|
+
| Shorthand | Expands to |
|
|
245
|
+
|-----------|------------|
|
|
246
|
+
| `w` | `width` |
|
|
247
|
+
| `h` | `height` |
|
|
248
|
+
|
|
249
|
+
```jsx
|
|
250
|
+
fill: {
|
|
251
|
+
'': '#surface',
|
|
252
|
+
'@media(w < 768px)': '#surface-mobile',
|
|
253
|
+
'@media(600px <= w < 1200px)': '#surface-tablet',
|
|
254
|
+
'@media(prefers-color-scheme: dark)': '#surface-dark',
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
| Tasty syntax | CSS output |
|
|
259
|
+
|--------------|------------|
|
|
260
|
+
| `@media(w < 768px)` | `@media (width < 768px)` |
|
|
261
|
+
| `@media(600px <= w < 1200px)` | `@media (600px <= width < 1200px)` |
|
|
262
|
+
| `@media:print` | `@media print` |
|
|
263
|
+
| `@media:screen` | `@media screen` |
|
|
264
|
+
| `@media(prefers-color-scheme: dark)` | `@media (prefers-color-scheme: dark)` |
|
|
265
|
+
| `@media(prefers-reduced-motion)` | `@media (prefers-reduced-motion)` |
|
|
266
|
+
|
|
267
|
+
Custom units work inside media queries: `@media(w < 40x)` → `@media (width < calc(var(--gap) * 40))`.
|
|
268
|
+
|
|
269
|
+
In practice, define state aliases via `configure({ states })` and use `@mobile` instead of writing the full query in every component.
|
|
270
|
+
|
|
271
|
+
### `@(...)` — Container Queries
|
|
272
|
+
|
|
273
|
+
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`.
|
|
274
|
+
|
|
275
|
+
| Shorthand | Expands to |
|
|
276
|
+
|-----------|------------|
|
|
277
|
+
| `w` | `width` |
|
|
278
|
+
| `h` | `height` |
|
|
279
|
+
| `is` | `inline-size` |
|
|
280
|
+
| `bs` | `block-size` |
|
|
281
|
+
|
|
282
|
+
```jsx
|
|
283
|
+
const Panel = tasty({
|
|
284
|
+
styles: {
|
|
285
|
+
flow: {
|
|
286
|
+
'': 'column',
|
|
287
|
+
'@(layout, w >= 600px)': 'row',
|
|
288
|
+
},
|
|
289
|
+
},
|
|
290
|
+
});
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
| Tasty syntax | CSS output |
|
|
294
|
+
|--------------|------------|
|
|
295
|
+
| `@(layout, w < 600px)` | `@container layout (width < 600px)` |
|
|
296
|
+
| `@(w < 600px)` | `@container (width < 600px)` |
|
|
297
|
+
| `@(layout, $variant=danger)` | `@container layout style(--variant: "danger")` |
|
|
298
|
+
| `@(layout, $compact)` | `@container layout style(--compact)` |
|
|
299
|
+
| `@(scroll-state(stuck: top))` | `@container scroll-state(stuck: top)` |
|
|
300
|
+
| `@(nav, scroll-state(stuck: top))` | `@container nav scroll-state(stuck: top)` |
|
|
301
|
+
|
|
302
|
+
Container style queries use `$prop` (boolean) or `$prop=value` syntax, which maps to CSS `style(--prop)` or `style(--prop: "value")`.
|
|
303
|
+
|
|
304
|
+
### `@supports(...)` — Feature Queries
|
|
305
|
+
|
|
306
|
+
Feature queries test CSS property support. Use `$` as the first argument to test selector support:
|
|
307
|
+
|
|
308
|
+
| Tasty syntax | CSS output |
|
|
309
|
+
|--------------|------------|
|
|
310
|
+
| `@supports(display: grid)` | `@supports (display: grid)` |
|
|
311
|
+
| `@supports($, :has(*))` | `@supports selector(:has(*))` |
|
|
312
|
+
| `!@supports(display: grid)` | `@supports (not (display: grid))` |
|
|
313
|
+
|
|
314
|
+
```jsx
|
|
315
|
+
display: {
|
|
316
|
+
'': 'flex',
|
|
317
|
+
'@supports(display: grid)': 'grid',
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
### `@root(...)` — Root Element States
|
|
322
|
+
|
|
323
|
+
Root states generate selectors on the `:root` element. They are useful for theme modes, feature flags, and other page-level conditions:
|
|
324
|
+
|
|
325
|
+
```jsx
|
|
326
|
+
color: {
|
|
327
|
+
'': '#text',
|
|
328
|
+
'@root(schema=dark)': '#text-on-dark',
|
|
329
|
+
'@root(.premium-user)': '#gold',
|
|
330
|
+
}
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
| Tasty syntax | CSS selector |
|
|
334
|
+
|--------------|-------------|
|
|
335
|
+
| `@root(schema=dark)` | `:root[data-schema="dark"]` |
|
|
336
|
+
| `@root(hovered)` | `:root[data-hovered]` |
|
|
337
|
+
| `@root(.premium-user)` | `:root.premium-user` |
|
|
338
|
+
| `@root([lang="en"])` | `:root[lang="en"]` |
|
|
339
|
+
| `!@root(schema=dark)` | `:root:not([data-schema="dark"])` |
|
|
340
|
+
|
|
341
|
+
Root conditions are prepended to the component selector: `:root[data-schema="dark"] .t0.t0 { ... }`.
|
|
342
|
+
|
|
343
|
+
### `@own(...)` — Sub-element's Own State
|
|
344
|
+
|
|
345
|
+
By default, state keys in sub-element styles refer to the root component's state context. Use `@own(...)` when the sub-element should react to its own state:
|
|
346
|
+
|
|
347
|
+
```jsx
|
|
348
|
+
const Nav = tasty({
|
|
349
|
+
styles: {
|
|
350
|
+
NavItem: {
|
|
351
|
+
color: {
|
|
352
|
+
'': '#text',
|
|
353
|
+
'@own(:hover)': '#primary',
|
|
354
|
+
'@own(:focus-visible)': '#primary',
|
|
355
|
+
'selected': '#primary', // root-level modifier
|
|
356
|
+
},
|
|
357
|
+
},
|
|
358
|
+
},
|
|
359
|
+
elements: { NavItem: 'a' },
|
|
360
|
+
});
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
| Tasty syntax (inside sub-element) | CSS output |
|
|
364
|
+
|-----------------------------------|------------|
|
|
365
|
+
| `@own(:hover)` | `:hover` on the sub-element selector |
|
|
366
|
+
| `@own(hovered)` | `[data-hovered]` on the sub-element selector |
|
|
367
|
+
| `@own(theme=dark)` | `[data-theme="dark"]` on the sub-element selector |
|
|
368
|
+
|
|
369
|
+
`@own()` is only valid inside sub-element styles. Using it on root styles emits a warning and is treated as a regular modifier.
|
|
370
|
+
|
|
371
|
+
### `@starting` — Entry Animation
|
|
372
|
+
|
|
373
|
+
Wraps the rule in `@starting-style`, enabling CSS entry animations for elements as they appear in the DOM:
|
|
374
|
+
|
|
375
|
+
```jsx
|
|
376
|
+
const FadeIn = tasty({
|
|
377
|
+
styles: {
|
|
378
|
+
opacity: { '': '1', '@starting': '0' },
|
|
379
|
+
transform: { '': 'scale(1)', '@starting': 'scale(0.95)' },
|
|
380
|
+
transition: 'opacity 0.3s, translate 0.3s',
|
|
381
|
+
},
|
|
382
|
+
});
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
| Tasty syntax | CSS output |
|
|
386
|
+
|--------------|------------|
|
|
387
|
+
| `@starting` | `@starting-style { .t0.t0 { ... } }` |
|
|
388
|
+
|
|
389
|
+
### `@parent(...)` — Parent Element States
|
|
343
390
|
|
|
344
391
|
Style based on ancestor element attributes. Uses `:is([selector] *)` / `:not([selector] *)` for symmetric, composable parent checks. Boolean logic (`&`, `|`, `!`) is supported inside `@parent()`.
|
|
345
392
|
|
|
@@ -381,7 +428,7 @@ const Card = tasty({
|
|
|
381
428
|
// → .t0.t0:is([data-hovered] *) [data-element="Label"]
|
|
382
429
|
```
|
|
383
430
|
|
|
384
|
-
|
|
431
|
+
### `:is()`, `:has()` — CSS Structural Pseudo-classes
|
|
385
432
|
|
|
386
433
|
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 `*`.
|
|
387
434
|
|
|
@@ -431,15 +478,9 @@ Combine with other states using boolean logic:
|
|
|
431
478
|
|
|
432
479
|
---
|
|
433
480
|
|
|
434
|
-
##
|
|
435
|
-
|
|
436
|
-
For a complete reference of all enhanced style properties — syntax, values, modifiers, and recommendations — see **[Style Properties Reference](styles.md)**.
|
|
481
|
+
## Keyframes
|
|
437
482
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
## Advanced Features
|
|
441
|
-
|
|
442
|
-
### Keyframes
|
|
483
|
+
Define animations inline using the `@keyframes` key in styles:
|
|
443
484
|
|
|
444
485
|
```jsx
|
|
445
486
|
const Pulse = tasty({
|
|
@@ -455,7 +496,9 @@ const Pulse = tasty({
|
|
|
455
496
|
});
|
|
456
497
|
```
|
|
457
498
|
|
|
458
|
-
|
|
499
|
+
---
|
|
500
|
+
|
|
501
|
+
## Properties (`@property`)
|
|
459
502
|
|
|
460
503
|
CSS cannot transition or animate custom properties unless the browser knows their type. Tasty solves this automatically — when you assign a concrete value to a custom property, the type is inferred and a CSS `@property` rule is registered behind the scenes:
|
|
461
504
|
|
|
@@ -480,216 +523,18 @@ Use explicit `@properties` when you need non-default settings like `inherits: fa
|
|
|
480
523
|
},
|
|
481
524
|
```
|
|
482
525
|
|
|
483
|
-
### Variants & Theming
|
|
484
|
-
|
|
485
|
-
```jsx
|
|
486
|
-
const Button = tasty({
|
|
487
|
-
styles: {
|
|
488
|
-
padding: '2x 4x',
|
|
489
|
-
border: true,
|
|
490
|
-
},
|
|
491
|
-
variants: {
|
|
492
|
-
default: { fill: '#blue', color: '#white' },
|
|
493
|
-
danger: { fill: '#red', color: '#white' },
|
|
494
|
-
outline: { fill: 'transparent', color: '#blue', border: '1bw solid #blue' },
|
|
495
|
-
},
|
|
496
|
-
});
|
|
497
|
-
|
|
498
|
-
<Button variant="danger">Delete</Button>
|
|
499
|
-
```
|
|
500
|
-
|
|
501
|
-
#### Extending Variants with Base State Maps
|
|
502
|
-
|
|
503
|
-
When base `styles` contain an extend-mode state map (an object **without** a `''` key), it is applied **after** the variant merge. This lets you add or override states across all variants without repeating yourself:
|
|
504
|
-
|
|
505
|
-
```jsx
|
|
506
|
-
const Badge = tasty({
|
|
507
|
-
styles: {
|
|
508
|
-
padding: '1x 2x',
|
|
509
|
-
// No '' key → extend mode: appended to every variant's border
|
|
510
|
-
border: {
|
|
511
|
-
'type=primary': '#clear',
|
|
512
|
-
},
|
|
513
|
-
},
|
|
514
|
-
variants: {
|
|
515
|
-
primary: {
|
|
516
|
-
border: { '': '#white.2', pressed: '#primary-text', disabled: '#clear' },
|
|
517
|
-
fill: { '': '#white #primary', hovered: '#white #primary-text' },
|
|
518
|
-
},
|
|
519
|
-
secondary: {
|
|
520
|
-
border: { '': '#primary.15', pressed: '#primary.3' },
|
|
521
|
-
fill: '#primary.10',
|
|
522
|
-
},
|
|
523
|
-
},
|
|
524
|
-
});
|
|
525
|
-
|
|
526
|
-
// Both variants get 'type=primary': '#clear' appended to their border map
|
|
527
|
-
```
|
|
528
|
-
|
|
529
|
-
Properties that are **not** extend-mode (simple values, state maps with `''`, `null`, `false`, selectors, sub-elements) merge with variants as before — the variant can fully replace them.
|
|
530
|
-
|
|
531
|
-
### Sub-element Styling
|
|
532
|
-
|
|
533
|
-
Sub-elements are inner parts of a compound component, styled via capitalized keys in `styles` and identified by `data-element` attributes in the DOM.
|
|
534
|
-
|
|
535
|
-
> **Best Practice:** Use the `elements` prop to declare sub-element components. This gives you typed, reusable sub-components (`Card.Title`, `Card.Content`) instead of manually writing `data-element` attributes.
|
|
536
|
-
|
|
537
|
-
```jsx
|
|
538
|
-
const Card = tasty({
|
|
539
|
-
styles: {
|
|
540
|
-
padding: '4x',
|
|
541
|
-
Title: { preset: 'h3', color: '#primary' },
|
|
542
|
-
Content: { color: '#text' },
|
|
543
|
-
},
|
|
544
|
-
elements: {
|
|
545
|
-
Title: 'h3',
|
|
546
|
-
Content: 'div',
|
|
547
|
-
},
|
|
548
|
-
});
|
|
549
|
-
|
|
550
|
-
// Sub-components automatically get data-element attributes
|
|
551
|
-
<Card>
|
|
552
|
-
<Card.Title>Card Title</Card.Title>
|
|
553
|
-
<Card.Content>Card content</Card.Content>
|
|
554
|
-
</Card>
|
|
555
|
-
```
|
|
556
|
-
|
|
557
|
-
Each entry in `elements` can be a tag name string or a config object:
|
|
558
|
-
|
|
559
|
-
```jsx
|
|
560
|
-
elements: {
|
|
561
|
-
Title: 'h3', // shorthand: tag name only
|
|
562
|
-
Icon: { as: 'span', qa: 'card-icon' }, // full form: tag + QA attribute
|
|
563
|
-
}
|
|
564
|
-
```
|
|
565
|
-
|
|
566
|
-
The sub-components produced by `elements` support `mods`, `tokens`, `isDisabled`, `isHidden`, and `isChecked` props — the same modifier interface as the root component.
|
|
567
|
-
|
|
568
|
-
If you don't need sub-components (e.g., the inner elements are already rendered by a third-party library), you can still style them by key alone — just omit `elements` and apply `data-element` manually:
|
|
569
|
-
|
|
570
|
-
```jsx
|
|
571
|
-
const Card = tasty({
|
|
572
|
-
styles: {
|
|
573
|
-
padding: '4x',
|
|
574
|
-
Title: { preset: 'h3', color: '#primary' },
|
|
575
|
-
},
|
|
576
|
-
});
|
|
577
|
-
|
|
578
|
-
<Card>
|
|
579
|
-
<div data-element="Title">Card Title</div>
|
|
580
|
-
</Card>
|
|
581
|
-
```
|
|
582
|
-
|
|
583
|
-
#### Selector Affix (`$`)
|
|
584
|
-
|
|
585
|
-
Control how a sub-element selector attaches to the root selector using the `$` property inside the sub-element's styles:
|
|
586
|
-
|
|
587
|
-
| Pattern | Result | Description |
|
|
588
|
-
|---------|--------|-------------|
|
|
589
|
-
| *(none)* | ` [el]` | Descendant (default) |
|
|
590
|
-
| `>` | `> [el]` | Direct child |
|
|
591
|
-
| `>Body>Row>` | `> [Body] > [Row] > [el]` | Chained elements |
|
|
592
|
-
| `::before` | `::before` | Root pseudo (no key) |
|
|
593
|
-
| `@::before` | `[el]::before` | Pseudo on the sub-element |
|
|
594
|
-
| `>@:hover` | `> [el]:hover` | Pseudo-class on the sub-element |
|
|
595
|
-
| `>@.active` | `> [el].active` | Class on the sub-element |
|
|
596
|
-
|
|
597
|
-
The `@` placeholder marks exactly where the `[data-element="..."]` selector is injected, allowing you to attach pseudo-classes, pseudo-elements, or class selectors directly to the sub-element instead of the root:
|
|
598
|
-
|
|
599
|
-
```jsx
|
|
600
|
-
const List = tasty({
|
|
601
|
-
styles: {
|
|
602
|
-
Item: {
|
|
603
|
-
$: '>@:last-child',
|
|
604
|
-
border: 'none',
|
|
605
|
-
},
|
|
606
|
-
},
|
|
607
|
-
});
|
|
608
|
-
// → .t0 > [data-element="Item"]:last-child { border: none }
|
|
609
|
-
```
|
|
610
|
-
|
|
611
526
|
---
|
|
612
527
|
|
|
613
|
-
##
|
|
614
|
-
|
|
615
|
-
### useStyles
|
|
616
|
-
|
|
617
|
-
```tsx
|
|
618
|
-
import { useStyles } from '@tenphi/tasty';
|
|
619
|
-
|
|
620
|
-
function MyComponent() {
|
|
621
|
-
const { className } = useStyles({
|
|
622
|
-
padding: '2x',
|
|
623
|
-
fill: '#surface',
|
|
624
|
-
radius: '1r',
|
|
625
|
-
});
|
|
626
|
-
|
|
627
|
-
return <div className={className}>Styled content</div>;
|
|
628
|
-
}
|
|
629
|
-
```
|
|
630
|
-
|
|
631
|
-
### useGlobalStyles
|
|
632
|
-
|
|
633
|
-
```tsx
|
|
634
|
-
import { useGlobalStyles } from '@tenphi/tasty';
|
|
635
|
-
|
|
636
|
-
function ThemeStyles() {
|
|
637
|
-
useGlobalStyles('.card', {
|
|
638
|
-
padding: '4x',
|
|
639
|
-
fill: '#surface',
|
|
640
|
-
radius: '1r',
|
|
641
|
-
});
|
|
642
|
-
|
|
643
|
-
return null;
|
|
644
|
-
}
|
|
645
|
-
```
|
|
646
|
-
|
|
647
|
-
### useRawCSS
|
|
648
|
-
|
|
649
|
-
```tsx
|
|
650
|
-
import { useRawCSS } from '@tenphi/tasty';
|
|
651
|
-
|
|
652
|
-
function GlobalReset() {
|
|
653
|
-
useRawCSS(`
|
|
654
|
-
body { margin: 0; padding: 0; }
|
|
655
|
-
`);
|
|
656
|
-
|
|
657
|
-
return null;
|
|
658
|
-
}
|
|
659
|
-
```
|
|
660
|
-
|
|
661
|
-
### useMergeStyles
|
|
662
|
-
|
|
663
|
-
```tsx
|
|
664
|
-
import { useMergeStyles } from '@tenphi/tasty';
|
|
665
|
-
|
|
666
|
-
function MyTabs({ styles, tabListStyles, prefixStyles }) {
|
|
667
|
-
const mergedStyles = useMergeStyles(styles, {
|
|
668
|
-
TabList: tabListStyles,
|
|
669
|
-
Prefix: prefixStyles,
|
|
670
|
-
});
|
|
528
|
+
## Style Properties
|
|
671
529
|
|
|
672
|
-
|
|
673
|
-
}
|
|
674
|
-
```
|
|
530
|
+
For a complete reference of all enhanced style properties — syntax, values, modifiers, and recommendations — see **[Style Properties Reference](styles.md)**.
|
|
675
531
|
|
|
676
532
|
---
|
|
677
533
|
|
|
678
|
-
##
|
|
679
|
-
|
|
680
|
-
### Do's
|
|
681
|
-
|
|
682
|
-
- Use styled wrappers instead of `styles` prop directly
|
|
683
|
-
- Use design tokens and custom units (`#text`, `2x`, `1r`)
|
|
684
|
-
- Use semantic transition names (`theme 0.3s`)
|
|
685
|
-
- Use `elements` prop to declare typed sub-components for compound components
|
|
686
|
-
- Use `styleProps` for component APIs
|
|
687
|
-
- Use `tokens` prop for dynamic values
|
|
688
|
-
|
|
689
|
-
### Don'ts
|
|
534
|
+
## Learn more
|
|
690
535
|
|
|
691
|
-
-
|
|
692
|
-
-
|
|
693
|
-
-
|
|
694
|
-
-
|
|
695
|
-
-
|
|
536
|
+
- **[Runtime API](runtime.md)** — `tasty()` factory, component props, variants, sub-elements, hooks
|
|
537
|
+
- **[Methodology](methodology.md)** — Recommended patterns: root + sub-elements, styleProps, tokens, wrapping
|
|
538
|
+
- **[Configuration](configuration.md)** — Tokens, recipes, custom units, style handlers, TypeScript extensions
|
|
539
|
+
- **[Style Properties](styles.md)** — Complete reference for all enhanced style properties
|
|
540
|
+
- **[Zero Runtime (tastyStatic)](tasty-static.md)** — Build-time static styling with Babel plugin
|