@tenphi/tasty 0.0.0-snapshot.1c5dff6 → 0.0.0-snapshot.1f1c00a
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 +377 -238
- package/dist/babel-rZcEwPLM.d.ts +82 -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-CL4eRu2z.js +42 -0
- package/dist/chunks/build-config-CL4eRu2z.js.map +1 -0
- package/dist/chunks/collector-D2ARGT2L.js +208 -0
- package/dist/chunks/collector-D2ARGT2L.js.map +1 -0
- package/dist/chunks/config-engine-DEmmPPdp.js +272 -0
- package/dist/chunks/config-engine-DEmmPPdp.js.map +1 -0
- package/dist/chunks/css-definitions-DotDkIXt.js +876 -0
- package/dist/chunks/css-definitions-DotDkIXt.js.map +1 -0
- package/dist/chunks/css-resources-P7IBUUZs.js +142 -0
- package/dist/chunks/css-resources-P7IBUUZs.js.map +1 -0
- package/dist/chunks/debug-D8pg9RGI.js +502 -0
- package/dist/chunks/debug-D8pg9RGI.js.map +1 -0
- package/dist/chunks/dsl-DRhFWQzo.js +1378 -0
- package/dist/chunks/dsl-DRhFWQzo.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-BpxlOTbO.js +562 -0
- package/dist/chunks/react-runtime-BpxlOTbO.js.map +1 -0
- package/dist/chunks/runtime-engine-CKFXUH-A.js +2416 -0
- package/dist/chunks/runtime-engine-CKFXUH-A.js.map +1 -0
- package/dist/chunks/runtime-state-BxaLhVdP.js +127 -0
- package/dist/chunks/runtime-state-BxaLhVdP.js.map +1 -0
- package/dist/{utils/merge-styles.js → chunks/shared-utils-y-HjvPN4.js} +61 -58
- package/dist/chunks/shared-utils-y-HjvPN4.js.map +1 -0
- package/dist/chunks/style-engine-BBbBuSkT.js +5260 -0
- package/dist/chunks/style-engine-BBbBuSkT.js.map +1 -0
- package/dist/{zero/extractor.js → chunks/zero-engine-gJrKBRiw.js} +126 -66
- package/dist/chunks/zero-engine-gJrKBRiw.js.map +1 -0
- package/dist/collector-w4mBj5gd.d.ts +164 -0
- package/dist/config-CZWCzBr6.d.ts +1349 -0
- package/dist/core/index.d.ts +5 -33
- package/dist/core/index.js +9 -26
- package/dist/index-G9JuwM9p.d.ts +1920 -0
- package/dist/index.d.ts +5 -48
- package/dist/index.js +11 -33
- package/dist/{utils/merge-styles.d.ts → merge-styles-VXwJ0Frr.d.ts} +3 -4
- package/dist/ssr/astro-client.d.ts +1 -0
- 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 +10 -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 +10 -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 +17 -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 +18 -0
- package/dist/ssr/astro-middleware.js +7 -0
- package/dist/ssr/astro-middleware.js.map +1 -0
- package/dist/ssr/astro.d.ts +127 -0
- package/dist/ssr/astro.js +221 -0
- package/dist/ssr/astro.js.map +1 -0
- package/dist/ssr/index.d.ts +36 -0
- package/dist/ssr/index.js +10 -0
- package/dist/ssr/index.js.map +1 -0
- package/dist/ssr/next-config.d.ts +64 -0
- package/dist/ssr/next-config.js +128 -0
- package/dist/ssr/next-config.js.map +1 -0
- package/dist/ssr/next.d.ts +46 -0
- package/dist/ssr/next.js +60 -0
- package/dist/ssr/next.js.map +1 -0
- package/dist/static/index.d.ts +90 -5
- package/dist/static/index.js +31 -4
- package/dist/static/index.js.map +1 -0
- package/dist/static/inject.d.ts +4 -0
- package/dist/static/inject.js +17 -0
- package/dist/static/inject.js.map +1 -0
- package/dist/types-JEY8PVYP.d.ts +1567 -0
- package/dist/zero/babel.d.ts +2 -113
- package/dist/zero/babel.js +230 -106
- package/dist/zero/babel.js.map +1 -1
- package/dist/zero/index.d.ts +80 -3
- package/dist/zero/index.js +2 -4
- package/dist/zero/next.d.ts +57 -33
- package/dist/zero/next.js +93 -59
- package/dist/zero/next.js.map +1 -1
- package/docs/README.md +39 -0
- package/docs/adoption.md +323 -0
- package/docs/ai-agents.md +250 -0
- package/docs/comparison.md +448 -0
- package/docs/configuration.md +684 -56
- package/docs/debug.md +170 -343
- package/docs/design-system.md +455 -0
- package/docs/dsl.md +1065 -0
- package/docs/getting-started.md +240 -0
- package/docs/injector.md +159 -53
- package/docs/methodology.md +648 -0
- package/docs/migration-v3.md +285 -0
- package/docs/pipeline.md +765 -0
- package/docs/plugins.md +353 -0
- package/docs/react-api.md +683 -0
- package/docs/runtime-benchmarks.md +388 -0
- package/docs/ssr.md +610 -0
- package/docs/styles.md +211 -63
- package/docs/tasty-static.md +273 -67
- package/package.json +182 -58
- package/tasty.config.ts +6 -0
- package/dist/_virtual/_rolldown/runtime.js +0 -8
- package/dist/chunks/cacheKey.js +0 -70
- package/dist/chunks/cacheKey.js.map +0 -1
- package/dist/chunks/definitions.d.ts +0 -37
- package/dist/chunks/definitions.js +0 -260
- package/dist/chunks/definitions.js.map +0 -1
- package/dist/chunks/renderChunk.js +0 -61
- package/dist/chunks/renderChunk.js.map +0 -1
- package/dist/config.d.ts +0 -288
- package/dist/config.js +0 -403
- package/dist/config.js.map +0 -1
- package/dist/debug.d.ts +0 -204
- package/dist/debug.js +0 -733
- package/dist/debug.js.map +0 -1
- package/dist/hooks/useGlobalStyles.d.ts +0 -27
- package/dist/hooks/useGlobalStyles.js +0 -56
- package/dist/hooks/useGlobalStyles.js.map +0 -1
- package/dist/hooks/useKeyframes.d.ts +0 -56
- package/dist/hooks/useKeyframes.js +0 -54
- package/dist/hooks/useKeyframes.js.map +0 -1
- package/dist/hooks/useProperty.d.ts +0 -79
- package/dist/hooks/useProperty.js +0 -91
- package/dist/hooks/useProperty.js.map +0 -1
- package/dist/hooks/useRawCSS.d.ts +0 -53
- package/dist/hooks/useRawCSS.js +0 -28
- package/dist/hooks/useRawCSS.js.map +0 -1
- package/dist/hooks/useStyles.d.ts +0 -40
- package/dist/hooks/useStyles.js +0 -169
- package/dist/hooks/useStyles.js.map +0 -1
- package/dist/injector/index.d.ts +0 -157
- package/dist/injector/index.js +0 -154
- package/dist/injector/index.js.map +0 -1
- package/dist/injector/injector.d.ts +0 -139
- package/dist/injector/injector.js +0 -417
- package/dist/injector/injector.js.map +0 -1
- package/dist/injector/sheet-manager.d.ts +0 -135
- package/dist/injector/sheet-manager.js +0 -728
- package/dist/injector/sheet-manager.js.map +0 -1
- package/dist/injector/types.d.ts +0 -144
- 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 -33
- 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 -116
- 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 -231
- package/dist/pipeline/exclusive.js.map +0 -1
- package/dist/pipeline/index.d.ts +0 -53
- package/dist/pipeline/index.js +0 -660
- package/dist/pipeline/index.js.map +0 -1
- package/dist/pipeline/materialize.js +0 -856
- package/dist/pipeline/materialize.js.map +0 -1
- package/dist/pipeline/parseStateKey.d.ts +0 -15
- package/dist/pipeline/parseStateKey.js +0 -451
- package/dist/pipeline/parseStateKey.js.map +0 -1
- package/dist/pipeline/simplify.js +0 -516
- 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/okhsl-plugin.d.ts +0 -35
- package/dist/plugins/okhsl-plugin.js +0 -371
- package/dist/plugins/okhsl-plugin.js.map +0 -1
- package/dist/plugins/types.d.ts +0 -69
- package/dist/properties/index.js +0 -221
- 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 -83
- package/dist/properties/property-type-resolver.js.map +0 -1
- package/dist/states/index.d.ts +0 -49
- package/dist/states/index.js +0 -416
- package/dist/states/index.js.map +0 -1
- package/dist/static/tastyStatic.d.ts +0 -46
- package/dist/static/tastyStatic.js +0 -31
- 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/align.d.ts +0 -15
- package/dist/styles/align.js +0 -14
- package/dist/styles/align.js.map +0 -1
- package/dist/styles/border.d.ts +0 -25
- package/dist/styles/border.js +0 -114
- package/dist/styles/border.js.map +0 -1
- package/dist/styles/color.d.ts +0 -14
- package/dist/styles/color.js +0 -23
- package/dist/styles/color.js.map +0 -1
- package/dist/styles/createStyle.js +0 -77
- package/dist/styles/createStyle.js.map +0 -1
- package/dist/styles/dimension.js +0 -97
- package/dist/styles/dimension.js.map +0 -1
- package/dist/styles/display.d.ts +0 -37
- package/dist/styles/display.js +0 -67
- package/dist/styles/display.js.map +0 -1
- package/dist/styles/fade.d.ts +0 -15
- package/dist/styles/fade.js +0 -58
- package/dist/styles/fade.js.map +0 -1
- package/dist/styles/fill.d.ts +0 -42
- package/dist/styles/fill.js +0 -52
- 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 -37
- package/dist/styles/gap.js.map +0 -1
- package/dist/styles/height.d.ts +0 -17
- package/dist/styles/height.js +0 -20
- package/dist/styles/height.js.map +0 -1
- package/dist/styles/index.d.ts +0 -2
- package/dist/styles/index.js +0 -9
- package/dist/styles/index.js.map +0 -1
- package/dist/styles/inset.d.ts +0 -52
- package/dist/styles/inset.js +0 -150
- package/dist/styles/inset.js.map +0 -1
- package/dist/styles/justify.d.ts +0 -15
- package/dist/styles/justify.js +0 -14
- package/dist/styles/justify.js.map +0 -1
- package/dist/styles/list.d.ts +0 -16
- package/dist/styles/list.js +0 -98
- package/dist/styles/list.js.map +0 -1
- package/dist/styles/margin.d.ts +0 -24
- package/dist/styles/margin.js +0 -104
- package/dist/styles/margin.js.map +0 -1
- package/dist/styles/outline.d.ts +0 -29
- package/dist/styles/outline.js +0 -65
- package/dist/styles/outline.js.map +0 -1
- package/dist/styles/padding.d.ts +0 -24
- package/dist/styles/padding.js +0 -104
- package/dist/styles/padding.js.map +0 -1
- package/dist/styles/predefined.d.ts +0 -73
- package/dist/styles/predefined.js +0 -241
- package/dist/styles/predefined.js.map +0 -1
- package/dist/styles/preset.d.ts +0 -47
- package/dist/styles/preset.js +0 -126
- package/dist/styles/preset.js.map +0 -1
- package/dist/styles/radius.d.ts +0 -14
- package/dist/styles/radius.js +0 -51
- package/dist/styles/radius.js.map +0 -1
- package/dist/styles/scrollbar.d.ts +0 -23
- package/dist/styles/scrollbar.js +0 -34
- package/dist/styles/scrollbar.js.map +0 -1
- package/dist/styles/shadow.d.ts +0 -14
- package/dist/styles/shadow.js +0 -24
- package/dist/styles/shadow.js.map +0 -1
- package/dist/styles/styledScrollbar.d.ts +0 -47
- package/dist/styles/styledScrollbar.js +0 -38
- package/dist/styles/styledScrollbar.js.map +0 -1
- package/dist/styles/transition.d.ts +0 -14
- package/dist/styles/transition.js +0 -158
- package/dist/styles/transition.js.map +0 -1
- package/dist/styles/types.d.ts +0 -498
- package/dist/styles/width.d.ts +0 -17
- package/dist/styles/width.js +0 -20
- package/dist/styles/width.js.map +0 -1
- package/dist/tasty.d.ts +0 -982
- package/dist/tasty.js +0 -206
- package/dist/tasty.js.map +0 -1
- package/dist/tokens/typography.d.ts +0 -19
- package/dist/tokens/typography.js +0 -237
- package/dist/tokens/typography.js.map +0 -1
- package/dist/types.d.ts +0 -184
- package/dist/utils/cache-wrapper.js +0 -26
- 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/colors.d.ts +0 -5
- package/dist/utils/colors.js +0 -9
- 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/hsl-to-rgb.js +0 -38
- package/dist/utils/hsl-to-rgb.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 -8
- package/dist/utils/mod-attrs.js +0 -21
- package/dist/utils/mod-attrs.js.map +0 -1
- package/dist/utils/okhsl-to-rgb.js +0 -296
- package/dist/utils/okhsl-to-rgb.js.map +0 -1
- package/dist/utils/process-tokens.d.ts +0 -31
- package/dist/utils/process-tokens.js +0 -171
- 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 -147
- 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 -177
- package/dist/utils/styles.js +0 -730
- package/dist/utils/styles.js.map +0 -1
- package/dist/utils/typography.d.ts +0 -36
- package/dist/utils/typography.js +0 -53
- 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 -74
- 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/usage.md +0 -695
package/docs/README.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Tasty Docs
|
|
2
|
+
|
|
3
|
+
Tasty is CSS-in-JS for React design systems. You describe hover, disabled, variants, themes, responsive behavior, and other conditions as state maps; Tasty compiles them so one branch wins without specificity fights or source-order surprises.
|
|
4
|
+
|
|
5
|
+
Use this hub to move from the core idea to a working component, then into design-system patterns or API reference as needed.
|
|
6
|
+
|
|
7
|
+
## Start Here
|
|
8
|
+
|
|
9
|
+
- **Understand why**: [Introduction](../README.md) for the problem, the state-map model, and the product overview.
|
|
10
|
+
- **Try it**: [Getting Started](getting-started.md) to build a stateful component, add shared configuration, and choose a rendering mode.
|
|
11
|
+
- **Build a component system**: [Methodology](methodology.md) for root + sub-elements, typed public APIs, tokens, and extension patterns; then [Building a Design System](design-system.md).
|
|
12
|
+
- **Evaluate or adopt it**: [Comparison](comparison.md) for tool-selection context and [Adoption Guide](adoption.md) for an incremental rollout.
|
|
13
|
+
- **Understand the compiler**: [Style rendering pipeline](pipeline.md) for how state maps become mutually exclusive selectors.
|
|
14
|
+
|
|
15
|
+
## By Role
|
|
16
|
+
|
|
17
|
+
- **Application developer using an existing design system**: [Getting Started](getting-started.md), then [React API](react-api.md).
|
|
18
|
+
- **Design-system author**: [Methodology](methodology.md), [Building a Design System](design-system.md), [Configuration](configuration.md), and [Adoption Guide](adoption.md).
|
|
19
|
+
- **Platform or tooling engineer**: [Configuration](configuration.md), [Build-Time Extraction (`tastyStatic`)](tasty-static.md), [Server-Side Rendering](ssr.md), and [Debug Utilities](debug.md).
|
|
20
|
+
|
|
21
|
+
## By Styling Approach
|
|
22
|
+
|
|
23
|
+
- **React components**: [React API](react-api.md)
|
|
24
|
+
- **Zero-client-runtime React with `tasty()`**: [Server-Side Rendering](ssr.md), especially the static Astro integration
|
|
25
|
+
- **Build-time extraction with `tastyStatic()`**: [Build-Time Extraction](tasty-static.md)
|
|
26
|
+
- **Hydrated `tasty()` with server collection**: [Server-Side Rendering](ssr.md)
|
|
27
|
+
- **Upgrading from v2**: [Migration Guide (v2 → v3)](migration-v3.md)
|
|
28
|
+
|
|
29
|
+
## By Task
|
|
30
|
+
|
|
31
|
+
- **Learn the style language**: [Style DSL](dsl.md)
|
|
32
|
+
- **Brief an AI agent (or yourself) on writing correct styles**: [Style Rules for AI Agents](ai-agents.md)
|
|
33
|
+
- **Look up a property handler**: [Style Properties](styles.md)
|
|
34
|
+
- **Define tokens, units, recipes, keyframes, properties, functions, or polyfills globally**: [Configuration](configuration.md)
|
|
35
|
+
- **Extend Tasty with a plugin, custom handler, or custom prop**: [Plugins & Extension Points](plugins.md)
|
|
36
|
+
- **Debug generated CSS or cache behavior**: [Debug Utilities](debug.md)
|
|
37
|
+
- **Understand how selector generation works internally**: [Style rendering pipeline](pipeline.md)
|
|
38
|
+
- **Understand runtime injection internals**: [Style Injector](injector.md)
|
|
39
|
+
- **Reproduce and interpret performance measurements**: [Runtime Benchmarks](runtime-benchmarks.md)
|
package/docs/adoption.md
ADDED
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
# Adoption Guide
|
|
2
|
+
|
|
3
|
+
Tasty is most useful where reusable component styling has become a state-resolution problem: hover intersects with disabled, variants intersect with themes, and every extension makes selector behavior harder to predict.
|
|
4
|
+
|
|
5
|
+
It normally sits underneath a design system’s existing components. You can preserve their public React APIs while moving their style logic into state maps that produce one declared winner. Adoption should start with a small, state-heavy component—not an all-at-once rewrite.
|
|
6
|
+
|
|
7
|
+
This guide is for design-system maintainers and platform engineers evaluating Tasty or introducing it into an existing codebase. Use it for rollout strategy and adoption sequencing; use the [Comparison guide](comparison.md) when the open question is whether Tasty is the right tool.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Where Tasty sits in the stack
|
|
12
|
+
|
|
13
|
+
Tasty is not the surface your product engineers interact with directly. It sits one layer below:
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
Product code
|
|
17
|
+
└─ DS components (Button, Card, Layout, ...)
|
|
18
|
+
└─ Tasty engine (tasty(), configure(), style functions)
|
|
19
|
+
└─ CSS (mutually exclusive selectors, tokens, custom properties)
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**What Tasty owns:**
|
|
23
|
+
|
|
24
|
+
- The DSL and value parser (tokens, custom units, auto-calc, color opacity)
|
|
25
|
+
- State compilation (mutually exclusive selectors from state maps)
|
|
26
|
+
- Style injection and deduplication (runtime or build-time)
|
|
27
|
+
- The `tasty()` component factory and hooks API
|
|
28
|
+
|
|
29
|
+
**What the DS team owns:**
|
|
30
|
+
|
|
31
|
+
- Token names and values (colors, spacing, typography)
|
|
32
|
+
- Custom units and their semantics
|
|
33
|
+
- State aliases (`@mobile`, `@dark`, `@compact`)
|
|
34
|
+
- Recipes (reusable style bundles)
|
|
35
|
+
- Which style props each component exposes
|
|
36
|
+
- Sub-element structure for compound components
|
|
37
|
+
- The override and extension rules product teams follow
|
|
38
|
+
|
|
39
|
+
Two teams using Tasty can end up with very different authoring models. That is by design.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Who should adopt Tasty
|
|
44
|
+
|
|
45
|
+
**Strong fit:**
|
|
46
|
+
|
|
47
|
+
- A design-system or platform team that owns shared component APIs
|
|
48
|
+
- Components with complex, intersecting states (hover + disabled + theme + breakpoint)
|
|
49
|
+
- Teams that need deterministic style resolution without cascade/specificity bugs
|
|
50
|
+
- Organizations that want tokens, states, and extension rules to stay consistent across products
|
|
51
|
+
|
|
52
|
+
**Not the right fit:**
|
|
53
|
+
|
|
54
|
+
- Solo developers building a one-off app with minimal UI structure
|
|
55
|
+
- Teams that want a shared utility vocabulary and direct markup authoring (Tailwind is better here)
|
|
56
|
+
- Projects where low ceremony matters more than central governance
|
|
57
|
+
- Codebases where intersecting state complexity is low
|
|
58
|
+
|
|
59
|
+
For a detailed comparison with Tailwind, Panda CSS, vanilla-extract, StyleX, Stitches, and Emotion, see the [Comparison guide](comparison.md).
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## What you are expected to define
|
|
64
|
+
|
|
65
|
+
Tasty provides the engine. The DS team defines the language that runs on it. Here is what that typically involves:
|
|
66
|
+
|
|
67
|
+
| Layer | What you define | Where |
|
|
68
|
+
| ------------------ | ---------------------------------------------------------- | ------------------------------------------ |
|
|
69
|
+
| **Tokens** | Color names, spacing scale, border widths, radii | `configure({ tokens })` |
|
|
70
|
+
| **Units** | Custom multiplier units (`x`, `r`, `bw`, or your own) | `configure({ units })` |
|
|
71
|
+
| **State aliases** | Responsive breakpoints, theme modes, feature flags | `configure({ states })` |
|
|
72
|
+
| **Recipes** | Reusable style bundles (card, elevated, input-reset) | `configure({ recipes })` |
|
|
73
|
+
| **Typography** | Preset definitions (h1-h6, t1-t4, etc.) | `configure({ presets: { ... } })` |
|
|
74
|
+
| **Style props** | Which CSS properties each component exposes as React props | `styleProps` in each component |
|
|
75
|
+
| **Sub-elements** | Inner parts of compound components (Title, Icon, Content) | `elements` + capitalized keys in `styles` |
|
|
76
|
+
| **Override rules** | How product engineers extend or constrain components | Styled wrappers via `tasty(Base, { ... })` |
|
|
77
|
+
|
|
78
|
+
The same engine can power a minimal design system with a handful of tokens:
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
configure({
|
|
82
|
+
tokens: { '#bg': '#white', '#text': '#111' },
|
|
83
|
+
states: { '@dark': '@root(schema=dark)' },
|
|
84
|
+
});
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
...or an enterprise-scale system with dozens of tokens, multiple state aliases, typography presets, recipes, and custom units. The scope is yours to decide.
|
|
88
|
+
|
|
89
|
+
Here is how the layers connect end-to-end. The DS team configures the engine, defines components, and product engineers consume them:
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
// ds/config.ts — DS team defines the language
|
|
93
|
+
configure({
|
|
94
|
+
tokens: {
|
|
95
|
+
'#primary': 'oklch(55% 0.25 265)',
|
|
96
|
+
'#surface': '#fff',
|
|
97
|
+
'#text': '#111',
|
|
98
|
+
},
|
|
99
|
+
states: { '@mobile': '@media(w < 768px)', '@dark': '@root(schema=dark)' },
|
|
100
|
+
recipes: {
|
|
101
|
+
card: { padding: '4x', fill: '#surface', radius: '1r', border: true },
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// ds/components/Card.tsx — DS team builds components on top
|
|
106
|
+
const Card = tasty({
|
|
107
|
+
styles: {
|
|
108
|
+
recipe: 'card',
|
|
109
|
+
Title: { preset: 'h3', color: '#primary' },
|
|
110
|
+
Body: { preset: 't2', color: '#text' },
|
|
111
|
+
},
|
|
112
|
+
elements: { Title: 'h2', Body: 'div' },
|
|
113
|
+
styleProps: ['padding', 'fill'],
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// app/Dashboard.tsx — product engineer uses the component
|
|
117
|
+
<Card padding={{ '': '4x', '@mobile': '2x' }}>
|
|
118
|
+
<Card.Title>Monthly Revenue</Card.Title>
|
|
119
|
+
<Card.Body>$1.2M — up 12% from last month</Card.Body>
|
|
120
|
+
</Card>;
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
See [Configuration](configuration.md) for the full `configure()` API.
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Incremental adoption
|
|
128
|
+
|
|
129
|
+
You do not need to adopt everything at once. Tasty is designed to be introduced layer by layer.
|
|
130
|
+
|
|
131
|
+
A practical first slice is one component that already suffers from intersecting state and variant logic: a button, input, menu item, disclosure, or interactive card. Keep its public API unchanged, move one or two properties into Tasty state maps, and verify that the declared priority matches the behavior your team expects.
|
|
132
|
+
|
|
133
|
+
This proves the differentiator before you invest in a complete token vocabulary or migrate an entire component library.
|
|
134
|
+
|
|
135
|
+
### Phase 1 -- Pilot one state-heavy component
|
|
136
|
+
|
|
137
|
+
Choose a component with a known hover/disabled, focus/error, or variant/theme interaction. Preserve its external props and visual behavior. The pilot should answer three questions:
|
|
138
|
+
|
|
139
|
+
1. Is the state map easier to review than the existing selectors or conditional class logic?
|
|
140
|
+
2. Does extending the component keep the intended state priority?
|
|
141
|
+
3. Can your current tokens and component API move over without product-code churn?
|
|
142
|
+
|
|
143
|
+
If the answer is yes, establish the shared design-system vocabulary around the successful slice.
|
|
144
|
+
|
|
145
|
+
### Phase 2 -- Tokens and units
|
|
146
|
+
|
|
147
|
+
Start by defining your design tokens and custom units. This is the lowest-risk step: it only configures the parser and does not require rewriting any components.
|
|
148
|
+
|
|
149
|
+
```tsx
|
|
150
|
+
import { configure } from '@tenphi/tasty';
|
|
151
|
+
|
|
152
|
+
configure({
|
|
153
|
+
tokens: {
|
|
154
|
+
'#primary': 'oklch(55% 0.25 265)',
|
|
155
|
+
'#surface': '#white',
|
|
156
|
+
'#text': '#111',
|
|
157
|
+
'$card-padding': '4x',
|
|
158
|
+
},
|
|
159
|
+
// Common units (x, r, bw, ow, cr) are built-in.
|
|
160
|
+
// A DS typically redefines them to use CSS custom properties
|
|
161
|
+
// so that the actual scale is controlled via CSS, not JS:
|
|
162
|
+
units: {
|
|
163
|
+
x: 'var(--gap)', // 2x → calc(var(--gap) * 2)
|
|
164
|
+
r: 'var(--radius)',
|
|
165
|
+
bw: 'var(--border-width)',
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Phase 3 -- State aliases and recipes
|
|
171
|
+
|
|
172
|
+
Define the state vocabulary your components will share. This is where you start encoding your team's conventions.
|
|
173
|
+
|
|
174
|
+
```tsx
|
|
175
|
+
configure({
|
|
176
|
+
states: {
|
|
177
|
+
'@mobile': '@media(w < 768px)',
|
|
178
|
+
'@tablet': '@media(w < 1024px)',
|
|
179
|
+
'@dark':
|
|
180
|
+
'@root(schema=dark) | (!@root(schema) & @media(prefers-color-scheme: dark))',
|
|
181
|
+
},
|
|
182
|
+
recipes: {
|
|
183
|
+
card: { padding: '4x', fill: '#surface', radius: '1r', border: true },
|
|
184
|
+
elevated: { shadow: '0 2x 4x #shadow' },
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Phase 4 -- Migrate a few primitives
|
|
190
|
+
|
|
191
|
+
Pick 2-3 widely used primitives (Box, Text, Button) and rewrite them with `tasty()`. Keep the public API identical so product code does not need to change.
|
|
192
|
+
|
|
193
|
+
```tsx
|
|
194
|
+
const Box = tasty({
|
|
195
|
+
as: 'div',
|
|
196
|
+
styles: {
|
|
197
|
+
display: 'flex',
|
|
198
|
+
flow: 'column',
|
|
199
|
+
gap: '1x',
|
|
200
|
+
},
|
|
201
|
+
styleProps: ['gap', 'flow', 'padding', 'fill'],
|
|
202
|
+
});
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
At this point you can validate the DSL, token workflow, and component authoring experience before expanding the rollout.
|
|
206
|
+
|
|
207
|
+
### Phase 5 -- Expand state resolution
|
|
208
|
+
|
|
209
|
+
Move the components with the most painful intersecting states (buttons with hover + disabled + theme variants, inputs with focus + error + readonly) to Tasty's state map syntax. This is where mutually exclusive selectors start paying off.
|
|
210
|
+
|
|
211
|
+
```tsx
|
|
212
|
+
const Button = tasty({
|
|
213
|
+
as: 'button',
|
|
214
|
+
styles: {
|
|
215
|
+
fill: {
|
|
216
|
+
'': '#primary',
|
|
217
|
+
':hover': '#primary-hover',
|
|
218
|
+
':active': '#primary-pressed',
|
|
219
|
+
// `disabled` is a data-attribute modifier → [data-disabled].
|
|
220
|
+
// Tasty auto-applies it from the native `disabled` attribute.
|
|
221
|
+
// `[disabled]` (attribute selector) also works here.
|
|
222
|
+
disabled: '#surface',
|
|
223
|
+
},
|
|
224
|
+
color: {
|
|
225
|
+
'': '#on-primary',
|
|
226
|
+
disabled: '#text.40',
|
|
227
|
+
},
|
|
228
|
+
cursor: {
|
|
229
|
+
'': 'pointer',
|
|
230
|
+
disabled: 'not-allowed',
|
|
231
|
+
},
|
|
232
|
+
transition: 'theme',
|
|
233
|
+
},
|
|
234
|
+
});
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Phase 6 -- Standardize style props and sub-elements
|
|
238
|
+
|
|
239
|
+
Define which style props each component category exposes. Layout components get flow/gap/padding. Interactive components get positioning. Compound components declare sub-elements.
|
|
240
|
+
|
|
241
|
+
```tsx
|
|
242
|
+
const Card = tasty({
|
|
243
|
+
styles: {
|
|
244
|
+
recipe: 'card elevated',
|
|
245
|
+
Title: { preset: 'h3', color: '#primary' },
|
|
246
|
+
Content: { color: '#text', preset: 't2' },
|
|
247
|
+
},
|
|
248
|
+
elements: { Title: 'h2', Content: 'div' },
|
|
249
|
+
styleProps: ['padding', 'fill', 'radius'],
|
|
250
|
+
});
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### Phase 7 -- Expand to full DS coverage
|
|
254
|
+
|
|
255
|
+
Migrate the remaining components, add the [ESLint plugin](https://github.com/tenphi/eslint-plugin-tasty) to enforce style conventions at lint time, and choose between [server-only `tasty()`](ssr.md) and [`tastyStatic()` build-time extraction](tasty-static.md) when a page should ship no Tasty styling runtime.
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## What changes for product engineers
|
|
260
|
+
|
|
261
|
+
When a DS is powered by Tasty, product engineers typically interact with **components, not Tasty itself**. Here is what changes from their perspective:
|
|
262
|
+
|
|
263
|
+
**They do not write CSS directly.** Styling decisions are embedded in the components the DS provides. Product code consumes components, tokens, and style props.
|
|
264
|
+
|
|
265
|
+
**Overrides use styled wrappers.** Instead of passing one-off `className` or `style` props, product engineers extend components:
|
|
266
|
+
|
|
267
|
+
```tsx
|
|
268
|
+
import { tasty } from '@tenphi/tasty';
|
|
269
|
+
import { Button } from 'my-ds';
|
|
270
|
+
|
|
271
|
+
// Replace mode: providing '' (default) key replaces the parent's fill entirely
|
|
272
|
+
const DangerButton = tasty(Button, {
|
|
273
|
+
styles: {
|
|
274
|
+
fill: { '': '#danger', ':hover': '#danger-hover' },
|
|
275
|
+
},
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
// Extend mode: omitting '' key preserves parent states and adds/overrides
|
|
279
|
+
const LoadingButton = tasty(Button, {
|
|
280
|
+
styles: {
|
|
281
|
+
fill: {
|
|
282
|
+
loading: '#yellow', // new state appended
|
|
283
|
+
disabled: '#gray.20', // existing state overridden in place
|
|
284
|
+
},
|
|
285
|
+
},
|
|
286
|
+
});
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
**Style props replace raw CSS.** Layout, spacing, and positioning are controlled through typed props on the components that expose them:
|
|
290
|
+
|
|
291
|
+
```tsx
|
|
292
|
+
<Space flow="row" gap="2x" placeItems="center">
|
|
293
|
+
<Title>Dashboard</Title>
|
|
294
|
+
<Button placeSelf="end">Add Item</Button>
|
|
295
|
+
</Space>
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
**Components are server components by default.** All `tasty()` components and style functions are hook-free, so they work as React Server Components without `'use client'`. In server-only contexts they ship no Tasty styling runtime. Astro without `client:*` directives ships no client JavaScript; server-only Next.js RSC follows the same Tasty architecture, while final output depends on the application deployment. Product engineers only add `'use client'` when their component needs actual React interactivity (state, effects, event handlers), never because of styling.
|
|
299
|
+
|
|
300
|
+
**No cascade/specificity concerns.** Tasty's mutually exclusive selectors mean extending a component cannot accidentally break another. Import order, class name collisions, and specificity arithmetic are non-issues.
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
## Migration and Interop Notes
|
|
305
|
+
|
|
306
|
+
- **From Tailwind**: keep utility-authored product surfaces as-is at first, and use Tasty to build or replace the design-system primitives underneath them instead of forcing an all-at-once markup rewrite.
|
|
307
|
+
- **From CSS-in-JS libraries**: start with shared primitives such as `Box`, `Text`, and `Button`, preserve the external component API, and move state logic into Tasty state maps so behavior changes without product-code churn.
|
|
308
|
+
- **From CSS Modules or plain CSS**: migrate token definitions and repeated patterns first, then wrap existing DOM structure with `tasty()` components gradually rather than converting every stylesheet at once.
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
## Learn more
|
|
313
|
+
|
|
314
|
+
- [README](../README.md) -- overview, quick start, and feature highlights
|
|
315
|
+
- [Getting Started](getting-started.md) -- installation, first component, tooling setup
|
|
316
|
+
- [Methodology](methodology.md) -- the recommended patterns for structuring Tasty components
|
|
317
|
+
- [Building a Design System](design-system.md) -- practical guide to building a DS layer with Tasty
|
|
318
|
+
- [Style DSL](dsl.md) -- state maps, tokens, units, extending semantics, keyframes, @property
|
|
319
|
+
- [React API](react-api.md) -- `tasty()` factory, component props, variants, sub-elements, style functions
|
|
320
|
+
- [Configuration](configuration.md) -- tokens, recipes, custom units, style handlers, and TypeScript extensions
|
|
321
|
+
- [Style Properties](styles.md) -- complete reference for all enhanced style properties
|
|
322
|
+
- [Comparison](comparison.md) -- positioning and trade-offs vs. other styling systems
|
|
323
|
+
- [Build-Time Extraction (`tastyStatic`)](tasty-static.md) -- static styling with the Babel plugin
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
# Tasty Style Rules for AI Agents
|
|
2
|
+
|
|
3
|
+
A compact ruleset for **writing correct `@tenphi/tasty` styles**. It is not an API tour — most rules here are machine-checked by [`@tenphi/eslint-plugin-tasty`](https://www.npmjs.com/package/@tenphi/eslint-plugin-tasty), so following it generally means clean lint output. The plugin ships separately and can lag a new DSL rule, so treat this page as the source of truth and the plugin as the enforcement that catches most of it. For explanations and the complete API, see [Style DSL](dsl.md), [Style Properties](styles.md), [React API](react-api.md).
|
|
4
|
+
|
|
5
|
+
Notation: ❌ wrong → ✅ correct.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 0. Read the project config first
|
|
10
|
+
|
|
11
|
+
Color tokens (`#name`), custom properties (`$name`), `preset` names, `recipe` names, state aliases (`@mobile`) and extra units are **project-defined** in `tasty.config.ts` or a `configure({ … })` call. Look them up before writing styles. Never invent a name — reuse an existing one, or add it to the config.
|
|
12
|
+
|
|
13
|
+
## 1. Where styles go
|
|
14
|
+
|
|
15
|
+
```jsx
|
|
16
|
+
const Card = tasty({ as: 'div', styles: { … }, styleProps: ['padding'] }); // new component
|
|
17
|
+
const Hero = tasty(Card, { styles: { … } }); // extend a component
|
|
18
|
+
const cls = useStyles({ … }); // ad-hoc class name
|
|
19
|
+
tastyStatic('.card', { … }); // build-time extraction
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
- Wrap, don't pass styles per instance: ❌ `<Box styles={{ padding: '2x' }} />` → ✅ `const Box = tasty({ styles: { padding: '2x' } })`.
|
|
23
|
+
- Style values are **static**. Route anything conditional through a state map plus `mods` (or `tokens` / `styleProps`):
|
|
24
|
+
❌ `fill: isOpen ? '#primary' : '#surface'` → ✅ `fill: { '': '#surface', open: '#primary' }` with `mods={{ open: isOpen }}`.
|
|
25
|
+
- Keep JavaScript identifiers, calls, computed keys, and interpolated templates
|
|
26
|
+
out of style values. Use token references inside strings for values supplied
|
|
27
|
+
at runtime. Spell out style properties rather than spreading objects; if a
|
|
28
|
+
runtime spread is unavoidable, add a
|
|
29
|
+
`// eslint-disable-next-line tasty/no-style-spread -- reason` comment directly
|
|
30
|
+
above it. `tastyStatic()` cannot use spreads.
|
|
31
|
+
|
|
32
|
+
## 2. Property names
|
|
33
|
+
|
|
34
|
+
Keys are camelCase — a Tasty property or a real CSS property. No kebab-case, no invented names.
|
|
35
|
+
|
|
36
|
+
Prefer the Tasty shorthand over its CSS equivalents:
|
|
37
|
+
|
|
38
|
+
| Use | Instead of |
|
|
39
|
+
| ---------------------------------------- | ------------------------------------------------------------------------------------- |
|
|
40
|
+
| `fill` | `backgroundColor`, `background` |
|
|
41
|
+
| `image` | `backgroundImage` |
|
|
42
|
+
| `border` | `borderColor`, `borderWidth`, `borderStyle`, `borderTop`/`Right`/`Bottom`/`Left` |
|
|
43
|
+
| `radius` | `borderRadius` |
|
|
44
|
+
| `outline` | `outlineColor`, `outlineWidth`, `outlineStyle`, `outlineOffset` |
|
|
45
|
+
| `shadow` | `boxShadow` |
|
|
46
|
+
| `padding` | `paddingTop`/`Right`/`Bottom`/`Left` |
|
|
47
|
+
| `margin` | `marginTop`/`Right`/`Bottom`/`Left` |
|
|
48
|
+
| `inset` | `top`, `right`, `bottom`, `left` |
|
|
49
|
+
| `width` / `height` | `minWidth`/`maxWidth`, `minHeight`/`maxHeight` |
|
|
50
|
+
| `flow` | `flexDirection`, `flexWrap`, `flexFlow`, `gridAutoFlow` |
|
|
51
|
+
| `preset` | `fontSize`, `lineHeight`, `letterSpacing`, `fontWeight`, `fontStyle`, `textTransform` |
|
|
52
|
+
| `font` | `fontFamily` |
|
|
53
|
+
| `scrollbar` | `scrollbarWidth`, `scrollbarColor`, `scrollbarGutter` |
|
|
54
|
+
| `gridColumns` / `gridRows` / `gridAreas` | `gridTemplateColumns` / `Rows` / `Areas` |
|
|
55
|
+
| `textOverflow: 'ellipsis / 3'` | `lineClamp` |
|
|
56
|
+
| `hide: true` | `display: 'none'` |
|
|
57
|
+
| `flexGrow` / `flexShrink` / `flexBasis` | `flex` |
|
|
58
|
+
|
|
59
|
+
Use logical category styles when layout must follow writing mode or direction: `blockSize`/`inlineSize`, `blockPadding`/`inlinePadding`, `blockMargin`/`inlineMargin`, `blockInset`/`inlineInset`, `blockScrollMargin`/`inlineScrollMargin`, `blockScrollPadding`/`inlineScrollPadding`, and `blockBorder`/`inlineBorder`. Use `start`/`end` value modifiers; do not replace them with physical sides. Native logical CSS properties remain valid as ordinary keys when an individual declaration is needed.
|
|
60
|
+
|
|
61
|
+
The last row goes the other way — longhands over the shorthand — because `flex` is lossy: it resets the components you omit to non-initial values (`flex: '0'` silently sets `flex-basis: 0%`) and cannot express `flexShrink: 0` at all. The longhands also carry separate state maps.
|
|
62
|
+
|
|
63
|
+
## 3. Values
|
|
64
|
+
|
|
65
|
+
### Tokens
|
|
66
|
+
|
|
67
|
+
| Syntax | Meaning |
|
|
68
|
+
| --------------------- | ------------------------------------------------------------------------- |
|
|
69
|
+
| `#name` | color token → `var(--name-color)` |
|
|
70
|
+
| `#name.50` | token at 50% opacity (`0`–`100`) |
|
|
71
|
+
| `#name.$opacity` | opacity from a custom property |
|
|
72
|
+
| `#clear` / `#current` | `transparent` / `currentcolor` |
|
|
73
|
+
| `$name` | custom property → `var(--name)` |
|
|
74
|
+
| `(#a, #b)` | fallback chain |
|
|
75
|
+
| `$$name` / `##name` | the custom property _name_ (not its value) — inside `transition` |
|
|
76
|
+
| `$$name(…)` | call a CSS `@function` declared via `@function`, `useFunction`, or config |
|
|
77
|
+
|
|
78
|
+
- ❌ `#f5f5f5`, `rgb(0 0 0)`, `oklch(…)`, `okhsl(…)`, `red` → ✅ `#surface` (add the token to the config if it doesn't exist).
|
|
79
|
+
- ❌ `var(--gap)` → ✅ `$gap` · ❌ `$accent-color` → ✅ `#accent` · ❌ `transparent` → ✅ `#clear` · ❌ `currentColor` → ✅ `#current`.
|
|
80
|
+
- A `$name` must be declared as a `'$name': value` key in the same styles object, or in the config.
|
|
81
|
+
- Names are case-sensitive and start lowercase — `$myVar` → `--myVar`; a leading capital folds (`$Foo` → `--foo`). Prefer kebab-case.
|
|
82
|
+
- A `$name` reference is untyped: it fills the first free slot in a shorthand (`border: '1bw $my-style'` → the line style). Write colors as `#name`; `$name-color` is a fallback for pointing at a raw CSS custom property that holds a color.
|
|
83
|
+
- `preset` and `transition` take token _names_, not values, so a `$name` cannot stand in for one (it warns in dev and is ignored).
|
|
84
|
+
|
|
85
|
+
### Units
|
|
86
|
+
|
|
87
|
+
Tasty units: `x` (gap multiple), `r` (radius), `cr` (card radius), `bw` (border width), `ow` (outline width), `sf` (`minmax(0, Nfr)`). All CSS units work too, plus anything in `units` in the config. Any other unit is an error.
|
|
88
|
+
|
|
89
|
+
Prefer units over raw pixels: `8px` → `1x` … `64px` → `8x`; `radius: '6px'` → `'1r'`; `border: '1px …'` → `'1bw …'`.
|
|
90
|
+
|
|
91
|
+
### Math
|
|
92
|
+
|
|
93
|
+
❌ `calc(100% - 2x)` → ✅ `(100% - 2x)`. Parentheses are wrapped in `calc()` automatically — and must be balanced.
|
|
94
|
+
|
|
95
|
+
### `true`
|
|
96
|
+
|
|
97
|
+
`true` means "the design-system default" and is accepted **only** by: `border`, `radius`, `outline`, `shadow`, `padding`, `margin`, `gap`, `inset`, `width`, `height`, `fill`, `color`, `fade`, `preset`, `font`, `scrollbar`, `scrollMargin`, `scrollPadding`, and the enhanced logical category styles listed above, plus `hide`. Anywhere else it is an error.
|
|
98
|
+
|
|
99
|
+
### `!important`
|
|
100
|
+
|
|
101
|
+
Never use it. Tasty owns specificity through doubled selectors and state ordering; `!important` breaks that. Express the exception as a state instead.
|
|
102
|
+
|
|
103
|
+
### Modifiers
|
|
104
|
+
|
|
105
|
+
A value is `[values…] [modifiers…]`, and several groups can be comma-separated (later groups override earlier ones). A group that names **direction** modifiers takes a **single** value, applied to every direction it names — per-side values come from comma groups. A group naming no direction keeps plain CSS shorthand order (1–4 values). (`inset` + `dock` is the one exception: a second value insets the spanned sides.) Only the modifiers a property knows are valid:
|
|
106
|
+
|
|
107
|
+
| Property | Modifiers |
|
|
108
|
+
| ------------------------------------ | ---------------------------------------------------------------------------------------------------------- |
|
|
109
|
+
| `padding`, `margin`, `inset`, `fade` | `top` `right` `bottom` `left` |
|
|
110
|
+
| `border` | the four directions + `solid` `dashed` `dotted` `double` `groove` `ridge` `inset` `outset` `none` `hidden` |
|
|
111
|
+
| logical axis spacing/inset styles | `start` `end` |
|
|
112
|
+
| `blockBorder`, `inlineBorder` | `start` `end` + the border style keywords above |
|
|
113
|
+
| `outline` | the style keywords above |
|
|
114
|
+
| `radius` | `top` `right` `bottom` `left` + shapes `round` `ellipse` `leaf` `backleaf` |
|
|
115
|
+
| `width`, `height`, `blockSize`, `inlineSize` | `min` `max` `fixed` |
|
|
116
|
+
| `flow` | `row` `column` `row-reverse` `column-reverse` `wrap` `nowrap` `dense` |
|
|
117
|
+
| `overflow` | `visible` `hidden` `scroll` `clip` `auto` `overlay` |
|
|
118
|
+
| `position` | `static` `relative` `absolute` `fixed` `sticky` |
|
|
119
|
+
| `shadow` | `inset` |
|
|
120
|
+
| `preset` | `name / strong` (or `bold`) `italic` `icon` `tight` |
|
|
121
|
+
| box properties | `longhand` — emit CSS longhands instead of the shorthand |
|
|
122
|
+
|
|
123
|
+
Directional modifiers beat placeholder zeros:
|
|
124
|
+
|
|
125
|
+
- ❌ `padding: '0 0 2x 0'` → ✅ `padding: '2x bottom'`
|
|
126
|
+
- ❌ `padding: '1x 1x 2x 1x'` → ✅ `padding: '1x, 2x bottom'`
|
|
127
|
+
- ❌ `border: '0 0 1bw 0'` — four tokens parse as _one_ border value, so this renders no border at all → ✅ `border: '1bw bottom'`
|
|
128
|
+
- ❌ `padding: '2x 4x top right'` — a directional group takes one value, so `4x` is dropped → ✅ `padding: '2x top, 4x right'`
|
|
129
|
+
- ❌ `fade: '3x 1x top bottom'` → ✅ `fade: '3x top, 1x bottom'`
|
|
130
|
+
|
|
131
|
+
Value-only properties reject both colors and modifiers: `gap`, `columnGap`, `rowGap`, `opacity`, `zIndex`, `order`, `flexGrow`, `flexShrink`, `flexBasis`, `aspectRatio`, `lineClamp`, and `tabSize`. Logical axis category styles accept one or two values in start/end order and the `start`, `end`, and `longhand` modifiers. `fill` and `color` take a color (plus `none` / `transparent`); `caretColor` and `accentColor` take a color only.
|
|
132
|
+
|
|
133
|
+
### `transition`
|
|
134
|
+
|
|
135
|
+
Use semantic names, not CSS property names: `fade` `fill` `color` `theme` `border` `radius` `shadow` `outline` `preset` `text` `gap` `opacity` `translate` `rotate` `scale` `filter` `image` `background` `width` `height` `zIndex` `inset` `flow` `dimension`.
|
|
136
|
+
|
|
137
|
+
❌ `transition: 'background-color 0.2s'` → ✅ `transition: 'fill'`
|
|
138
|
+
|
|
139
|
+
Omit the duration to use `$fill-transition` with `$transition` as fallback, or
|
|
140
|
+
use a duration token explicitly (`transition: 'fill $transition'`). Avoid raw
|
|
141
|
+
time values in duration slots. An explicit zero can be useful to disable motion.
|
|
142
|
+
|
|
143
|
+
Use `transition` when defining a complete transition, and the native CSS
|
|
144
|
+
`animation` shorthand when defining a complete animation. Tasty accepts
|
|
145
|
+
`animation` directly and tracks local keyframes referenced by it; no special
|
|
146
|
+
animation handler is needed. Use duration tokens in either form. Keep
|
|
147
|
+
`transitionDuration`, `animationDuration`, and other motion longhands when a
|
|
148
|
+
state or extension changes only one part of an existing effect. A shorthand
|
|
149
|
+
resets omitted parts, including an animation's timeline and range.
|
|
150
|
+
|
|
151
|
+
## 4. State maps
|
|
152
|
+
|
|
153
|
+
A property value can be an object of `state: value`. Key order is priority — later keys win.
|
|
154
|
+
|
|
155
|
+
| Key | Generated selector |
|
|
156
|
+
| ------------------------ | ----------------------------------------------- |
|
|
157
|
+
| `hovered` | `[data-hovered]` (boolean modifier from `mods`) |
|
|
158
|
+
| `theme=danger` | `[data-theme="danger"]` (value modifier) |
|
|
159
|
+
| `:hover` | pseudo-class |
|
|
160
|
+
| `.active` | class selector |
|
|
161
|
+
| `[aria-expanded="true"]` | attribute selector |
|
|
162
|
+
| `hovered & .active` | AND |
|
|
163
|
+
| `hovered \| focused` | OR (`,` also means OR) |
|
|
164
|
+
| `!disabled` | NOT |
|
|
165
|
+
| `hovered ^ focused` | XOR — exactly one (keep chains ≤ 4 operands) |
|
|
166
|
+
|
|
167
|
+
Precedence `!` > `^` > `|` > `&`; use parentheses to override.
|
|
168
|
+
|
|
169
|
+
Rules:
|
|
170
|
+
|
|
171
|
+
1. **`''` comes first.** The bare default is the lowest-priority state; placing it later would override everything above it.
|
|
172
|
+
2. **Every state map needs `''` or `_`** — except when extending (`tasty(Parent, …)`), where omitting `''` merges into the parent's states and including `''` replaces them wholesale.
|
|
173
|
+
3. **`_` is standalone-only** and always first (with `''` right after it, if present). `_` is a never-negated fallback floor for cases where a higher-priority branch may be _unknown_ (`@supports`, container queries). If a map contains only `_` and `''`, drop the `''`.
|
|
174
|
+
4. **No nested maps:** ❌ `{ hovered: { pressed: 'x' } }` → ✅ `{ 'hovered & pressed': 'x' }`
|
|
175
|
+
5. **State keys never sit at the top level** of a styles object — `:hover`, `.active`, `[open]` belong inside a property value.
|
|
176
|
+
6. **Write keys Tasty can fully read.** Names start with a letter (a single letter is fine: `x` → `[data-x]`); join states with an operator (`hovered & focused`, not `hovered focused`); no space between `@media` and `(`. Tasty warns (`INVALID_STATE_KEY`) and skips what it cannot read.
|
|
177
|
+
7. **Prefer automatic native states:** use `disabled` and `checked` in Tasty component styles instead of `[disabled]` and `[checked]`. The concise states track the corresponding native prop or attribute automatically.
|
|
178
|
+
|
|
179
|
+
```jsx
|
|
180
|
+
color: { '': '#text', hovered: '#accent', disabled: '#text.40' }
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Advanced states:
|
|
184
|
+
|
|
185
|
+
| Prefix | Use | Example |
|
|
186
|
+
| -------------- | -------------------------------------------------------------------------------- | --------------------------------------------------- |
|
|
187
|
+
| `@media(…)` | media query; dimensions `w` `h`; types `@media:print` `:screen` `:all` `:speech` | `@media(w < 768px)`, `@media(600px <= w < 1200px)` |
|
|
188
|
+
| `@(…)` | container query; dimensions `w` `h` `is` `bs` | `@(layout, w >= 600px)`, `@($variant=primary)` |
|
|
189
|
+
| `@supports(…)` | feature query; `$` first argument tests a selector | `@supports(display: grid)`, `@supports($, :has(*))` |
|
|
190
|
+
| `@root(…)` | condition on `:root` | `@root(schema=dark)` |
|
|
191
|
+
| `@parent(…)` | condition on an ancestor; `, >` for the direct parent | `@parent(hovered, >)` |
|
|
192
|
+
| `@own(…)` | a sub-element's own state — **only inside sub-element styles** | `@own(:hover)` |
|
|
193
|
+
| `@starting` | `@starting-style` entry animation | `@starting` |
|
|
194
|
+
| `@name` | project state alias | `@mobile` |
|
|
195
|
+
|
|
196
|
+
- At root level write the selector directly: ❌ `'@own(:hover)'` → ✅ `':hover'`.
|
|
197
|
+
- `@name` aliases must exist in `states` in the config or be declared locally as an `'@name': '<state expression>'` key; alias keys start with `@` and their value must be a valid state expression.
|
|
198
|
+
- `:is()` / `:has()` / `:not()` / `:where()` work in state keys but support at most 2 levels of nested parentheses, and `:has()` is expensive — prefer `@parent()`, `@own()` and modifiers.
|
|
199
|
+
|
|
200
|
+
When extending a parent's state map: `'@inherit'` reuses the parent's value for that state, `null` removes a state (or resets a property, letting recipes fill in), `false` is a tombstone that blocks it entirely.
|
|
201
|
+
|
|
202
|
+
## 5. Sub-elements
|
|
203
|
+
|
|
204
|
+
A **capitalized** key targets `[data-element="Name"]`, and its value must be a style object.
|
|
205
|
+
|
|
206
|
+
```jsx
|
|
207
|
+
styles: { Title: { preset: 'h3' }, Icon: { $: '>@:last-child', color: '#accent' } }
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
❌ nested-selector keys (`'& .title'`, `'&:hover'`) → ✅ sub-elements and state maps. Use the `$` affix property inside a sub-element to control how its selector attaches (`>` direct child, `@` placeholder for the element itself, `&::before` for a root pseudo-element).
|
|
211
|
+
|
|
212
|
+
## 6. Special top-level keys
|
|
213
|
+
|
|
214
|
+
| Key | Shape |
|
|
215
|
+
| ---------------- | ----------------------------------------------------------------------------------------------------------- |
|
|
216
|
+
| `@keyframes` | `{ name: { '0%': styles, … } }` |
|
|
217
|
+
| `@property` | `{ '$name': { syntax, inherits, initialValue } }` |
|
|
218
|
+
| `@font-face` | `{ 'Family Name': descriptors \| descriptors[] }` |
|
|
219
|
+
| `@counter-style` | `{ name: descriptors }` |
|
|
220
|
+
| `@function` | `{ '$$name': { args, returns?, result, '$local'? } }` |
|
|
221
|
+
| `recipe` | a **string** of configured recipe names: `'card elevated'`, `'reset input / autofill'`, `'none / disabled'` |
|
|
222
|
+
|
|
223
|
+
`recipe` resolves on the component root and independently in each sub-element's styles: `styles: { Panel: { recipe: 'card elevated' } }` is supported. Configured recipe definitions must be flat and cannot contain their own `recipe` key; references inside recipe values are not recursively expanded. Compose names where the recipe is applied, not inside `configure({ recipes })`. See [Recipes](dsl.md#recipes).
|
|
224
|
+
|
|
225
|
+
At-rule keys match the real CSS at-rule names, so they are kebab-case, not camelCase. Inside `@function`, the callable is `$$name` but its parameters and local variables are `$name`:
|
|
226
|
+
|
|
227
|
+
```jsx
|
|
228
|
+
styles: {
|
|
229
|
+
'@function': { $$negative: { args: ['$value'], result: '(-1 * $value)' } },
|
|
230
|
+
margin: '$$negative(2x) top',
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## 7. `tastyStatic()`
|
|
235
|
+
|
|
236
|
+
The selector must be a string literal and valid CSS. Values must be static — strings, numbers, booleans, `null`, or objects/arrays of those. No variables, template literals, function calls, or spreads.
|
|
237
|
+
|
|
238
|
+
## 8. Checklist
|
|
239
|
+
|
|
240
|
+
- Token, preset, recipe, unit and `@alias` names exist in the project config.
|
|
241
|
+
- Tasty shorthand chosen over CSS longhands; `flexGrow`/`flexShrink`/`flexBasis` over `flex`; `hide: true` over `display: 'none'`.
|
|
242
|
+
- Logical category styles keep native `block`/`inline` declarations and accept `start`/`end`, never physical direction modifiers.
|
|
243
|
+
- Colors are `#tokens`, not hex/rgb/oklch/named; `$prop` not `var(--prop)`.
|
|
244
|
+
- Spacing uses `x`/`r`/`bw`/`ow` units; math uses `(…)`, not `calc(…)`.
|
|
245
|
+
- `true` only on the properties that accept it; no `!important`.
|
|
246
|
+
- Modifiers valid for the property; directional shorthand instead of placeholder zeros.
|
|
247
|
+
- Every state map starts with `''` (or `_`), is flat, and lives inside a property value.
|
|
248
|
+
- `@own()` only inside sub-elements; sub-element keys are capitalized and hold objects.
|
|
249
|
+
- At-rule keys are kebab-case (`@property`, `@font-face`, `@counter-style`, `@function`); `@function` names use `$$`, their args and locals use `$`.
|
|
250
|
+
- Values are static; dynamic behavior comes from `mods` / `tokens` / `styleProps`.
|