@tenphi/tasty 0.0.0-snapshot.08a6610
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/LICENSE +21 -0
- package/README.md +666 -0
- package/dist/_virtual/_rolldown/runtime.js +8 -0
- package/dist/chunks/cacheKey.js +70 -0
- package/dist/chunks/cacheKey.js.map +1 -0
- package/dist/chunks/definitions.d.ts +37 -0
- package/dist/chunks/definitions.js +259 -0
- package/dist/chunks/definitions.js.map +1 -0
- package/dist/chunks/renderChunk.js +61 -0
- package/dist/chunks/renderChunk.js.map +1 -0
- package/dist/config.d.ts +311 -0
- package/dist/config.js +458 -0
- package/dist/config.js.map +1 -0
- package/dist/core/index.d.ts +32 -0
- package/dist/core/index.js +26 -0
- package/dist/debug.d.ts +204 -0
- package/dist/debug.js +733 -0
- package/dist/debug.js.map +1 -0
- package/dist/hooks/useGlobalStyles.d.ts +30 -0
- package/dist/hooks/useGlobalStyles.js +83 -0
- package/dist/hooks/useGlobalStyles.js.map +1 -0
- package/dist/hooks/useKeyframes.d.ts +56 -0
- package/dist/hooks/useKeyframes.js +69 -0
- package/dist/hooks/useKeyframes.js.map +1 -0
- package/dist/hooks/useProperty.d.ts +79 -0
- package/dist/hooks/useProperty.js +114 -0
- package/dist/hooks/useProperty.js.map +1 -0
- package/dist/hooks/useRawCSS.d.ts +53 -0
- package/dist/hooks/useRawCSS.js +40 -0
- package/dist/hooks/useRawCSS.js.map +1 -0
- package/dist/hooks/useStyles.d.ts +45 -0
- package/dist/hooks/useStyles.js +248 -0
- package/dist/hooks/useStyles.js.map +1 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.js +33 -0
- package/dist/injector/index.d.ts +165 -0
- package/dist/injector/index.js +162 -0
- package/dist/injector/index.js.map +1 -0
- package/dist/injector/injector.d.ts +148 -0
- package/dist/injector/injector.js +430 -0
- package/dist/injector/injector.js.map +1 -0
- package/dist/injector/sheet-manager.d.ts +136 -0
- package/dist/injector/sheet-manager.js +729 -0
- package/dist/injector/sheet-manager.js.map +1 -0
- package/dist/injector/types.d.ts +144 -0
- package/dist/keyframes/index.js +206 -0
- package/dist/keyframes/index.js.map +1 -0
- package/dist/parser/classify.js +319 -0
- package/dist/parser/classify.js.map +1 -0
- package/dist/parser/const.js +33 -0
- package/dist/parser/const.js.map +1 -0
- package/dist/parser/lru.js +109 -0
- package/dist/parser/lru.js.map +1 -0
- package/dist/parser/parser.d.ts +25 -0
- package/dist/parser/parser.js +116 -0
- package/dist/parser/parser.js.map +1 -0
- package/dist/parser/tokenizer.js +69 -0
- package/dist/parser/tokenizer.js.map +1 -0
- package/dist/parser/types.d.ts +51 -0
- package/dist/parser/types.js +46 -0
- package/dist/parser/types.js.map +1 -0
- package/dist/pipeline/conditions.d.ts +134 -0
- package/dist/pipeline/conditions.js +406 -0
- package/dist/pipeline/conditions.js.map +1 -0
- package/dist/pipeline/exclusive.js +231 -0
- package/dist/pipeline/exclusive.js.map +1 -0
- package/dist/pipeline/index.d.ts +53 -0
- package/dist/pipeline/index.js +660 -0
- package/dist/pipeline/index.js.map +1 -0
- package/dist/pipeline/materialize.js +856 -0
- package/dist/pipeline/materialize.js.map +1 -0
- package/dist/pipeline/parseStateKey.d.ts +15 -0
- package/dist/pipeline/parseStateKey.js +451 -0
- package/dist/pipeline/parseStateKey.js.map +1 -0
- package/dist/pipeline/simplify.js +516 -0
- package/dist/pipeline/simplify.js.map +1 -0
- package/dist/pipeline/warnings.js +18 -0
- package/dist/pipeline/warnings.js.map +1 -0
- package/dist/plugins/okhsl-plugin.d.ts +35 -0
- package/dist/plugins/okhsl-plugin.js +371 -0
- package/dist/plugins/okhsl-plugin.js.map +1 -0
- package/dist/plugins/types.d.ts +76 -0
- package/dist/properties/index.js +236 -0
- package/dist/properties/index.js.map +1 -0
- package/dist/properties/property-type-resolver.d.ts +24 -0
- package/dist/properties/property-type-resolver.js +91 -0
- package/dist/properties/property-type-resolver.js.map +1 -0
- package/dist/ssr/astro.d.ts +29 -0
- package/dist/ssr/astro.js +65 -0
- package/dist/ssr/astro.js.map +1 -0
- package/dist/ssr/async-storage.d.ts +17 -0
- package/dist/ssr/async-storage.js +35 -0
- package/dist/ssr/async-storage.js.map +1 -0
- package/dist/ssr/collect-auto-properties.js +40 -0
- package/dist/ssr/collect-auto-properties.js.map +1 -0
- package/dist/ssr/collector.d.ts +85 -0
- package/dist/ssr/collector.js +183 -0
- package/dist/ssr/collector.js.map +1 -0
- package/dist/ssr/context.d.ts +8 -0
- package/dist/ssr/context.js +14 -0
- package/dist/ssr/context.js.map +1 -0
- package/dist/ssr/format-global-rules.js +22 -0
- package/dist/ssr/format-global-rules.js.map +1 -0
- package/dist/ssr/format-keyframes.js +70 -0
- package/dist/ssr/format-keyframes.js.map +1 -0
- package/dist/ssr/format-property.js +48 -0
- package/dist/ssr/format-property.js.map +1 -0
- package/dist/ssr/format-rules.js +70 -0
- package/dist/ssr/format-rules.js.map +1 -0
- package/dist/ssr/hydrate.d.ts +22 -0
- package/dist/ssr/hydrate.js +50 -0
- package/dist/ssr/hydrate.js.map +1 -0
- package/dist/ssr/index.d.ts +5 -0
- package/dist/ssr/index.js +12 -0
- package/dist/ssr/index.js.map +1 -0
- package/dist/ssr/next.d.ts +45 -0
- package/dist/ssr/next.js +71 -0
- package/dist/ssr/next.js.map +1 -0
- package/dist/ssr/ssr-collector-ref.js +12 -0
- package/dist/ssr/ssr-collector-ref.js.map +1 -0
- package/dist/states/index.d.ts +49 -0
- package/dist/states/index.js +416 -0
- package/dist/states/index.js.map +1 -0
- package/dist/static/index.d.ts +5 -0
- package/dist/static/index.js +5 -0
- package/dist/static/tastyStatic.d.ts +46 -0
- package/dist/static/tastyStatic.js +31 -0
- package/dist/static/tastyStatic.js.map +1 -0
- package/dist/static/types.d.ts +49 -0
- package/dist/static/types.js +24 -0
- package/dist/static/types.js.map +1 -0
- package/dist/styles/align.d.ts +15 -0
- package/dist/styles/align.js +14 -0
- package/dist/styles/align.js.map +1 -0
- package/dist/styles/border.d.ts +25 -0
- package/dist/styles/border.js +114 -0
- package/dist/styles/border.js.map +1 -0
- package/dist/styles/color.d.ts +14 -0
- package/dist/styles/color.js +23 -0
- package/dist/styles/color.js.map +1 -0
- package/dist/styles/createStyle.js +77 -0
- package/dist/styles/createStyle.js.map +1 -0
- package/dist/styles/dimension.js +97 -0
- package/dist/styles/dimension.js.map +1 -0
- package/dist/styles/display.d.ts +37 -0
- package/dist/styles/display.js +67 -0
- package/dist/styles/display.js.map +1 -0
- package/dist/styles/fade.d.ts +15 -0
- package/dist/styles/fade.js +58 -0
- package/dist/styles/fade.js.map +1 -0
- package/dist/styles/fill.d.ts +42 -0
- package/dist/styles/fill.js +52 -0
- package/dist/styles/fill.js.map +1 -0
- package/dist/styles/flow.d.ts +16 -0
- package/dist/styles/flow.js +12 -0
- package/dist/styles/flow.js.map +1 -0
- package/dist/styles/gap.d.ts +31 -0
- package/dist/styles/gap.js +37 -0
- package/dist/styles/gap.js.map +1 -0
- package/dist/styles/height.d.ts +17 -0
- package/dist/styles/height.js +20 -0
- package/dist/styles/height.js.map +1 -0
- package/dist/styles/index.d.ts +2 -0
- package/dist/styles/index.js +9 -0
- package/dist/styles/index.js.map +1 -0
- package/dist/styles/inset.d.ts +52 -0
- package/dist/styles/inset.js +150 -0
- package/dist/styles/inset.js.map +1 -0
- package/dist/styles/justify.d.ts +15 -0
- package/dist/styles/justify.js +14 -0
- package/dist/styles/justify.js.map +1 -0
- package/dist/styles/list.d.ts +16 -0
- package/dist/styles/list.js +98 -0
- package/dist/styles/list.js.map +1 -0
- package/dist/styles/margin.d.ts +24 -0
- package/dist/styles/margin.js +104 -0
- package/dist/styles/margin.js.map +1 -0
- package/dist/styles/outline.d.ts +29 -0
- package/dist/styles/outline.js +65 -0
- package/dist/styles/outline.js.map +1 -0
- package/dist/styles/padding.d.ts +24 -0
- package/dist/styles/padding.js +104 -0
- package/dist/styles/padding.js.map +1 -0
- package/dist/styles/predefined.d.ts +71 -0
- package/dist/styles/predefined.js +238 -0
- package/dist/styles/predefined.js.map +1 -0
- package/dist/styles/preset.d.ts +47 -0
- package/dist/styles/preset.js +126 -0
- package/dist/styles/preset.js.map +1 -0
- package/dist/styles/radius.d.ts +14 -0
- package/dist/styles/radius.js +51 -0
- package/dist/styles/radius.js.map +1 -0
- package/dist/styles/scrollbar.d.ts +25 -0
- package/dist/styles/scrollbar.js +48 -0
- package/dist/styles/scrollbar.js.map +1 -0
- package/dist/styles/shadow.d.ts +14 -0
- package/dist/styles/shadow.js +24 -0
- package/dist/styles/shadow.js.map +1 -0
- package/dist/styles/transition.d.ts +14 -0
- package/dist/styles/transition.js +158 -0
- package/dist/styles/transition.js.map +1 -0
- package/dist/styles/types.d.ts +508 -0
- package/dist/styles/width.d.ts +17 -0
- package/dist/styles/width.js +20 -0
- package/dist/styles/width.js.map +1 -0
- package/dist/tasty.d.ts +981 -0
- package/dist/tasty.js +206 -0
- package/dist/tasty.js.map +1 -0
- package/dist/types.d.ts +184 -0
- package/dist/utils/cache-wrapper.js +26 -0
- package/dist/utils/cache-wrapper.js.map +1 -0
- package/dist/utils/case-converter.js +8 -0
- package/dist/utils/case-converter.js.map +1 -0
- package/dist/utils/colors.d.ts +5 -0
- package/dist/utils/colors.js +9 -0
- package/dist/utils/colors.js.map +1 -0
- package/dist/utils/css-types.d.ts +7 -0
- package/dist/utils/dotize.d.ts +26 -0
- package/dist/utils/dotize.js +122 -0
- package/dist/utils/dotize.js.map +1 -0
- package/dist/utils/filter-base-props.d.ts +15 -0
- package/dist/utils/filter-base-props.js +45 -0
- package/dist/utils/filter-base-props.js.map +1 -0
- package/dist/utils/get-display-name.d.ts +7 -0
- package/dist/utils/get-display-name.js +10 -0
- package/dist/utils/get-display-name.js.map +1 -0
- package/dist/utils/hsl-to-rgb.js +38 -0
- package/dist/utils/hsl-to-rgb.js.map +1 -0
- package/dist/utils/is-dev-env.js +19 -0
- package/dist/utils/is-dev-env.js.map +1 -0
- package/dist/utils/is-valid-element-type.js +15 -0
- package/dist/utils/is-valid-element-type.js.map +1 -0
- package/dist/utils/merge-styles.d.ts +7 -0
- package/dist/utils/merge-styles.js +146 -0
- package/dist/utils/merge-styles.js.map +1 -0
- package/dist/utils/mod-attrs.d.ts +8 -0
- package/dist/utils/mod-attrs.js +21 -0
- package/dist/utils/mod-attrs.js.map +1 -0
- package/dist/utils/okhsl-to-rgb.js +296 -0
- package/dist/utils/okhsl-to-rgb.js.map +1 -0
- package/dist/utils/process-tokens.d.ts +31 -0
- package/dist/utils/process-tokens.js +171 -0
- package/dist/utils/process-tokens.js.map +1 -0
- package/dist/utils/resolve-recipes.d.ts +17 -0
- package/dist/utils/resolve-recipes.js +147 -0
- package/dist/utils/resolve-recipes.js.map +1 -0
- package/dist/utils/selector-transform.js +32 -0
- package/dist/utils/selector-transform.js.map +1 -0
- package/dist/utils/string.js +8 -0
- package/dist/utils/string.js.map +1 -0
- package/dist/utils/styles.d.ts +177 -0
- package/dist/utils/styles.js +730 -0
- package/dist/utils/styles.js.map +1 -0
- package/dist/utils/typography.d.ts +47 -0
- package/dist/utils/typography.js +43 -0
- package/dist/utils/typography.js.map +1 -0
- package/dist/utils/warnings.d.ts +16 -0
- package/dist/utils/warnings.js +16 -0
- package/dist/utils/warnings.js.map +1 -0
- package/dist/zero/babel.d.ts +147 -0
- package/dist/zero/babel.js +331 -0
- package/dist/zero/babel.js.map +1 -0
- package/dist/zero/css-writer.d.ts +45 -0
- package/dist/zero/css-writer.js +74 -0
- package/dist/zero/css-writer.js.map +1 -0
- package/dist/zero/extractor.d.ts +24 -0
- package/dist/zero/extractor.js +215 -0
- package/dist/zero/extractor.js.map +1 -0
- package/dist/zero/index.d.ts +3 -0
- package/dist/zero/index.js +4 -0
- package/dist/zero/next.d.ts +74 -0
- package/dist/zero/next.js +129 -0
- package/dist/zero/next.js.map +1 -0
- package/docs/adoption.md +286 -0
- package/docs/comparison.md +413 -0
- package/docs/configuration.md +242 -0
- package/docs/debug.md +505 -0
- package/docs/design-system.md +401 -0
- package/docs/dsl.md +540 -0
- package/docs/getting-started.md +201 -0
- package/docs/injector.md +528 -0
- package/docs/methodology.md +501 -0
- package/docs/runtime.md +291 -0
- package/docs/ssr.md +382 -0
- package/docs/styles.md +574 -0
- package/docs/tasty-static.md +421 -0
- package/package.json +209 -0
- package/tasty.config.ts +14 -0
package/docs/dsl.md
ADDED
|
@@ -0,0 +1,540 @@
|
|
|
1
|
+
# Style DSL Reference
|
|
2
|
+
|
|
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
|
+
|
|
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).
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Dictionary
|
|
10
|
+
|
|
11
|
+
### Style Mapping
|
|
12
|
+
|
|
13
|
+
Object where keys represent states and values are the styles to apply:
|
|
14
|
+
|
|
15
|
+
```jsx
|
|
16
|
+
fill: { '': '#white', hovered: '#gray.05', 'theme=danger': '#red' }
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
#### State Key Types
|
|
20
|
+
|
|
21
|
+
| Syntax | Example | Generated CSS |
|
|
22
|
+
|--------|---------|---------------|
|
|
23
|
+
| Boolean modifier | `hovered` | `[data-hovered]` |
|
|
24
|
+
| Value modifier | `theme=danger` | `[data-theme="danger"]` |
|
|
25
|
+
| Pseudo-class | `:hover` | `:hover` |
|
|
26
|
+
| Class selector | `.active` | `.active` |
|
|
27
|
+
| Attribute selector | `[aria-expanded="true"]` | `[aria-expanded="true"]` |
|
|
28
|
+
| Combined | `hovered & .active` | `[data-hovered].active` |
|
|
29
|
+
|
|
30
|
+
### Sub-element
|
|
31
|
+
|
|
32
|
+
Element styled using a capitalized key. Identified by `data-element` attribute:
|
|
33
|
+
|
|
34
|
+
```jsx
|
|
35
|
+
styles: { Title: { preset: 'h3' } }
|
|
36
|
+
// Targets: <div data-element="Title">
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Color Token
|
|
40
|
+
|
|
41
|
+
Named color prefixed with `#` that maps to CSS custom properties. Supports opacity with `.N` suffix:
|
|
42
|
+
|
|
43
|
+
```jsx
|
|
44
|
+
fill: '#purple.5' // → var(--purple-color) with 50% opacity
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Modifier
|
|
48
|
+
|
|
49
|
+
State value via `mods` prop that generates `data-*` attributes:
|
|
50
|
+
|
|
51
|
+
```jsx
|
|
52
|
+
mods={{ hovered: true, theme: 'danger' }}
|
|
53
|
+
// → data-hovered="" data-theme="danger"
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Color Tokens & Opacity
|
|
59
|
+
|
|
60
|
+
```jsx
|
|
61
|
+
color: '#purple', // Full opacity
|
|
62
|
+
color: '#purple.5', // 50% opacity
|
|
63
|
+
color: '#purple.05', // 5% opacity
|
|
64
|
+
fill: '#current', // → currentcolor
|
|
65
|
+
fill: '#current.5', // → color-mix(in oklab, currentcolor 50%, transparent)
|
|
66
|
+
color: '(#primary, #secondary)', // Fallback syntax
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Built-in Units
|
|
72
|
+
|
|
73
|
+
| Unit | Description | Example | CSS Output |
|
|
74
|
+
|------|-------------|---------|------------|
|
|
75
|
+
| `x` | Gap multiplier | `2x` | `calc(var(--gap) * 2)` |
|
|
76
|
+
| `r` | Border radius | `1r` | `var(--radius)` |
|
|
77
|
+
| `cr` | Card border radius | `1cr` | `var(--card-radius)` |
|
|
78
|
+
| `bw` | Border width | `2bw` | `calc(var(--border-width) * 2)` |
|
|
79
|
+
| `ow` | Outline width | `1ow` | `var(--outline-width)` |
|
|
80
|
+
| `fs` | Font size | `1fs` | `var(--font-size)` |
|
|
81
|
+
| `lh` | Line height | `1lh` | `var(--line-height)` |
|
|
82
|
+
| `sf` | Stable fraction | `1sf` | `minmax(0, 1fr)` |
|
|
83
|
+
|
|
84
|
+
You can register additional custom units via [`configure()`](configuration.md#options).
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Replace Tokens
|
|
89
|
+
|
|
90
|
+
Tokens defined via [`configure({ replaceTokens })`](configuration.md#replace-tokens-parse-time-substitution) are replaced at parse time and baked into the generated CSS:
|
|
91
|
+
|
|
92
|
+
```jsx
|
|
93
|
+
const Card = tasty({
|
|
94
|
+
styles: {
|
|
95
|
+
padding: '$card-padding',
|
|
96
|
+
fill: '#surface',
|
|
97
|
+
border: '1bw solid #accent',
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Recipes
|
|
105
|
+
|
|
106
|
+
Apply predefined style bundles (defined via [`configure({ recipes })`](configuration.md#recipes)) using the `recipe` style property:
|
|
107
|
+
|
|
108
|
+
```jsx
|
|
109
|
+
const Card = tasty({
|
|
110
|
+
styles: {
|
|
111
|
+
recipe: 'card',
|
|
112
|
+
color: '#text',
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Compose multiple recipes
|
|
117
|
+
const ElevatedCard = tasty({
|
|
118
|
+
styles: {
|
|
119
|
+
recipe: 'card elevated',
|
|
120
|
+
color: '#text',
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
**Post-merge recipes (`/` separator):**
|
|
126
|
+
|
|
127
|
+
Recipes listed after `/` are applied *after* component styles using `mergeStyles`:
|
|
128
|
+
|
|
129
|
+
```jsx
|
|
130
|
+
const Input = tasty({
|
|
131
|
+
styles: {
|
|
132
|
+
recipe: 'reset input / input-autofill',
|
|
133
|
+
preset: 't3',
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Use `none` to skip base recipes and apply only post recipes:
|
|
139
|
+
|
|
140
|
+
```jsx
|
|
141
|
+
const Custom = tasty({
|
|
142
|
+
styles: {
|
|
143
|
+
recipe: 'none / disabled',
|
|
144
|
+
padding: '2x',
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
```
|
|
148
|
+
|
|
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)
|
|
225
|
+
|
|
226
|
+
| Prefix | Purpose | Example |
|
|
227
|
+
|--------|---------|---------|
|
|
228
|
+
| `@media` | Media queries | `@media(w < 768px)` |
|
|
229
|
+
| `@(...)` | Container queries | `@(panel, w >= 300px)` |
|
|
230
|
+
| `@supports` | Feature/selector support | `@supports(display: grid)` |
|
|
231
|
+
| `@root` | Root element states | `@root(theme=dark)` |
|
|
232
|
+
| `@parent` | Parent/ancestor element states | `@parent(hovered)` |
|
|
233
|
+
| `@own` | Sub-element's own state | `@own(hovered)` |
|
|
234
|
+
| `@starting` | Entry animation | `@starting` |
|
|
235
|
+
| `:is()` | CSS `:is()` structural pseudo-class | `:is(fieldset > label)` |
|
|
236
|
+
| `:has()` | CSS `:has()` relational pseudo-class | `:has(> Icon)` |
|
|
237
|
+
| `:not()` | CSS `:not()` negation (prefer `!:is()`) | `:not(:first-child)` |
|
|
238
|
+
| `:where()` | CSS `:where()` (zero specificity) | `:where(Section)` |
|
|
239
|
+
|
|
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
|
|
390
|
+
|
|
391
|
+
Style based on ancestor element attributes. Uses `:is([selector] *)` / `:not([selector] *)` for symmetric, composable parent checks. Boolean logic (`&`, `|`, `!`) is supported inside `@parent()`.
|
|
392
|
+
|
|
393
|
+
```jsx
|
|
394
|
+
const Highlight = tasty({
|
|
395
|
+
styles: {
|
|
396
|
+
fill: {
|
|
397
|
+
'': '#white',
|
|
398
|
+
'@parent(hovered)': '#gray.05', // Any ancestor has [data-hovered]
|
|
399
|
+
'@parent(theme=dark, >)': '#dark-02', // Direct parent has [data-theme="dark"]
|
|
400
|
+
},
|
|
401
|
+
},
|
|
402
|
+
});
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
| Syntax | CSS Output |
|
|
406
|
+
|--------|------------|
|
|
407
|
+
| `@parent(hovered)` | `:is([data-hovered] *)` |
|
|
408
|
+
| `!@parent(hovered)` | `:not([data-hovered] *)` |
|
|
409
|
+
| `@parent(hovered, >)` | `:is([data-hovered] > *)` (direct parent) |
|
|
410
|
+
| `@parent(.active)` | `:is(.active *)` |
|
|
411
|
+
| `@parent(hovered & focused)` | `:is([data-hovered][data-focused] *)` (same ancestor) |
|
|
412
|
+
| `@parent(hovered) & @parent(focused)` | `:is([data-hovered] *):is([data-focused] *)` (independent ancestors) |
|
|
413
|
+
| `@parent(hovered \| focused)` | `:is([data-hovered] *, [data-focused] *)` (OR inside single wrapper) |
|
|
414
|
+
|
|
415
|
+
For sub-elements, the parent check applies to the root element's ancestors:
|
|
416
|
+
|
|
417
|
+
```jsx
|
|
418
|
+
const Card = tasty({
|
|
419
|
+
styles: {
|
|
420
|
+
Label: {
|
|
421
|
+
color: {
|
|
422
|
+
'': '#text',
|
|
423
|
+
'@parent(hovered)': '#primary',
|
|
424
|
+
},
|
|
425
|
+
},
|
|
426
|
+
},
|
|
427
|
+
});
|
|
428
|
+
// → .t0.t0:is([data-hovered] *) [data-element="Label"]
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
### `:is()`, `:has()` — CSS Structural Pseudo-classes
|
|
432
|
+
|
|
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 `*`.
|
|
434
|
+
|
|
435
|
+
`:where()` and `:not()` are also supported but rarely needed — use `:is()` and `!` negation instead.
|
|
436
|
+
|
|
437
|
+
> **Performance warning:** CSS structural pseudo-classes — especially `:has()` — can be costly for the browser to evaluate because they require inspecting the DOM tree beyond the matched element. Tasty already provides a rich, purpose-built state system (`@parent()`, `@own()`, modifiers, boolean logic) that covers the vast majority of use cases without the performance trade-off. **Prefer Tasty's built-in mechanisms and treat `:has()` / `:is()` as a last resort** for conditions that cannot be expressed any other way.
|
|
438
|
+
|
|
439
|
+
```jsx
|
|
440
|
+
const Card = tasty({
|
|
441
|
+
styles: {
|
|
442
|
+
display: {
|
|
443
|
+
'': 'block',
|
|
444
|
+
':has(> Icon)': 'grid', // has Icon as direct child
|
|
445
|
+
':has(+ Icon)': 'grid', // immediately followed by an Icon sibling
|
|
446
|
+
':has(~ Icon)': 'grid', // has an Icon sibling somewhere after
|
|
447
|
+
':has(Icon +)': 'grid', // immediately preceded by an Icon sibling (auto-completes to `Icon + *`)
|
|
448
|
+
':has(Icon ~)': 'grid', // has an Icon sibling somewhere before (auto-completes to `Icon ~ *`)
|
|
449
|
+
':is(fieldset > label)': 'inline', // is a label inside a fieldset (HTML tags)
|
|
450
|
+
'!:has(> Icon)': 'flex', // negation: no Icon child
|
|
451
|
+
},
|
|
452
|
+
},
|
|
453
|
+
});
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
| Syntax | CSS Output | Meaning |
|
|
457
|
+
|--------|------------|---------|
|
|
458
|
+
| `:has(> Icon)` | `:has(> [data-element="Icon"])` | Has Icon as direct child |
|
|
459
|
+
| `:has(+ Icon)` | `:has(+ [data-element="Icon"])` | Immediately followed by an Icon sibling |
|
|
460
|
+
| `:has(~ Icon)` | `:has(~ [data-element="Icon"])` | Has an Icon sibling somewhere after |
|
|
461
|
+
| `:has(Icon +)` | `:has([data-element="Icon"] + *)` | Immediately preceded by an Icon sibling |
|
|
462
|
+
| `:has(Icon ~)` | `:has([data-element="Icon"] ~ *)` | Has an Icon sibling somewhere before |
|
|
463
|
+
| `:has(>)` | `:has(> *)` | Has any direct child |
|
|
464
|
+
| `:is(> Field + input)` | `:is(> [data-element="Field"] + input)` | Structural match |
|
|
465
|
+
| `:has(button)` | `:has(button)` | HTML tag (lowercase, unchanged) |
|
|
466
|
+
| `!:has(> Icon)` | `:not(:has(> [data-element="Icon"]))` | Negation (use `!`) |
|
|
467
|
+
| `!:is(Panel)` | `:not([data-element="Panel"])` | Negation (use `!:is`) |
|
|
468
|
+
|
|
469
|
+
Combine with other states using boolean logic:
|
|
470
|
+
|
|
471
|
+
```jsx
|
|
472
|
+
':has(> Icon) & hovered' // structural + data attribute
|
|
473
|
+
'@parent(hovered) & :has(> Icon)' // parent check + structural
|
|
474
|
+
':has(> Icon) | :has(> Button)' // OR: either sub-element present
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
> **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.
|
|
478
|
+
|
|
479
|
+
---
|
|
480
|
+
|
|
481
|
+
## Keyframes
|
|
482
|
+
|
|
483
|
+
Define animations inline using the `@keyframes` key in styles:
|
|
484
|
+
|
|
485
|
+
```jsx
|
|
486
|
+
const Pulse = tasty({
|
|
487
|
+
styles: {
|
|
488
|
+
animation: 'pulse 2s infinite',
|
|
489
|
+
'@keyframes': {
|
|
490
|
+
pulse: {
|
|
491
|
+
'0%, 100%': { transform: 'scale(1)' },
|
|
492
|
+
'50%': { transform: 'scale(1.05)' },
|
|
493
|
+
},
|
|
494
|
+
},
|
|
495
|
+
},
|
|
496
|
+
});
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
---
|
|
500
|
+
|
|
501
|
+
## Properties (`@property`)
|
|
502
|
+
|
|
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:
|
|
504
|
+
|
|
505
|
+
```jsx
|
|
506
|
+
const AnimatedGradient = tasty({
|
|
507
|
+
styles: {
|
|
508
|
+
'$gradient-angle': '0deg',
|
|
509
|
+
'#theme': 'okhsl(280 80% 50%)',
|
|
510
|
+
background: 'linear-gradient($gradient-angle, #theme, transparent)',
|
|
511
|
+
transition: '$$gradient-angle 0.3s, ##theme 0.3s',
|
|
512
|
+
},
|
|
513
|
+
});
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
Here `$gradient-angle: '0deg'` is detected as `<angle>` and `#theme` as `<color>` (via the `#name` naming convention), so both transitions work without any manual `@property` declarations. Numeric types (`<number>`, `<length>`, `<percentage>`, `<angle>`, `<time>`) are inferred from values; `<color>` is inferred from `#name` tokens.
|
|
517
|
+
|
|
518
|
+
Use explicit `@properties` when you need non-default settings like `inherits: false`:
|
|
519
|
+
|
|
520
|
+
```jsx
|
|
521
|
+
'@properties': {
|
|
522
|
+
'$gradient-angle': { syntax: '<angle>', inherits: false, initialValue: '0deg' },
|
|
523
|
+
},
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
---
|
|
527
|
+
|
|
528
|
+
## Style Properties
|
|
529
|
+
|
|
530
|
+
For a complete reference of all enhanced style properties — syntax, values, modifiers, and recommendations — see **[Style Properties Reference](styles.md)**.
|
|
531
|
+
|
|
532
|
+
---
|
|
533
|
+
|
|
534
|
+
## Learn more
|
|
535
|
+
|
|
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
|