@tenphi/tasty 0.0.0-snapshot.bbd10c7 → 0.0.0-snapshot.bbfcd75
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 +196 -140
- package/dist/babel-Zs1Q03qt.d.ts +83 -0
- package/dist/chunks/async-storage-CG5HnUT5.js +20 -0
- package/dist/chunks/async-storage-CG5HnUT5.js.map +1 -0
- package/dist/chunks/build-config-CUQVwdWF.js +42 -0
- package/dist/chunks/build-config-CUQVwdWF.js.map +1 -0
- package/dist/chunks/collector-B0LTHw6Z.js +208 -0
- package/dist/chunks/collector-B0LTHw6Z.js.map +1 -0
- package/dist/chunks/config-engine-C3pF9EK9.js +267 -0
- package/dist/chunks/config-engine-C3pF9EK9.js.map +1 -0
- package/dist/chunks/css-definitions-D_it4XZH.js +875 -0
- package/dist/chunks/css-definitions-D_it4XZH.js.map +1 -0
- package/dist/chunks/css-resources-BN8apgxt.js +142 -0
- package/dist/chunks/css-resources-BN8apgxt.js.map +1 -0
- package/dist/{debug.js → chunks/debug-C9LeNVoJ.js} +187 -139
- package/dist/chunks/debug-C9LeNVoJ.js.map +1 -0
- package/dist/chunks/dsl-C5yuAn41.js +1373 -0
- package/dist/chunks/dsl-C5yuAn41.js.map +1 -0
- package/dist/chunks/hydration-TRcz5vnk.js +41 -0
- package/dist/chunks/hydration-TRcz5vnk.js.map +1 -0
- package/dist/chunks/react-runtime-O-OUrKLH.js +557 -0
- package/dist/chunks/react-runtime-O-OUrKLH.js.map +1 -0
- package/dist/chunks/runtime-engine-DvnE2g-E.js +2437 -0
- package/dist/chunks/runtime-engine-DvnE2g-E.js.map +1 -0
- package/dist/chunks/runtime-state-BTDqSYBk.js +127 -0
- package/dist/chunks/runtime-state-BTDqSYBk.js.map +1 -0
- package/dist/{utils/merge-styles.js → chunks/shared-utils-DWA4LjIa.js} +55 -55
- package/dist/chunks/shared-utils-DWA4LjIa.js.map +1 -0
- package/dist/chunks/style-engine-C1ECBZxR.js +5131 -0
- package/dist/chunks/style-engine-C1ECBZxR.js.map +1 -0
- package/dist/{zero/extractor.js → chunks/zero-engine-B6EnYcXY.js} +89 -82
- package/dist/chunks/zero-engine-B6EnYcXY.js.map +1 -0
- package/dist/collector-hl5KNH7k.d.ts +165 -0
- package/dist/config-DWL1ZYue.d.ts +1358 -0
- package/dist/core/index.d.ts +5 -34
- package/dist/core/index.js +9 -27
- package/dist/index-D9N9GV24.d.ts +1947 -0
- package/dist/index.d.ts +5 -51
- package/dist/index.js +11 -36
- package/dist/{utils/merge-styles.d.ts → merge-styles-C_oMoJVd.d.ts} +3 -3
- package/dist/ssr/astro-client.js +6 -0
- package/dist/ssr/astro-client.js.map +1 -0
- package/dist/ssr/astro-middleware-extract-static.d.ts +11 -0
- package/dist/ssr/astro-middleware-extract-static.js +9 -0
- package/dist/ssr/astro-middleware-extract-static.js.map +1 -0
- package/dist/ssr/astro-middleware-extract.d.ts +11 -0
- package/dist/ssr/astro-middleware-extract.js +9 -0
- package/dist/ssr/astro-middleware-extract.js.map +1 -0
- package/dist/ssr/astro-middleware-static.d.ts +18 -0
- package/dist/ssr/astro-middleware-static.js +7 -0
- package/dist/ssr/astro-middleware-static.js.map +1 -0
- package/dist/ssr/astro-middleware.d.ts +19 -0
- package/dist/ssr/astro-middleware.js +7 -0
- package/dist/ssr/astro-middleware.js.map +1 -0
- package/dist/ssr/astro.d.ts +117 -11
- package/dist/ssr/astro.js +204 -48
- package/dist/ssr/astro.js.map +1 -1
- package/dist/ssr/index.d.ts +37 -4
- package/dist/ssr/index.js +6 -6
- package/dist/ssr/index.js.map +1 -1
- package/dist/ssr/next-config.d.ts +66 -0
- package/dist/ssr/next-config.js +124 -0
- package/dist/ssr/next-config.js.map +1 -0
- package/dist/ssr/next.d.ts +14 -5
- package/dist/ssr/next.js +31 -46
- package/dist/ssr/next.js.map +1 -1
- package/dist/static/index.d.ts +91 -5
- package/dist/static/index.js +31 -3
- package/dist/static/index.js.map +1 -0
- package/dist/static/inject.js.map +1 -1
- package/dist/types-jKZNpias.d.ts +1616 -0
- package/dist/zero/babel.d.ts +2 -182
- package/dist/zero/babel.js +82 -113
- package/dist/zero/babel.js.map +1 -1
- package/dist/zero/index.d.ts +81 -3
- package/dist/zero/index.js +2 -3
- package/dist/zero/next.d.ts +1 -1
- package/dist/zero/next.js +0 -31
- package/dist/zero/next.js.map +1 -1
- package/docs/README.md +20 -12
- package/docs/adoption.md +59 -32
- package/docs/ai-agents.md +229 -0
- package/docs/comparison.md +76 -48
- package/docs/configuration.md +566 -69
- package/docs/debug.md +37 -23
- package/docs/design-system.md +63 -32
- package/docs/dsl.md +527 -179
- package/docs/getting-started.md +62 -41
- package/docs/injector.md +157 -51
- package/docs/methodology.md +69 -37
- package/docs/migration-v3.md +285 -0
- package/docs/pipeline.md +744 -0
- package/docs/plugins.md +353 -0
- package/docs/{runtime.md → react-api.md} +176 -67
- package/docs/runtime-benchmarks.md +377 -0
- package/docs/ssr.md +350 -94
- package/docs/styles.md +108 -27
- package/docs/tasty-static.md +149 -87
- package/package.json +121 -23
- package/tasty.config.ts +6 -0
- package/dist/_virtual/_rolldown/runtime.js +0 -7
- package/dist/chunks/cacheKey.js +0 -77
- package/dist/chunks/cacheKey.js.map +0 -1
- package/dist/chunks/definitions.d.ts +0 -37
- package/dist/chunks/definitions.js +0 -258
- package/dist/chunks/definitions.js.map +0 -1
- package/dist/chunks/index.d.ts +0 -1
- package/dist/chunks/renderChunk.d.ts +0 -1
- package/dist/chunks/renderChunk.js +0 -59
- package/dist/chunks/renderChunk.js.map +0 -1
- package/dist/compute-styles.d.ts +0 -31
- package/dist/compute-styles.js +0 -356
- package/dist/compute-styles.js.map +0 -1
- package/dist/config.d.ts +0 -372
- package/dist/config.js +0 -507
- package/dist/config.js.map +0 -1
- package/dist/counter-style/index.js +0 -51
- package/dist/counter-style/index.js.map +0 -1
- package/dist/debug.d.ts +0 -89
- package/dist/debug.js.map +0 -1
- package/dist/font-face/index.js +0 -63
- package/dist/font-face/index.js.map +0 -1
- package/dist/hooks/index.d.ts +0 -7
- package/dist/hooks/useCounterStyle.d.ts +0 -50
- package/dist/hooks/useCounterStyle.js +0 -45
- package/dist/hooks/useCounterStyle.js.map +0 -1
- package/dist/hooks/useFontFace.d.ts +0 -43
- package/dist/hooks/useFontFace.js +0 -69
- package/dist/hooks/useFontFace.js.map +0 -1
- package/dist/hooks/useGlobalStyles.d.ts +0 -30
- package/dist/hooks/useGlobalStyles.js +0 -77
- package/dist/hooks/useGlobalStyles.js.map +0 -1
- package/dist/hooks/useKeyframes.d.ts +0 -56
- package/dist/hooks/useKeyframes.js +0 -63
- package/dist/hooks/useKeyframes.js.map +0 -1
- package/dist/hooks/useProperty.d.ts +0 -79
- package/dist/hooks/useProperty.js +0 -108
- package/dist/hooks/useProperty.js.map +0 -1
- package/dist/hooks/useRawCSS.d.ts +0 -53
- package/dist/hooks/useRawCSS.js +0 -34
- package/dist/hooks/useRawCSS.js.map +0 -1
- package/dist/hooks/useStyles.d.ts +0 -40
- package/dist/hooks/useStyles.js +0 -29
- package/dist/hooks/useStyles.js.map +0 -1
- package/dist/injector/index.d.ts +0 -166
- package/dist/injector/index.js +0 -166
- package/dist/injector/index.js.map +0 -1
- package/dist/injector/injector.d.ts +0 -166
- package/dist/injector/injector.js +0 -464
- package/dist/injector/injector.js.map +0 -1
- package/dist/injector/sheet-manager.d.ts +0 -136
- package/dist/injector/sheet-manager.js +0 -733
- package/dist/injector/sheet-manager.js.map +0 -1
- package/dist/injector/types.d.ts +0 -204
- package/dist/keyframes/index.js +0 -206
- package/dist/keyframes/index.js.map +0 -1
- package/dist/parser/classify.js +0 -319
- package/dist/parser/classify.js.map +0 -1
- package/dist/parser/const.js +0 -60
- package/dist/parser/const.js.map +0 -1
- package/dist/parser/lru.js +0 -109
- package/dist/parser/lru.js.map +0 -1
- package/dist/parser/parser.d.ts +0 -25
- package/dist/parser/parser.js +0 -115
- package/dist/parser/parser.js.map +0 -1
- package/dist/parser/tokenizer.js +0 -69
- package/dist/parser/tokenizer.js.map +0 -1
- package/dist/parser/types.d.ts +0 -51
- package/dist/parser/types.js +0 -46
- package/dist/parser/types.js.map +0 -1
- package/dist/pipeline/conditions.d.ts +0 -134
- package/dist/pipeline/conditions.js +0 -406
- package/dist/pipeline/conditions.js.map +0 -1
- package/dist/pipeline/exclusive.js +0 -230
- package/dist/pipeline/exclusive.js.map +0 -1
- package/dist/pipeline/index.d.ts +0 -55
- package/dist/pipeline/index.js +0 -708
- package/dist/pipeline/index.js.map +0 -1
- package/dist/pipeline/materialize.js +0 -1103
- package/dist/pipeline/materialize.js.map +0 -1
- package/dist/pipeline/parseStateKey.d.ts +0 -15
- package/dist/pipeline/parseStateKey.js +0 -446
- package/dist/pipeline/parseStateKey.js.map +0 -1
- package/dist/pipeline/simplify.js +0 -515
- package/dist/pipeline/simplify.js.map +0 -1
- package/dist/pipeline/warnings.js +0 -18
- package/dist/pipeline/warnings.js.map +0 -1
- package/dist/plugins/index.d.ts +0 -2
- package/dist/plugins/okhsl-plugin.d.ts +0 -35
- package/dist/plugins/okhsl-plugin.js +0 -97
- package/dist/plugins/okhsl-plugin.js.map +0 -1
- package/dist/plugins/types.d.ts +0 -76
- package/dist/properties/index.js +0 -222
- package/dist/properties/index.js.map +0 -1
- package/dist/properties/property-type-resolver.d.ts +0 -24
- package/dist/properties/property-type-resolver.js +0 -90
- package/dist/properties/property-type-resolver.js.map +0 -1
- package/dist/ssr/async-storage.d.ts +0 -17
- package/dist/ssr/async-storage.js +0 -34
- package/dist/ssr/async-storage.js.map +0 -1
- package/dist/ssr/collect-auto-properties.js +0 -39
- package/dist/ssr/collect-auto-properties.js.map +0 -1
- package/dist/ssr/collector.d.ts +0 -102
- package/dist/ssr/collector.js +0 -219
- package/dist/ssr/collector.js.map +0 -1
- package/dist/ssr/format-global-rules.js +0 -22
- package/dist/ssr/format-global-rules.js.map +0 -1
- package/dist/ssr/format-keyframes.js +0 -69
- package/dist/ssr/format-keyframes.js.map +0 -1
- package/dist/ssr/format-property.js +0 -49
- package/dist/ssr/format-property.js.map +0 -1
- package/dist/ssr/format-rules.js +0 -73
- package/dist/ssr/format-rules.js.map +0 -1
- package/dist/ssr/hydrate.d.ts +0 -22
- package/dist/ssr/hydrate.js +0 -49
- package/dist/ssr/hydrate.js.map +0 -1
- package/dist/ssr/ssr-collector-ref.js +0 -12
- package/dist/ssr/ssr-collector-ref.js.map +0 -1
- package/dist/states/index.d.ts +0 -49
- package/dist/states/index.js +0 -170
- package/dist/states/index.js.map +0 -1
- package/dist/static/tastyStatic.d.ts +0 -46
- package/dist/static/tastyStatic.js +0 -30
- package/dist/static/tastyStatic.js.map +0 -1
- package/dist/static/types.d.ts +0 -49
- package/dist/static/types.js +0 -24
- package/dist/static/types.js.map +0 -1
- package/dist/styles/border.d.ts +0 -25
- package/dist/styles/border.js +0 -120
- package/dist/styles/border.js.map +0 -1
- package/dist/styles/color.d.ts +0 -14
- package/dist/styles/color.js +0 -26
- package/dist/styles/color.js.map +0 -1
- package/dist/styles/const.js +0 -17
- package/dist/styles/const.js.map +0 -1
- package/dist/styles/createStyle.js +0 -79
- package/dist/styles/createStyle.js.map +0 -1
- package/dist/styles/dimension.js +0 -109
- package/dist/styles/dimension.js.map +0 -1
- package/dist/styles/directional.js +0 -133
- package/dist/styles/directional.js.map +0 -1
- package/dist/styles/display.d.ts +0 -30
- package/dist/styles/display.js +0 -73
- package/dist/styles/display.js.map +0 -1
- package/dist/styles/fade.d.ts +0 -15
- package/dist/styles/fade.js +0 -62
- package/dist/styles/fade.js.map +0 -1
- package/dist/styles/fill.d.ts +0 -42
- package/dist/styles/fill.js +0 -51
- package/dist/styles/fill.js.map +0 -1
- package/dist/styles/flow.d.ts +0 -16
- package/dist/styles/flow.js +0 -12
- package/dist/styles/flow.js.map +0 -1
- package/dist/styles/gap.d.ts +0 -31
- package/dist/styles/gap.js +0 -38
- package/dist/styles/gap.js.map +0 -1
- package/dist/styles/height.d.ts +0 -17
- package/dist/styles/height.js +0 -19
- package/dist/styles/height.js.map +0 -1
- package/dist/styles/index.d.ts +0 -1
- package/dist/styles/index.js +0 -8
- package/dist/styles/index.js.map +0 -1
- package/dist/styles/inset.d.ts +0 -24
- package/dist/styles/inset.js +0 -34
- package/dist/styles/inset.js.map +0 -1
- package/dist/styles/list.d.ts +0 -16
- package/dist/styles/list.js +0 -100
- package/dist/styles/list.js.map +0 -1
- package/dist/styles/margin.d.ts +0 -24
- package/dist/styles/margin.js +0 -32
- package/dist/styles/margin.js.map +0 -1
- package/dist/styles/outline.d.ts +0 -29
- package/dist/styles/outline.js +0 -55
- package/dist/styles/outline.js.map +0 -1
- package/dist/styles/padding.d.ts +0 -24
- package/dist/styles/padding.js +0 -32
- package/dist/styles/padding.js.map +0 -1
- package/dist/styles/placement.d.ts +0 -37
- package/dist/styles/placement.js +0 -74
- package/dist/styles/placement.js.map +0 -1
- package/dist/styles/predefined.d.ts +0 -71
- package/dist/styles/predefined.js +0 -237
- package/dist/styles/predefined.js.map +0 -1
- package/dist/styles/preset.d.ts +0 -52
- package/dist/styles/preset.js +0 -127
- package/dist/styles/preset.js.map +0 -1
- package/dist/styles/radius.d.ts +0 -12
- package/dist/styles/radius.js +0 -83
- package/dist/styles/radius.js.map +0 -1
- package/dist/styles/scrollMargin.d.ts +0 -24
- package/dist/styles/scrollMargin.js +0 -32
- package/dist/styles/scrollMargin.js.map +0 -1
- package/dist/styles/scrollbar.d.ts +0 -25
- package/dist/styles/scrollbar.js +0 -51
- package/dist/styles/scrollbar.js.map +0 -1
- package/dist/styles/shadow.d.ts +0 -14
- package/dist/styles/shadow.js +0 -25
- package/dist/styles/shadow.js.map +0 -1
- package/dist/styles/shared.js +0 -17
- package/dist/styles/shared.js.map +0 -1
- package/dist/styles/transition.d.ts +0 -14
- package/dist/styles/transition.js +0 -159
- package/dist/styles/transition.js.map +0 -1
- package/dist/styles/types.d.ts +0 -564
- package/dist/styles/width.d.ts +0 -17
- package/dist/styles/width.js +0 -19
- package/dist/styles/width.js.map +0 -1
- package/dist/tasty.d.ts +0 -134
- package/dist/tasty.js +0 -240
- package/dist/tasty.js.map +0 -1
- package/dist/types.d.ts +0 -184
- package/dist/utils/cache-wrapper.js +0 -21
- package/dist/utils/cache-wrapper.js.map +0 -1
- package/dist/utils/case-converter.js +0 -8
- package/dist/utils/case-converter.js.map +0 -1
- package/dist/utils/color-math.d.ts +0 -46
- package/dist/utils/color-math.js +0 -749
- package/dist/utils/color-math.js.map +0 -1
- package/dist/utils/color-space.d.ts +0 -5
- package/dist/utils/color-space.js +0 -228
- package/dist/utils/color-space.js.map +0 -1
- package/dist/utils/colors.d.ts +0 -5
- package/dist/utils/colors.js +0 -10
- package/dist/utils/colors.js.map +0 -1
- package/dist/utils/css-types.d.ts +0 -7
- package/dist/utils/dotize.d.ts +0 -26
- package/dist/utils/dotize.js +0 -122
- package/dist/utils/dotize.js.map +0 -1
- package/dist/utils/filter-base-props.d.ts +0 -15
- package/dist/utils/filter-base-props.js +0 -45
- package/dist/utils/filter-base-props.js.map +0 -1
- package/dist/utils/get-display-name.d.ts +0 -7
- package/dist/utils/get-display-name.js +0 -10
- package/dist/utils/get-display-name.js.map +0 -1
- package/dist/utils/has-keys.js +0 -13
- package/dist/utils/has-keys.js.map +0 -1
- package/dist/utils/is-dev-env.js +0 -19
- package/dist/utils/is-dev-env.js.map +0 -1
- package/dist/utils/is-valid-element-type.js +0 -15
- package/dist/utils/is-valid-element-type.js.map +0 -1
- package/dist/utils/merge-styles.js.map +0 -1
- package/dist/utils/mod-attrs.d.ts +0 -6
- package/dist/utils/mod-attrs.js +0 -20
- package/dist/utils/mod-attrs.js.map +0 -1
- package/dist/utils/process-tokens.d.ts +0 -17
- package/dist/utils/process-tokens.js +0 -83
- package/dist/utils/process-tokens.js.map +0 -1
- package/dist/utils/resolve-recipes.d.ts +0 -17
- package/dist/utils/resolve-recipes.js +0 -146
- package/dist/utils/resolve-recipes.js.map +0 -1
- package/dist/utils/selector-transform.js +0 -32
- package/dist/utils/selector-transform.js.map +0 -1
- package/dist/utils/string.js +0 -8
- package/dist/utils/string.js.map +0 -1
- package/dist/utils/styles.d.ts +0 -99
- package/dist/utils/styles.js +0 -220
- package/dist/utils/styles.js.map +0 -1
- package/dist/utils/typography.d.ts +0 -47
- package/dist/utils/typography.js +0 -51
- package/dist/utils/typography.js.map +0 -1
- package/dist/utils/warnings.d.ts +0 -16
- package/dist/utils/warnings.js +0 -16
- package/dist/utils/warnings.js.map +0 -1
- package/dist/zero/css-writer.d.ts +0 -45
- package/dist/zero/css-writer.js +0 -73
- package/dist/zero/css-writer.js.map +0 -1
- package/dist/zero/extractor.d.ts +0 -24
- package/dist/zero/extractor.js.map +0 -1
- package/docs/PIPELINE.md +0 -519
- /package/dist/{chunks/cacheKey.d.ts → ssr/astro-client.d.ts} +0 -0
package/docs/dsl.md
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# Style DSL Reference
|
|
2
2
|
|
|
3
|
-
This is the Tasty
|
|
3
|
+
This is the language behind Tasty’s predictable state resolution. Every property can use a state map; later branches declare higher priority, and Tasty compiles them so only one generated selector can match.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
The same value syntax, state maps, tokens, units, extension semantics, and special declarations apply to React `tasty()`—whether rendered on the client or server—and build-time `tastyStatic()`.
|
|
6
|
+
|
|
7
|
+
For the React API (`tasty()`, style functions, component props), see [React API](react-api.md). For all enhanced style properties, see [Style Properties](styles.md). For global configuration, see [Configuration](configuration.md).
|
|
6
8
|
|
|
7
9
|
---
|
|
8
10
|
|
|
@@ -18,41 +20,86 @@ fill: { '': '#white', hovered: '#gray.05', 'theme=danger': '#red' }
|
|
|
18
20
|
|
|
19
21
|
#### State Key Types
|
|
20
22
|
|
|
21
|
-
| Syntax
|
|
22
|
-
|
|
23
|
-
| Boolean modifier
|
|
24
|
-
| Value modifier
|
|
25
|
-
| Pseudo-class
|
|
26
|
-
| Class selector
|
|
27
|
-
| Attribute selector | `[aria-expanded="true"]` | `[aria-expanded="true"]`
|
|
28
|
-
| Combined | `hovered & .active`
|
|
23
|
+
| Syntax | Example | Generated CSS |
|
|
24
|
+
| ------------------ | ------------------------ | ------------------------------------------------------------------------ |
|
|
25
|
+
| Boolean modifier | `hovered` | `[data-hovered]` |
|
|
26
|
+
| Value modifier | `theme=danger` | `[data-theme="danger"]` |
|
|
27
|
+
| Pseudo-class | `:hover` | `:hover` |
|
|
28
|
+
| Class selector | `.active` | `.active` |
|
|
29
|
+
| Attribute selector | `[aria-expanded="true"]` | `[aria-expanded="true"]` |
|
|
30
|
+
| Combined (AND) | `hovered & .active` | `[data-hovered].active` |
|
|
31
|
+
| Combined (OR) | `hovered \| focused` | `[data-hovered], [data-focused]` |
|
|
32
|
+
| Negated (NOT) | `!disabled` | `:not([data-disabled])` |
|
|
33
|
+
| Exclusive (XOR) | `hovered ^ focused` | `[data-hovered]:not([data-focused]), :not([data-hovered])[data-focused]` |
|
|
34
|
+
|
|
35
|
+
#### Automatic Native States
|
|
36
|
+
|
|
37
|
+
`disabled` and `checked` are built-in automatic states. On a Tasty component, they track the corresponding native prop or attribute, so these pairs express the same component state:
|
|
38
|
+
|
|
39
|
+
| Concise Tasty state | Explicit attribute selector |
|
|
40
|
+
| ------------------- | --------------------------- |
|
|
41
|
+
| `disabled` | `[disabled]` |
|
|
42
|
+
| `checked` | `[checked]` |
|
|
43
|
+
|
|
44
|
+
Prefer the concise form when authoring Tasty component styles. Explicit attribute selectors remain valid and can be useful when targeting arbitrary attributes or when CSS-shaped syntax makes an introductory example easier to recognize.
|
|
45
|
+
|
|
46
|
+
Operator precedence (highest to lowest): `!` (NOT) > `^` (XOR) > `|` (OR) > `&` (AND). Use parentheses to override: `hovered & (pressed ^ focused)`.
|
|
47
|
+
|
|
48
|
+
`^` (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
|
+
|
|
50
|
+
```jsx
|
|
51
|
+
fill: {
|
|
52
|
+
'': '#surface',
|
|
53
|
+
'hovered ^ focused': '#accent', // active when hovered OR focused, but not both
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
#### Default State Ordering
|
|
58
|
+
|
|
59
|
+
Key order sets priority — later keys win and turn off earlier ones via negation. The bare default (`''`) is the lowest-priority state, so it **must be the first key**. If it appears after other states, Tasty moves it to the front and emits a dev warning (`MISPLACED_DEFAULT_STATE`); otherwise it would override every state above it.
|
|
60
|
+
|
|
61
|
+
```jsx
|
|
62
|
+
// Correct — default first
|
|
63
|
+
color: { '': '#text', hovered: '#accent' }
|
|
64
|
+
|
|
65
|
+
// Auto-corrected with a warning — '' moved to the front
|
|
66
|
+
color: { hovered: '#accent', '': '#text' }
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The bare `''` default still **receives negation** (it is turned off when a higher-priority state matches). For a value that must always apply as a guaranteed floor — even where a query is _unknown_ — use the [`_` fallback floor](#_--fallback-floor) instead. The two can coexist: `''` is the negated default, `_` is the always-on floor. If a map contains only `_` and `''` (no other states), the `''` default is redundant — Tasty keeps the `_` value and drops `''` with a `REDUNDANT_DEFAULT_STATE` warning.
|
|
29
70
|
|
|
30
71
|
### Sub-element
|
|
31
72
|
|
|
32
73
|
Element styled using a capitalized key. Identified by `data-element` attribute:
|
|
33
74
|
|
|
34
75
|
```jsx
|
|
35
|
-
styles: {
|
|
76
|
+
styles: {
|
|
77
|
+
Title: {
|
|
78
|
+
preset: 'h3';
|
|
79
|
+
}
|
|
80
|
+
}
|
|
36
81
|
// Targets: <div data-element="Title">
|
|
37
82
|
```
|
|
38
83
|
|
|
39
84
|
#### Selector Affix (`$`)
|
|
40
85
|
|
|
41
|
-
Control how a sub-element selector attaches to the root selector using the `$` property inside the sub-element's styles
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
|
46
|
-
|
|
|
47
|
-
|
|
|
48
|
-
|
|
|
49
|
-
|
|
|
50
|
-
| `
|
|
51
|
-
|
|
|
52
|
-
|
|
|
53
|
-
|
|
|
54
|
-
|
|
|
55
|
-
|
|
|
86
|
+
Control how a sub-element selector attaches to the root selector using the `$` property inside the sub-element's styles.
|
|
87
|
+
|
|
88
|
+
Examples below assume the sub-element key is **Cell** (i.e. `[data-element="Cell"]` in CSS):
|
|
89
|
+
|
|
90
|
+
| Pattern | Result | Description |
|
|
91
|
+
| ----------- | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
92
|
+
| _(none)_ | `[data-element="Cell"]` | Descendant (default) |
|
|
93
|
+
| `>` | `> [data-element="Cell"]` | Direct child |
|
|
94
|
+
| `>Body>` | `> [data-element="Body"] > [data-element="Cell"]` | Chained elements |
|
|
95
|
+
| `> Cell` | `> [data-element="Cell"]` | Self-name shorthand — when the trailing element name matches the sub-element's own key, it acts as the placeholder (same as `@`); no duplication |
|
|
96
|
+
| `h1` | `h1` | Tag selector (no key injection) |
|
|
97
|
+
| `h1 >` | `h1 > [data-element="Cell"]` | Key is direct child of tag |
|
|
98
|
+
| `h1 *` | `h1 *` | Any descendant of tag |
|
|
99
|
+
| `*` | `*` | All descendants |
|
|
100
|
+
| `&::before` | `::before` | Root pseudo (no key); `&` is required |
|
|
101
|
+
| `@::before` | `[data-element="Cell"]::before` | Pseudo on the sub-element |
|
|
102
|
+
| `>@.active` | `> [data-element="Cell"].active` | Class on the sub-element |
|
|
56
103
|
|
|
57
104
|
Rules for key injection (`[data-element="..."]`):
|
|
58
105
|
|
|
@@ -81,7 +128,7 @@ const List = tasty({
|
|
|
81
128
|
Named color prefixed with `#` that maps to CSS custom properties. Supports opacity with `.N` suffix:
|
|
82
129
|
|
|
83
130
|
```jsx
|
|
84
|
-
fill: '#purple.5'
|
|
131
|
+
fill: '#purple.5'; // → oklch(from var(--purple-color) l c h / .5)
|
|
85
132
|
```
|
|
86
133
|
|
|
87
134
|
### Modifier
|
|
@@ -93,7 +140,7 @@ mods={{ hovered: true, theme: 'danger' }}
|
|
|
93
140
|
// → data-hovered="" data-theme="danger"
|
|
94
141
|
```
|
|
95
142
|
|
|
96
|
-
Modifiers can also be exposed as top-level component props via `modProps` — see [
|
|
143
|
+
Modifiers can also be exposed as top-level component props via `modProps` — see [React API — Mod Props](react-api.md#mod-props).
|
|
97
144
|
|
|
98
145
|
---
|
|
99
146
|
|
|
@@ -103,23 +150,155 @@ Modifiers can also be exposed as top-level component props via `modProps` — se
|
|
|
103
150
|
color: '#purple', // Full opacity
|
|
104
151
|
color: '#purple.5', // 50% opacity
|
|
105
152
|
color: '#purple.05', // 5% opacity
|
|
153
|
+
color: '#purple.$fade', // Opacity from a custom property
|
|
106
154
|
fill: '#current', // → currentcolor
|
|
107
|
-
fill: '#current.5', // → color-mix(in oklab, currentcolor 50%, transparent)
|
|
108
155
|
color: '(#primary, #secondary)', // Fallback syntax
|
|
109
156
|
```
|
|
110
157
|
|
|
158
|
+
The suffix sets the alpha on the token's color with CSS relative color syntax:
|
|
159
|
+
|
|
160
|
+
```jsx
|
|
161
|
+
fill: '#purple.5';
|
|
162
|
+
// → oklch(from var(--purple-color) l c h / .5)
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
The channels are copied over and the alpha slot is written, which asks nothing of
|
|
166
|
+
the color beyond _being_ a color. That means the suffix works on every one of
|
|
167
|
+
these:
|
|
168
|
+
|
|
169
|
+
- a token holding a `color-mix()`, a `light-dark()`, or a `color()` in a space
|
|
170
|
+
Tasty cannot convert — none of which have channels to decompose
|
|
171
|
+
- `#current`, which resolves to the color the element inherits
|
|
172
|
+
- a `--name-color` variable declared in your own CSS, with no Tasty token
|
|
173
|
+
definition behind it
|
|
174
|
+
|
|
175
|
+
Two properties follow from writing the alpha slot rather than compositing:
|
|
176
|
+
|
|
177
|
+
- **Alpha is replaced, not multiplied.** A token holding `rgb(255 0 0 / .8)`
|
|
178
|
+
faded to `.5` is alpha `.5`, not `.4`.
|
|
179
|
+
- **The alpha may be a number or a percentage.** `#purple.$fade` emits
|
|
180
|
+
`/ var(--fade)` unchanged, so it works whether `$fade` holds `.5` or `50%` —
|
|
181
|
+
which is what `--*-opacity` properties are registered to accept.
|
|
182
|
+
|
|
183
|
+
### `#current` is the keyword
|
|
184
|
+
|
|
185
|
+
`#current` emits the `currentcolor` keyword, so it resolves against the element
|
|
186
|
+
that reads it. That is what lets it follow a color an ancestor faded — a ramp
|
|
187
|
+
that expresses its disabled state once, in `color`, fades everything painted from
|
|
188
|
+
`#current` below it with no further declarations.
|
|
189
|
+
|
|
190
|
+
The `color` style also publishes `--current-color` beside every color it sets,
|
|
191
|
+
for anything that needs the inherited color _as a color_ rather than as the
|
|
192
|
+
keyword — hand-authored CSS, or a place the keyword will not do. Read it as
|
|
193
|
+
`$current-color`. It is registered with `initial-value: currentcolor`, so where
|
|
194
|
+
nothing published it, it still resolves against each element's own color:
|
|
195
|
+
|
|
196
|
+
```jsx
|
|
197
|
+
fill: '$current-color'; // → var(--current-color)
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
A color that already reads the color it inherits — `#current`, a `#current` fade
|
|
201
|
+
— is not published into it: resolving such a value a second time at a descendant
|
|
202
|
+
would fade it twice.
|
|
203
|
+
|
|
204
|
+
One case needs the wider floor to be considered. A token _defined_ as `#current`
|
|
205
|
+
and then faded gives relative color syntax a `currentcolor` origin, which Safari
|
|
206
|
+
supports only from 18:
|
|
207
|
+
|
|
208
|
+
```jsx
|
|
209
|
+
{ '#ink': '#current', fill: '#ink.5' }
|
|
210
|
+
// --ink-color: currentcolor
|
|
211
|
+
// fill: oklch(from currentcolor l c h / .5) → Chrome, Firefox, Safari 18+
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Put the fade in the token instead — `{ '#ink': '#current.5' }` — and it goes
|
|
215
|
+
through `color-mix()`, which works from Safari 16.2 and composes like every other
|
|
216
|
+
`#current` fade.
|
|
217
|
+
|
|
218
|
+
### `#current` composes instead
|
|
219
|
+
|
|
220
|
+
`#current` is the one exception, and the difference is deliberate. A token _names_
|
|
221
|
+
a color, so fading it sets its alpha. `currentcolor` is the color an element
|
|
222
|
+
**inherits**, which an ancestor may already have faded — `#current.4` means "40%
|
|
223
|
+
of what reaches me":
|
|
224
|
+
|
|
225
|
+
```jsx
|
|
226
|
+
fill: '#current.4';
|
|
227
|
+
// → color-mix(in oklab, currentcolor 40%, transparent)
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
So a `#current` fade nested inside another one composes: a label at `#current.4`
|
|
231
|
+
with a fill of `#current.18` under it lands at `.072`. Color ramps built on
|
|
232
|
+
`#current` depend on that — replacing would double the opacity of every nested
|
|
233
|
+
step. Because a `color-mix()` percentage cannot be a `<number>`, an opacity
|
|
234
|
+
custom property used as `#current.$fade` must hold a unitless number; a token
|
|
235
|
+
accepts either form.
|
|
236
|
+
|
|
237
|
+
The space is always `oklch`: it is unbounded, so a wide-gamut color survives a
|
|
238
|
+
round trip that a gamut-limited space would clamp. Nothing configures this —
|
|
239
|
+
see [Color space](configuration.md#color-space).
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## CSS Color Functions
|
|
244
|
+
|
|
245
|
+
Every CSS color function is recognized as a color, so it lands in the color slot
|
|
246
|
+
of whichever style property you use it in — and tokens inside it are expanded:
|
|
247
|
+
|
|
248
|
+
```jsx
|
|
249
|
+
fill: 'color-mix(in oklab, #primary 50%, #surface)',
|
|
250
|
+
color: 'light-dark(#dark, #light)',
|
|
251
|
+
color: 'contrast-color(#primary)',
|
|
252
|
+
fill: 'color(display-p3 1 .5 0)',
|
|
253
|
+
border: '1bw solid oklch(from #primary l c h / 50%)', // relative color syntax
|
|
254
|
+
shadow: '0 0 1x color-mix(in oklab, #dark 20%, transparent)',
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
`light-dark()` is the one exception to "always a color": CSS lets it pick between
|
|
258
|
+
values of any type, so it is treated as a color only when its arguments are
|
|
259
|
+
colors. `padding: 'light-dark(1x, 2x)'` still reaches the padding slot.
|
|
260
|
+
|
|
261
|
+
A color token defined as one of these functions takes the
|
|
262
|
+
[opacity suffix](#color-tokens--opacity) like any other:
|
|
263
|
+
|
|
264
|
+
```jsx
|
|
265
|
+
const Card = tasty({
|
|
266
|
+
styles: {
|
|
267
|
+
'#brand': 'color-mix(in oklab, #primary 50%, #surface)',
|
|
268
|
+
fill: '#brand.5',
|
|
269
|
+
},
|
|
270
|
+
});
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
A [replace token](configuration.md#replace-tokens-parse-time-substitution) is
|
|
274
|
+
substituted while parsing, so its color is right there to fade in place. A
|
|
275
|
+
channel function takes the alpha after a slash; a derived function has no alpha
|
|
276
|
+
channel, so it gets wrapped:
|
|
277
|
+
|
|
278
|
+
```jsx
|
|
279
|
+
configure({
|
|
280
|
+
replaceTokens: {
|
|
281
|
+
'#solid': 'hsl(220 90% 50%)',
|
|
282
|
+
'#adaptive': 'light-dark(#dark, #light)',
|
|
283
|
+
},
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
fill: '#solid.5'; // → hsl(220 90% 50% / .5)
|
|
287
|
+
fill: '#adaptive.5'; // → oklch(from light-dark(…) l c h / .5)
|
|
288
|
+
```
|
|
289
|
+
|
|
111
290
|
---
|
|
112
291
|
|
|
113
292
|
## Built-in Units
|
|
114
293
|
|
|
115
|
-
| Unit | Description
|
|
116
|
-
|
|
117
|
-
| `x`
|
|
118
|
-
| `r`
|
|
119
|
-
| `cr` | Card border radius | `1cr`
|
|
120
|
-
| `bw` | Border width
|
|
121
|
-
| `ow` | Outline width
|
|
122
|
-
| `sf` | Stable fraction
|
|
294
|
+
| Unit | Description | Example | CSS Output |
|
|
295
|
+
| ---- | ------------------ | ------- | ------------------------------- |
|
|
296
|
+
| `x` | Gap multiplier | `2x` | `calc(var(--gap) * 2)` |
|
|
297
|
+
| `r` | Border radius | `1r` | `var(--radius)` |
|
|
298
|
+
| `cr` | Card border radius | `1cr` | `var(--card-radius)` |
|
|
299
|
+
| `bw` | Border width | `2bw` | `calc(var(--border-width) * 2)` |
|
|
300
|
+
| `ow` | Outline width | `1ow` | `var(--outline-width)` |
|
|
301
|
+
| `sf` | Stable fraction | `1sf` | `minmax(0, 1fr)` |
|
|
123
302
|
|
|
124
303
|
You can register additional custom units via [`configure()`](configuration.md#options).
|
|
125
304
|
|
|
@@ -164,7 +343,7 @@ const ElevatedCard = tasty({
|
|
|
164
343
|
|
|
165
344
|
**Post-merge recipes (`/` separator):**
|
|
166
345
|
|
|
167
|
-
Recipes listed after `/` are applied
|
|
346
|
+
Recipes listed after `/` are applied _after_ component styles using `mergeStyles`:
|
|
168
347
|
|
|
169
348
|
```jsx
|
|
170
349
|
const Input = tasty({
|
|
@@ -202,8 +381,8 @@ When a style property uses a state map, the merge behavior depends on whether th
|
|
|
202
381
|
const MyButton = tasty(Button, {
|
|
203
382
|
styles: {
|
|
204
383
|
fill: {
|
|
205
|
-
|
|
206
|
-
|
|
384
|
+
loading: '#yellow', // append new state
|
|
385
|
+
disabled: '#gray.20', // override existing state in place
|
|
207
386
|
},
|
|
208
387
|
},
|
|
209
388
|
});
|
|
@@ -213,7 +392,7 @@ const MyButton = tasty(Button, {
|
|
|
213
392
|
styles: {
|
|
214
393
|
fill: {
|
|
215
394
|
'': '#red',
|
|
216
|
-
|
|
395
|
+
hovered: '#blue',
|
|
217
396
|
},
|
|
218
397
|
},
|
|
219
398
|
});
|
|
@@ -238,8 +417,12 @@ fill: {
|
|
|
238
417
|
Use `null` inside a state map to remove a state, or `false` to block it entirely (tombstone):
|
|
239
418
|
|
|
240
419
|
```jsx
|
|
241
|
-
fill: {
|
|
242
|
-
|
|
420
|
+
fill: {
|
|
421
|
+
pressed: null;
|
|
422
|
+
} // removes pressed from the result
|
|
423
|
+
fill: {
|
|
424
|
+
disabled: false;
|
|
425
|
+
} // tombstone — no CSS for disabled, blocks recipe too
|
|
243
426
|
```
|
|
244
427
|
|
|
245
428
|
### Resetting Properties with `null` and `false`
|
|
@@ -247,44 +430,54 @@ fill: { disabled: false } // tombstone — no CSS for disabled, blocks recipe to
|
|
|
247
430
|
```jsx
|
|
248
431
|
const SimpleButton = tasty(Button, {
|
|
249
432
|
styles: {
|
|
250
|
-
fill: null,
|
|
433
|
+
fill: null, // discard parent's fill, let recipe fill in
|
|
251
434
|
border: false, // no border at all (tombstone — blocks recipe too)
|
|
252
435
|
},
|
|
253
436
|
});
|
|
254
437
|
```
|
|
255
438
|
|
|
256
|
-
| Value
|
|
257
|
-
|
|
258
|
-
| `undefined` | Not provided — parent preserved
|
|
259
|
-
| `null`
|
|
260
|
-
| `false`
|
|
439
|
+
| Value | Meaning | Recipe fills in? |
|
|
440
|
+
| ----------- | ------------------------------------ | ---------------- |
|
|
441
|
+
| `undefined` | Not provided — parent preserved | N/A |
|
|
442
|
+
| `null` | Intentional unset — parent discarded | Yes |
|
|
443
|
+
| `false` | Tombstone — blocks everything | No |
|
|
261
444
|
|
|
262
445
|
---
|
|
263
446
|
|
|
264
447
|
## Advanced States (`@` prefix)
|
|
265
448
|
|
|
266
|
-
| Prefix
|
|
267
|
-
|
|
268
|
-
| `@media`
|
|
269
|
-
| `@(...)`
|
|
270
|
-
| `@supports` | Feature/selector support
|
|
271
|
-
| `@root`
|
|
272
|
-
| `@parent`
|
|
273
|
-
| `@own`
|
|
274
|
-
| `@starting` | Entry animation
|
|
275
|
-
|
|
|
276
|
-
| `:
|
|
277
|
-
| `:
|
|
278
|
-
| `:
|
|
449
|
+
| Prefix | Purpose | Example |
|
|
450
|
+
| ----------- | ----------------------------------------- | -------------------------- |
|
|
451
|
+
| `@media` | Media queries | `@media(w < 768px)` |
|
|
452
|
+
| `@(...)` | Container queries | `@(panel, w >= 300px)` |
|
|
453
|
+
| `@supports` | Feature/selector support | `@supports(display: grid)` |
|
|
454
|
+
| `@root` | Root element states | `@root(schema=dark)` |
|
|
455
|
+
| `@parent` | Parent/ancestor element states | `@parent(hovered)` |
|
|
456
|
+
| `@own` | Sub-element's own state | `@own(hovered)` |
|
|
457
|
+
| `@starting` | Entry animation | `@starting` |
|
|
458
|
+
| `_` | Fallback floor (always-on, never negated) | `_` |
|
|
459
|
+
| `:is()` | CSS `:is()` structural pseudo-class | `:is(fieldset > label)` |
|
|
460
|
+
| `:has()` | CSS `:has()` relational pseudo-class | `:has(> Icon)` |
|
|
461
|
+
| `:not()` | CSS `:not()` negation (prefer `!:is()`) | `:not(:first-child)` |
|
|
462
|
+
| `:where()` | CSS `:where()` (zero specificity) | `:where(Section)` |
|
|
463
|
+
|
|
464
|
+
> **Specificity.** All state selectors Tasty generates (modifiers, pseudo-classes,
|
|
465
|
+
> `:is()`/`:not()` groups, and `@root` / `@parent` context) are wrapped in
|
|
466
|
+
> `:where(...)` so they carry **zero specificity**. The only specificity anchors
|
|
467
|
+
> are the doubled component class (`.t0.t0`) and sub-element `[data-element]`
|
|
468
|
+
> attributes. This means overlapping rules (e.g. a `_` fallback floor and the
|
|
469
|
+
> states layered over it) resolve purely by **source order** — Tasty emits
|
|
470
|
+
> lower-priority states first and higher-priority states last so the cascade
|
|
471
|
+
> produces the intended winner.
|
|
279
472
|
|
|
280
473
|
### `@media(...)` — Media Queries
|
|
281
474
|
|
|
282
475
|
Media queries support dimension shorthands and custom unit expansion:
|
|
283
476
|
|
|
284
477
|
| Shorthand | Expands to |
|
|
285
|
-
|
|
286
|
-
| `w`
|
|
287
|
-
| `h`
|
|
478
|
+
| --------- | ---------- |
|
|
479
|
+
| `w` | `width` |
|
|
480
|
+
| `h` | `height` |
|
|
288
481
|
|
|
289
482
|
```jsx
|
|
290
483
|
fill: {
|
|
@@ -295,14 +488,14 @@ fill: {
|
|
|
295
488
|
}
|
|
296
489
|
```
|
|
297
490
|
|
|
298
|
-
| Tasty syntax
|
|
299
|
-
|
|
300
|
-
| `@media(w < 768px)`
|
|
301
|
-
| `@media(600px <= w < 1200px)`
|
|
302
|
-
| `@media:print`
|
|
303
|
-
| `@media:screen`
|
|
491
|
+
| Tasty syntax | CSS output |
|
|
492
|
+
| ------------------------------------ | ------------------------------------- |
|
|
493
|
+
| `@media(w < 768px)` | `@media (width < 768px)` |
|
|
494
|
+
| `@media(600px <= w < 1200px)` | `@media (600px <= width < 1200px)` |
|
|
495
|
+
| `@media:print` | `@media print` |
|
|
496
|
+
| `@media:screen` | `@media screen` |
|
|
304
497
|
| `@media(prefers-color-scheme: dark)` | `@media (prefers-color-scheme: dark)` |
|
|
305
|
-
| `@media(prefers-reduced-motion)`
|
|
498
|
+
| `@media(prefers-reduced-motion)` | `@media (prefers-reduced-motion)` |
|
|
306
499
|
|
|
307
500
|
Custom units work inside media queries: `@media(w < 40x)` → `@media (width < calc(var(--gap) * 40))`.
|
|
308
501
|
|
|
@@ -312,12 +505,12 @@ In practice, define state aliases via `configure({ states })` and use `@mobile`
|
|
|
312
505
|
|
|
313
506
|
Container queries use the syntax `@(name, condition)` for named containers or `@(condition)` for the nearest ancestor container. Dimension shorthands (`w`, `h`, `is`, `bs`) are expanded the same way as `@media`.
|
|
314
507
|
|
|
315
|
-
| Shorthand | Expands to
|
|
316
|
-
|
|
317
|
-
| `w`
|
|
318
|
-
| `h`
|
|
319
|
-
| `is`
|
|
320
|
-
| `bs`
|
|
508
|
+
| Shorthand | Expands to |
|
|
509
|
+
| --------- | ------------- |
|
|
510
|
+
| `w` | `width` |
|
|
511
|
+
| `h` | `height` |
|
|
512
|
+
| `is` | `inline-size` |
|
|
513
|
+
| `bs` | `block-size` |
|
|
321
514
|
|
|
322
515
|
```jsx
|
|
323
516
|
const Panel = tasty({
|
|
@@ -330,14 +523,14 @@ const Panel = tasty({
|
|
|
330
523
|
});
|
|
331
524
|
```
|
|
332
525
|
|
|
333
|
-
| Tasty syntax
|
|
334
|
-
|
|
335
|
-
| `@(layout, w < 600px)`
|
|
336
|
-
| `@(w < 600px)`
|
|
337
|
-
| `@(layout, $variant=danger)`
|
|
338
|
-
| `@(layout, $compact)`
|
|
339
|
-
| `@(scroll-state(stuck: top))`
|
|
340
|
-
| `@(nav, scroll-state(stuck: top))` | `@container nav scroll-state(stuck: top)`
|
|
526
|
+
| Tasty syntax | CSS output |
|
|
527
|
+
| ---------------------------------- | ---------------------------------------------- |
|
|
528
|
+
| `@(layout, w < 600px)` | `@container layout (width < 600px)` |
|
|
529
|
+
| `@(w < 600px)` | `@container (width < 600px)` |
|
|
530
|
+
| `@(layout, $variant=danger)` | `@container layout style(--variant: "danger")` |
|
|
531
|
+
| `@(layout, $compact)` | `@container layout style(--compact)` |
|
|
532
|
+
| `@(scroll-state(stuck: top))` | `@container scroll-state(stuck: top)` |
|
|
533
|
+
| `@(nav, scroll-state(stuck: top))` | `@container nav scroll-state(stuck: top)` |
|
|
341
534
|
|
|
342
535
|
Container style queries use `$prop` (boolean) or `$prop=value` syntax, which maps to CSS `style(--prop)` or `style(--prop: "value")`.
|
|
343
536
|
|
|
@@ -345,10 +538,10 @@ Container style queries use `$prop` (boolean) or `$prop=value` syntax, which map
|
|
|
345
538
|
|
|
346
539
|
Feature queries test CSS property support. Use `$` as the first argument to test selector support:
|
|
347
540
|
|
|
348
|
-
| Tasty syntax
|
|
349
|
-
|
|
350
|
-
| `@supports(display: grid)`
|
|
351
|
-
| `@supports($, :has(*))`
|
|
541
|
+
| Tasty syntax | CSS output |
|
|
542
|
+
| --------------------------- | --------------------------------- |
|
|
543
|
+
| `@supports(display: grid)` | `@supports (display: grid)` |
|
|
544
|
+
| `@supports($, :has(*))` | `@supports selector(:has(*))` |
|
|
352
545
|
| `!@supports(display: grid)` | `@supports (not (display: grid))` |
|
|
353
546
|
|
|
354
547
|
```jsx
|
|
@@ -358,6 +551,95 @@ display: {
|
|
|
358
551
|
}
|
|
359
552
|
```
|
|
360
553
|
|
|
554
|
+
### `_` — Fallback Floor
|
|
555
|
+
|
|
556
|
+
By default Tasty makes states **mutually exclusive**: a higher-priority state
|
|
557
|
+
turns the lower-priority ones off via negation, so exactly one branch applies.
|
|
558
|
+
This relies on `A | !A` always being true. CSS feature/container queries break
|
|
559
|
+
that assumption: `@supports(...)` and `@(...)` queries can be **unknown** (not
|
|
560
|
+
just true/false), and `not(unknown)` is also unknown — so a negated default
|
|
561
|
+
branch silently never applies. The classic case is `scroll-state`: a browser can
|
|
562
|
+
support `container-type: scroll-state` while a specific `scroll-state(...)` query
|
|
563
|
+
is unknown, leaving _no_ branch active.
|
|
564
|
+
|
|
565
|
+
The `_` fallback floor solves this. Use `_` as a **standalone key** and its value
|
|
566
|
+
**always applies** as a guaranteed floor: it never receives negation, and
|
|
567
|
+
higher-priority states simply layer over it via the cascade.
|
|
568
|
+
|
|
569
|
+
```jsx
|
|
570
|
+
inset: {
|
|
571
|
+
_: '0 top',
|
|
572
|
+
'@supports(container-type: scroll-state) & @(scroll-state(scrolled: block-end))':
|
|
573
|
+
'-80x top',
|
|
574
|
+
}
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
```css
|
|
578
|
+
.t0.t0 {
|
|
579
|
+
inset: 0...;
|
|
580
|
+
}
|
|
581
|
+
@container scroll-state(scrolled: block-end) {
|
|
582
|
+
@supports (container-type: scroll-state) {
|
|
583
|
+
.t0.t0 {
|
|
584
|
+
inset: -80px...;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
The floor is emitted as a bare rule (no negated `@supports` / container branches),
|
|
591
|
+
so it always applies. When the override matches it wins because it is emitted
|
|
592
|
+
later and all state selectors share the same specificity (see the note on
|
|
593
|
+
`:where()` below).
|
|
594
|
+
|
|
595
|
+
Notes:
|
|
596
|
+
|
|
597
|
+
- `_` is a **standalone key only** — it defines a single map-wide floor and
|
|
598
|
+
cannot be combined with state logic. Keys like `_ & hovered` or `_ | focused`
|
|
599
|
+
are ignored with an `INVALID_FALLBACK_KEY` dev warning.
|
|
600
|
+
- `_` can coexist with the bare `''` default when other states exist: `''` is the
|
|
601
|
+
negated default, `_` is the always-on floor. With only `_` and `''` (no other
|
|
602
|
+
states) the `''` default is redundant — Tasty keeps the `_` value and drops
|
|
603
|
+
`''` with a `REDUNDANT_DEFAULT_STATE` warning.
|
|
604
|
+
- `_` is position-independent: it is always placed at the lowest priority
|
|
605
|
+
regardless of where it appears in the map.
|
|
606
|
+
|
|
607
|
+
#### `_` vs the `''` default
|
|
608
|
+
|
|
609
|
+
In simple maps where every other state is a plain modifier (always cleanly
|
|
610
|
+
on/off), `_` and `''` produce the **same visible result** — the base value shows
|
|
611
|
+
whenever no other state wins. They diverge in three situations, and the root
|
|
612
|
+
cause is always the same: **`''` is mutually exclusive (turned off by negation),
|
|
613
|
+
while `_` is always on (layered underneath).**
|
|
614
|
+
|
|
615
|
+
- **Unknown / invalid branches.** If a higher-priority branch sits behind a query
|
|
616
|
+
that can be _unknown_ (`@supports`, container, `scroll-state`) or uses a
|
|
617
|
+
selector the browser drops, the negated `''` default disappears along with it
|
|
618
|
+
(`not(unknown) = unknown`), leaving the property with no rule. The `_` floor
|
|
619
|
+
survives because it is an unconditional bare rule. This is the case `_` exists
|
|
620
|
+
for.
|
|
621
|
+
|
|
622
|
+
- **Empty / "unset" states.** A state can intentionally produce _no_ output (an
|
|
623
|
+
empty value) to leave a property unset while that state is active. With `''`
|
|
624
|
+
this works — the default is negated away and nothing replaces it, so the
|
|
625
|
+
property unsets. With `_` the floor can never be turned off, so its value keeps
|
|
626
|
+
applying and the "unset on this state" intent is lost.
|
|
627
|
+
|
|
628
|
+
```jsx
|
|
629
|
+
// '' : color unsets while loading. '_' : color stays #text while loading.
|
|
630
|
+
color: { '': '#text', loading: '' }
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
- **States with different output shapes.** Because `_` layers additively, when
|
|
634
|
+
different states emit _different sets_ of declarations, the floor's
|
|
635
|
+
declarations bleed through wherever the winning state does not override the
|
|
636
|
+
same property — which can produce inconsistent combinations. A `''` default is
|
|
637
|
+
swapped out cleanly by its mutually-exclusive condition.
|
|
638
|
+
|
|
639
|
+
Rule of thumb: **use `''` for the normal default** (clean mutual exclusivity,
|
|
640
|
+
supports unsetting), and reach for `_` only when a value must survive an
|
|
641
|
+
_unknown_ higher-priority branch.
|
|
642
|
+
|
|
361
643
|
### `@root(...)` — Root Element States
|
|
362
644
|
|
|
363
645
|
Root states generate selectors on the `:root` element. They are useful for theme modes, feature flags, and other page-level conditions:
|
|
@@ -372,13 +654,13 @@ color: {
|
|
|
372
654
|
}
|
|
373
655
|
```
|
|
374
656
|
|
|
375
|
-
| Tasty syntax
|
|
376
|
-
|
|
377
|
-
| `@root(schema=dark)`
|
|
378
|
-
| `@root(hovered)`
|
|
379
|
-
| `@root(.premium-user)` | `:root.premium-user`
|
|
380
|
-
| `@root([lang="en"])`
|
|
381
|
-
| `!@root(schema=dark)`
|
|
657
|
+
| Tasty syntax | CSS selector |
|
|
658
|
+
| ---------------------- | --------------------------------- |
|
|
659
|
+
| `@root(schema=dark)` | `:root[data-schema="dark"]` |
|
|
660
|
+
| `@root(hovered)` | `:root[data-hovered]` |
|
|
661
|
+
| `@root(.premium-user)` | `:root.premium-user` |
|
|
662
|
+
| `@root([lang="en"])` | `:root[lang="en"]` |
|
|
663
|
+
| `!@root(schema=dark)` | `:root:not([data-schema="dark"])` |
|
|
382
664
|
|
|
383
665
|
Root conditions are prepended to the component selector: `:root[data-schema="dark"] .t0.t0 { ... }`.
|
|
384
666
|
|
|
@@ -394,7 +676,7 @@ const Nav = tasty({
|
|
|
394
676
|
'': '#text',
|
|
395
677
|
'@own(:hover)': '#primary',
|
|
396
678
|
'@own(:focus-visible)': '#primary',
|
|
397
|
-
|
|
679
|
+
selected: '#primary', // root-level modifier
|
|
398
680
|
},
|
|
399
681
|
},
|
|
400
682
|
},
|
|
@@ -402,11 +684,11 @@ const Nav = tasty({
|
|
|
402
684
|
});
|
|
403
685
|
```
|
|
404
686
|
|
|
405
|
-
| Tasty syntax (inside sub-element) | CSS output
|
|
406
|
-
|
|
407
|
-
| `@own(:hover)`
|
|
408
|
-
| `@own(hovered)`
|
|
409
|
-
| `@own(theme=dark)`
|
|
687
|
+
| Tasty syntax (inside sub-element) | CSS output |
|
|
688
|
+
| --------------------------------- | ------------------------------------------------- |
|
|
689
|
+
| `@own(:hover)` | `:hover` on the sub-element selector |
|
|
690
|
+
| `@own(hovered)` | `[data-hovered]` on the sub-element selector |
|
|
691
|
+
| `@own(theme=dark)` | `[data-theme="dark"]` on the sub-element selector |
|
|
410
692
|
|
|
411
693
|
`@own()` is only valid inside sub-element styles. Using it on root styles emits a warning and is treated as a regular modifier.
|
|
412
694
|
|
|
@@ -424,35 +706,35 @@ const FadeIn = tasty({
|
|
|
424
706
|
});
|
|
425
707
|
```
|
|
426
708
|
|
|
427
|
-
| Tasty syntax | CSS output
|
|
428
|
-
|
|
429
|
-
| `@starting`
|
|
709
|
+
| Tasty syntax | CSS output |
|
|
710
|
+
| ------------ | ------------------------------------ |
|
|
711
|
+
| `@starting` | `@starting-style { .t0.t0 { ... } }` |
|
|
430
712
|
|
|
431
713
|
### `@parent(...)` — Parent Element States
|
|
432
714
|
|
|
433
|
-
Style based on ancestor element attributes. Uses `:is([selector] *)` / `:not([selector] *)` for symmetric, composable parent checks. Boolean logic (`&`, `|`,
|
|
715
|
+
Style based on ancestor element attributes. Uses `:is([selector] *)` / `:not([selector] *)` for symmetric, composable parent checks. Boolean logic (`&`, `|`, `!`, `^`) is supported inside `@parent()`.
|
|
434
716
|
|
|
435
717
|
```jsx
|
|
436
718
|
const Highlight = tasty({
|
|
437
719
|
styles: {
|
|
438
720
|
fill: {
|
|
439
721
|
'': '#white',
|
|
440
|
-
'@parent(hovered)': '#gray.05',
|
|
441
|
-
'@parent(theme=dark, >)': '#dark-02',
|
|
722
|
+
'@parent(hovered)': '#gray.05', // Any ancestor has [data-hovered]
|
|
723
|
+
'@parent(theme=dark, >)': '#dark-02', // Direct parent has [data-theme="dark"]
|
|
442
724
|
},
|
|
443
725
|
},
|
|
444
726
|
});
|
|
445
727
|
```
|
|
446
728
|
|
|
447
|
-
| Syntax
|
|
448
|
-
|
|
449
|
-
| `@parent(hovered)`
|
|
450
|
-
| `!@parent(hovered)`
|
|
451
|
-
| `@parent(hovered, >)`
|
|
452
|
-
| `@parent(.active)`
|
|
453
|
-
| `@parent(hovered & focused)`
|
|
729
|
+
| Syntax | CSS Output |
|
|
730
|
+
| ------------------------------------- | -------------------------------------------------------------------- |
|
|
731
|
+
| `@parent(hovered)` | `:is([data-hovered] *)` |
|
|
732
|
+
| `!@parent(hovered)` | `:not([data-hovered] *)` |
|
|
733
|
+
| `@parent(hovered, >)` | `:is([data-hovered] > *)` (direct parent) |
|
|
734
|
+
| `@parent(.active)` | `:is(.active *)` |
|
|
735
|
+
| `@parent(hovered & focused)` | `:is([data-hovered][data-focused] *)` (same ancestor) |
|
|
454
736
|
| `@parent(hovered) & @parent(focused)` | `:is([data-hovered] *):is([data-focused] *)` (independent ancestors) |
|
|
455
|
-
| `@parent(hovered \| focused)`
|
|
737
|
+
| `@parent(hovered \| focused)` | `:is([data-hovered] *, [data-focused] *)` (OR inside single wrapper) |
|
|
456
738
|
|
|
457
739
|
For sub-elements, the parent check applies to the root element's ancestors:
|
|
458
740
|
|
|
@@ -483,37 +765,38 @@ const Card = tasty({
|
|
|
483
765
|
styles: {
|
|
484
766
|
display: {
|
|
485
767
|
'': 'block',
|
|
486
|
-
':has(> Icon)': 'grid',
|
|
487
|
-
':has(+ Icon)': 'grid',
|
|
488
|
-
':has(~ Icon)': 'grid',
|
|
489
|
-
':has(Icon +)': 'grid',
|
|
490
|
-
':has(Icon ~)': 'grid',
|
|
491
|
-
':is(fieldset > label)': 'inline',
|
|
492
|
-
'!:has(> Icon)': 'flex',
|
|
768
|
+
':has(> Icon)': 'grid', // has Icon as direct child
|
|
769
|
+
':has(+ Icon)': 'grid', // immediately followed by an Icon sibling
|
|
770
|
+
':has(~ Icon)': 'grid', // has an Icon sibling somewhere after
|
|
771
|
+
':has(Icon +)': 'grid', // immediately preceded by an Icon sibling (auto-completes to `Icon + *`)
|
|
772
|
+
':has(Icon ~)': 'grid', // has an Icon sibling somewhere before (auto-completes to `Icon ~ *`)
|
|
773
|
+
':is(fieldset > label)': 'inline', // is a label inside a fieldset (HTML tags)
|
|
774
|
+
'!:has(> Icon)': 'flex', // negation: no Icon child
|
|
493
775
|
},
|
|
494
776
|
},
|
|
495
777
|
});
|
|
496
778
|
```
|
|
497
779
|
|
|
498
|
-
| Syntax
|
|
499
|
-
|
|
500
|
-
| `:has(> Icon)`
|
|
501
|
-
| `:has(+ Icon)`
|
|
502
|
-
| `:has(~ Icon)`
|
|
503
|
-
| `:has(Icon +)`
|
|
504
|
-
| `:has(Icon ~)`
|
|
505
|
-
| `:has(>)`
|
|
506
|
-
| `:is(> Field + input)` | `:is(> [data-element="Field"] + input)` | Structural match
|
|
507
|
-
| `:has(button)`
|
|
508
|
-
| `!:has(> Icon)`
|
|
509
|
-
| `!:is(Panel)`
|
|
510
|
-
|
|
511
|
-
Combine with other states using boolean logic:
|
|
780
|
+
| Syntax | CSS Output | Meaning |
|
|
781
|
+
| ---------------------- | --------------------------------------- | --------------------------------------- |
|
|
782
|
+
| `:has(> Icon)` | `:has(> [data-element="Icon"])` | Has Icon as direct child |
|
|
783
|
+
| `:has(+ Icon)` | `:has(+ [data-element="Icon"])` | Immediately followed by an Icon sibling |
|
|
784
|
+
| `:has(~ Icon)` | `:has(~ [data-element="Icon"])` | Has an Icon sibling somewhere after |
|
|
785
|
+
| `:has(Icon +)` | `:has([data-element="Icon"] + *)` | Immediately preceded by an Icon sibling |
|
|
786
|
+
| `:has(Icon ~)` | `:has([data-element="Icon"] ~ *)` | Has an Icon sibling somewhere before |
|
|
787
|
+
| `:has(>)` | `:has(> *)` | Has any direct child |
|
|
788
|
+
| `:is(> Field + input)` | `:is(> [data-element="Field"] + input)` | Structural match |
|
|
789
|
+
| `:has(button)` | `:has(button)` | HTML tag (lowercase, unchanged) |
|
|
790
|
+
| `!:has(> Icon)` | `:not(:has(> [data-element="Icon"]))` | Negation (use `!`) |
|
|
791
|
+
| `!:is(Panel)` | `:not([data-element="Panel"])` | Negation (use `!:is`) |
|
|
792
|
+
|
|
793
|
+
Combine with other states using boolean logic (`&`, `|`, `!`, `^`):
|
|
512
794
|
|
|
513
795
|
```jsx
|
|
514
|
-
':has(> Icon) & hovered'
|
|
515
|
-
'@parent(hovered) & :has(> Icon)'
|
|
516
|
-
':has(> Icon) | :has(> Button)'
|
|
796
|
+
':has(> Icon) & hovered'; // AND: structural + data attribute
|
|
797
|
+
'@parent(hovered) & :has(> Icon)'; // AND: parent check + structural
|
|
798
|
+
':has(> Icon) | :has(> Button)'; // OR: either sub-element present
|
|
799
|
+
':has(> Icon) ^ :has(> Button)'; // XOR: exactly one present
|
|
517
800
|
```
|
|
518
801
|
|
|
519
802
|
> **Nesting limit:** The state key parser supports up to 2 levels of nested parentheses inside `:is()`, `:has()`, `:not()`, and `:where()` — e.g. `:has(Input:not(:disabled))` works, but 3+ levels like `:has(:is(:not(:hover)))` will not be tokenized correctly. This covers virtually all practical use cases.
|
|
@@ -548,8 +831,8 @@ CSS cannot transition or animate custom properties unless the browser knows thei
|
|
|
548
831
|
const AnimatedGradient = tasty({
|
|
549
832
|
styles: {
|
|
550
833
|
'$gradient-angle': '0deg',
|
|
551
|
-
'#theme': '
|
|
552
|
-
|
|
834
|
+
'#theme': 'okhst(280 80% 50%)',
|
|
835
|
+
image: 'linear-gradient($gradient-angle, #theme, #clear)',
|
|
553
836
|
transition: '$$gradient-angle 0.3s, ##theme 0.3s',
|
|
554
837
|
},
|
|
555
838
|
});
|
|
@@ -557,30 +840,30 @@ const AnimatedGradient = tasty({
|
|
|
557
840
|
|
|
558
841
|
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.
|
|
559
842
|
|
|
560
|
-
Use explicit `@
|
|
843
|
+
Use explicit `@property` when you need non-default settings like `inherits: false`:
|
|
561
844
|
|
|
562
845
|
```jsx
|
|
563
|
-
'@
|
|
846
|
+
'@property': {
|
|
564
847
|
'$gradient-angle': { syntax: '<angle>', inherits: false, initialValue: '0deg' },
|
|
565
848
|
},
|
|
566
849
|
```
|
|
567
850
|
|
|
568
851
|
---
|
|
569
852
|
|
|
570
|
-
## Font Face (`@
|
|
853
|
+
## Font Face (`@font-face`)
|
|
571
854
|
|
|
572
855
|
Register custom fonts directly inside a `styles` object. Keys are font-family names, values are descriptor objects (or arrays of them for multiple weights/styles).
|
|
573
856
|
|
|
574
857
|
```ts
|
|
575
858
|
const Heading = tasty({
|
|
576
859
|
styles: {
|
|
577
|
-
'@
|
|
860
|
+
'@font-face': {
|
|
578
861
|
'Brand Sans': {
|
|
579
862
|
src: 'url("/fonts/brand-sans.woff2") format("woff2")',
|
|
580
863
|
fontDisplay: 'swap',
|
|
581
864
|
},
|
|
582
865
|
},
|
|
583
|
-
|
|
866
|
+
font: '"Brand Sans", sans-serif',
|
|
584
867
|
},
|
|
585
868
|
});
|
|
586
869
|
```
|
|
@@ -590,7 +873,7 @@ const Heading = tasty({
|
|
|
590
873
|
Supply an array to register several variants of the same family:
|
|
591
874
|
|
|
592
875
|
```ts
|
|
593
|
-
'@
|
|
876
|
+
'@font-face': {
|
|
594
877
|
'Brand Sans': [
|
|
595
878
|
{ src: 'url("/fonts/brand-regular.woff2") format("woff2")', fontWeight: 400, fontDisplay: 'swap' },
|
|
596
879
|
{ src: 'url("/fonts/brand-bold.woff2") format("woff2")', fontWeight: 700, fontDisplay: 'swap' },
|
|
@@ -600,26 +883,26 @@ Supply an array to register several variants of the same family:
|
|
|
600
883
|
|
|
601
884
|
### Supported descriptors
|
|
602
885
|
|
|
603
|
-
| Descriptor
|
|
604
|
-
|
|
605
|
-
| `src` (required)
|
|
606
|
-
| `fontWeight`
|
|
607
|
-
| `fontStyle`
|
|
608
|
-
| `fontStretch`
|
|
609
|
-
| `fontDisplay`
|
|
610
|
-
| `unicodeRange`
|
|
611
|
-
| `ascentOverride`
|
|
612
|
-
| `descentOverride`
|
|
613
|
-
| `lineGapOverride`
|
|
614
|
-
| `sizeAdjust`
|
|
615
|
-
| `fontFeatureSettings`
|
|
616
|
-
| `fontVariationSettings` | `font-variation-settings` | `string`
|
|
886
|
+
| Descriptor | CSS property | Type |
|
|
887
|
+
| ----------------------- | ------------------------- | --------------------------------------------------------- |
|
|
888
|
+
| `src` (required) | `src` | `string` |
|
|
889
|
+
| `fontWeight` | `font-weight` | `string \| number` |
|
|
890
|
+
| `fontStyle` | `font-style` | `string` |
|
|
891
|
+
| `fontStretch` | `font-stretch` | `string` |
|
|
892
|
+
| `fontDisplay` | `font-display` | `'auto' \| 'block' \| 'swap' \| 'fallback' \| 'optional'` |
|
|
893
|
+
| `unicodeRange` | `unicode-range` | `string` |
|
|
894
|
+
| `ascentOverride` | `ascent-override` | `string` |
|
|
895
|
+
| `descentOverride` | `descent-override` | `string` |
|
|
896
|
+
| `lineGapOverride` | `line-gap-override` | `string` |
|
|
897
|
+
| `sizeAdjust` | `size-adjust` | `string` |
|
|
898
|
+
| `fontFeatureSettings` | `font-feature-settings` | `string` |
|
|
899
|
+
| `fontVariationSettings` | `font-variation-settings` | `string` |
|
|
617
900
|
|
|
618
901
|
> Font-face rules are permanent — they are injected once and never cleaned up, matching how browsers handle `@font-face`.
|
|
619
902
|
|
|
620
903
|
---
|
|
621
904
|
|
|
622
|
-
## Counter Style (`@
|
|
905
|
+
## Counter Style (`@counter-style`)
|
|
623
906
|
|
|
624
907
|
Define custom list markers via the CSS `@counter-style` at-rule. Keys are counter-style names, values are descriptor objects.
|
|
625
908
|
|
|
@@ -627,7 +910,7 @@ Define custom list markers via the CSS `@counter-style` at-rule. Keys are counte
|
|
|
627
910
|
const EmojiList = tasty({
|
|
628
911
|
tag: 'ol',
|
|
629
912
|
styles: {
|
|
630
|
-
'@
|
|
913
|
+
'@counter-style': {
|
|
631
914
|
thumbs: {
|
|
632
915
|
system: 'cyclic',
|
|
633
916
|
symbols: '"👍"',
|
|
@@ -641,23 +924,88 @@ const EmojiList = tasty({
|
|
|
641
924
|
|
|
642
925
|
### Supported descriptors
|
|
643
926
|
|
|
644
|
-
| Descriptor
|
|
645
|
-
|
|
646
|
-
| `system` (required) | `system`
|
|
647
|
-
| `symbols`
|
|
648
|
-
| `additiveSymbols`
|
|
649
|
-
| `prefix`
|
|
650
|
-
| `suffix`
|
|
651
|
-
| `negative`
|
|
652
|
-
| `range`
|
|
653
|
-
| `pad`
|
|
654
|
-
| `fallback`
|
|
655
|
-
| `speakAs`
|
|
927
|
+
| Descriptor | CSS property | Type |
|
|
928
|
+
| ------------------- | ------------------ | ---------------------------------------------------------------------------------------- |
|
|
929
|
+
| `system` (required) | `system` | `'cyclic' \| 'numeric' \| 'alphabetic' \| 'symbolic' \| 'additive' \| 'fixed' \| string` |
|
|
930
|
+
| `symbols` | `symbols` | `string` |
|
|
931
|
+
| `additiveSymbols` | `additive-symbols` | `string` |
|
|
932
|
+
| `prefix` | `prefix` | `string` |
|
|
933
|
+
| `suffix` | `suffix` | `string` |
|
|
934
|
+
| `negative` | `negative` | `string` |
|
|
935
|
+
| `range` | `range` | `string` |
|
|
936
|
+
| `pad` | `pad` | `string` |
|
|
937
|
+
| `fallback` | `fallback` | `string` |
|
|
938
|
+
| `speakAs` | `speak-as` | `string` |
|
|
656
939
|
|
|
657
940
|
> Counter-style rules are permanent — they are injected once and never cleaned up, matching how browsers handle `@counter-style`.
|
|
658
941
|
|
|
659
942
|
---
|
|
660
943
|
|
|
944
|
+
## Functions (`@function`)
|
|
945
|
+
|
|
946
|
+
Define reusable, parameterized CSS [custom functions](https://developer.mozilla.org/en-US/docs/Web/CSS/@function) via the CSS `@function` at-rule. A custom function takes parameters and returns a single value — like a dynamic custom property.
|
|
947
|
+
|
|
948
|
+
The token convention follows Tasty's `$` vs `$$` distinction:
|
|
949
|
+
|
|
950
|
+
- **`$$name`** — the literal callable name (`--name`). Used for the **definition key** and for **invocation** `$$name(...)`, mirroring the existing `$$gradient-angle` → `--gradient-angle` sugar. The definition key matches the call site.
|
|
951
|
+
- **`$name`** — a custom-property declaration/reference (`var(--name)`). Used for **parameters** and **local variables**, exactly like declaring custom props in a styles object.
|
|
952
|
+
- **Local variables** are declared directly as `$name` keys on the descriptor — the body reads like a mini styles object.
|
|
953
|
+
- `result`, local-variable values, and parameter `default` values flow through the full Tasty DSL, so units (`2x`), color tokens (`#theme`), auto-calc (`(-1 * $value)` → `calc(-1 * var(--value))`), and fallbacks (`($shadow-color, black)` → `var(--shadow-color, black)`) all work.
|
|
954
|
+
|
|
955
|
+
```ts
|
|
956
|
+
const Box = tasty({
|
|
957
|
+
styles: {
|
|
958
|
+
'@function': {
|
|
959
|
+
// simplest: one bare param + result (auto-calc, no explicit calc())
|
|
960
|
+
$$negative: { args: ['$value'], result: '(-1 * $value)' },
|
|
961
|
+
|
|
962
|
+
// typed param + default + return type + local variable
|
|
963
|
+
$$shadow: {
|
|
964
|
+
args: { '$shadow-color': { syntax: '<color>', default: 'inherit' } },
|
|
965
|
+
returns: '<color>',
|
|
966
|
+
$offset: '2px', // local variable
|
|
967
|
+
result: '$offset $offset ($shadow-color, black)',
|
|
968
|
+
},
|
|
969
|
+
},
|
|
970
|
+
margin: '$$negative(10px) top', // → margin: --negative(10px) 0 0 0
|
|
971
|
+
shadow: '$$shadow(#accent)', // → box-shadow: --shadow(var(--accent-color))
|
|
972
|
+
},
|
|
973
|
+
});
|
|
974
|
+
```
|
|
975
|
+
|
|
976
|
+
Generated CSS:
|
|
977
|
+
|
|
978
|
+
```css
|
|
979
|
+
@function --negative(--value) {
|
|
980
|
+
result: calc(-1 * var(--value));
|
|
981
|
+
}
|
|
982
|
+
@function --shadow(--shadow-color <color>: inherit) returns <color> {
|
|
983
|
+
--offset: 2px;
|
|
984
|
+
result: var(--offset) var(--offset) var(--shadow-color, black);
|
|
985
|
+
}
|
|
986
|
+
.t0 {
|
|
987
|
+
margin: --negative(10px) 0 0 0;
|
|
988
|
+
box-shadow: --shadow(var(--accent-color));
|
|
989
|
+
}
|
|
990
|
+
```
|
|
991
|
+
|
|
992
|
+
### Descriptor shape
|
|
993
|
+
|
|
994
|
+
| Field | Type | Notes |
|
|
995
|
+
| ------------------- | ------------------------------------------------- | ---------------------------------------------------------------------------------------- |
|
|
996
|
+
| `result` (required) | `string` | The `result:` value, parsed through the Tasty DSL. |
|
|
997
|
+
| `args` | `string[]` \| `Record<string, FunctionParameter>` | Ordered parameters. Array form lists bare names; object form maps names to type/default. |
|
|
998
|
+
| `returns` | `string` | Optional return type, e.g. `'<color>'`. |
|
|
999
|
+
| `$name` keys | `string \| number` | Any `$name` key declares a local variable `--name` (value parsed). |
|
|
1000
|
+
|
|
1001
|
+
A `FunctionParameter` is one of: `true` (bare param), a string CSS type shorthand (`'<length>'`), or `{ syntax?: string; default?: string | number }`.
|
|
1002
|
+
|
|
1003
|
+
Functions can also be registered with the [`useFunction`](react-api.md#usefunction) hook or globally via [`configure({ functions })`](configuration.md#functions) (the same map also accepts bare-key parse functions).
|
|
1004
|
+
|
|
1005
|
+
> `@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.
|
|
1006
|
+
|
|
1007
|
+
---
|
|
1008
|
+
|
|
661
1009
|
## Style Properties
|
|
662
1010
|
|
|
663
1011
|
For a complete reference of all enhanced style properties — syntax, values, modifiers, and recommendations — see **[Style Properties Reference](styles.md)**.
|
|
@@ -666,8 +1014,8 @@ For a complete reference of all enhanced style properties — syntax, values, mo
|
|
|
666
1014
|
|
|
667
1015
|
## Learn more
|
|
668
1016
|
|
|
669
|
-
- **[
|
|
1017
|
+
- **[React API](react-api.md)** — `tasty()` factory, component props, variants, sub-elements, style functions
|
|
670
1018
|
- **[Methodology](methodology.md)** — Recommended patterns: root + sub-elements, styleProps, tokens, wrapping
|
|
671
1019
|
- **[Configuration](configuration.md)** — Tokens, recipes, custom units, style handlers, TypeScript extensions
|
|
672
1020
|
- **[Style Properties](styles.md)** — Complete reference for all enhanced style properties
|
|
673
|
-
- **[
|
|
1021
|
+
- **[Build-Time Extraction (`tastyStatic`)](tasty-static.md)** — Static styling with the Babel plugin
|