@tenphi/tasty 3.10.0 → 3.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +403 -380
- package/dist/{babel-types-C_7kRP38.d.ts → babel-types-BKDTT4--.d.ts} +2 -2
- package/dist/chunks/async-storage-f9j9JRtq.js +37 -0
- package/dist/chunks/async-storage-f9j9JRtq.js.map +1 -0
- package/dist/chunks/{build-config-l2Ys-AJr.js → build-config-5d8WlQbq.js} +5 -5
- package/dist/chunks/{build-config-l2Ys-AJr.js.map → build-config-5d8WlQbq.js.map} +1 -1
- package/dist/chunks/{collector-CaNtsRtn.js → collector-CjCak4iE.js} +4 -4
- package/dist/chunks/{collector-CaNtsRtn.js.map → collector-CjCak4iE.js.map} +1 -1
- package/dist/chunks/{config-engine-CpExZO_T.js → config-engine-uOFYZidB.js} +4 -4
- package/dist/chunks/{config-engine-CpExZO_T.js.map → config-engine-uOFYZidB.js.map} +1 -1
- package/dist/chunks/config-file-DlZXJMj8.js +25 -0
- package/dist/chunks/config-file-DlZXJMj8.js.map +1 -0
- package/dist/chunks/{css-definitions-DAIJxzFY.js → css-definitions-ubrHP97m.js} +88 -77
- package/dist/chunks/css-definitions-ubrHP97m.js.map +1 -0
- package/dist/chunks/{debug-CKy49GEr.js → debug-CJ31U_WE.js} +6 -6
- package/dist/chunks/{debug-CKy49GEr.js.map → debug-CJ31U_WE.js.map} +1 -1
- package/dist/chunks/{dsl-DRhFWQzo.js → dsl-DYSJc1-T.js} +11 -6
- package/dist/chunks/dsl-DYSJc1-T.js.map +1 -0
- package/dist/chunks/{react-runtime-oi6i3ELR.js → react-runtime-CyyCI02N.js} +168 -61
- package/dist/chunks/react-runtime-CyyCI02N.js.map +1 -0
- package/dist/chunks/{runtime-engine-DoULyhPt.js → runtime-engine-BQmOu5-W.js} +53 -36
- package/dist/chunks/runtime-engine-BQmOu5-W.js.map +1 -0
- package/dist/chunks/{runtime-state-CZ2J5QiZ.js → runtime-state-DEsyUMU-.js} +14 -3
- package/dist/chunks/runtime-state-DEsyUMU-.js.map +1 -0
- package/dist/chunks/{style-engine-CeKsjtIr.js → style-engine-BX2LshDZ.js} +116 -56
- package/dist/chunks/style-engine-BX2LshDZ.js.map +1 -0
- package/dist/chunks/{zero-engine-fxKu8Jbn.js → zero-engine-Dw0P4yGM.js} +22 -7
- package/dist/chunks/zero-engine-Dw0P4yGM.js.map +1 -0
- package/dist/{collector-w4mBj5gd.d.ts → collector-CTU-nl0g.d.ts} +2 -2
- package/dist/{config-CrZOfNJ5.d.ts → config-CcxqSZI2.d.ts} +28 -9
- package/dist/core/index.d.ts +4 -4
- package/dist/core/index.js +7 -7
- package/dist/hydrate-CRyjjAyl.d.ts +22 -0
- package/dist/{index-CYJtAI9a.d.ts → index--GdqCPlp.d.ts} +58 -16
- package/dist/index.d.ts +4 -4
- package/dist/index.js +8 -8
- package/dist/{merge-styles-VXwJ0Frr.d.ts → merge-styles-DSkVc6CD.d.ts} +2 -2
- package/dist/ssr/astro.js +4 -4
- package/dist/ssr/client.d.ts +2 -0
- package/dist/ssr/client.js +2 -0
- package/dist/ssr/index.d.ts +3 -22
- package/dist/ssr/index.js +4 -4
- package/dist/ssr/index.js.map +1 -1
- package/dist/ssr/next-config.d.ts +1 -1
- package/dist/ssr/next-config.js +6 -17
- package/dist/ssr/next-config.js.map +1 -1
- package/dist/ssr/next.d.ts +1 -1
- package/dist/ssr/next.js +3 -3
- package/dist/static/index.d.ts +2 -2
- package/dist/{types-JEY8PVYP.d.ts → types-DJEM_BOJ.d.ts} +6 -6
- package/dist/zero/babel.d.ts +1 -1
- package/dist/zero/babel.js +30 -19
- package/dist/zero/babel.js.map +1 -1
- package/dist/zero/index.d.ts +24 -6
- package/dist/zero/index.js +4 -4
- package/dist/zero/index.js.map +1 -1
- package/dist/zero/next.d.ts +1 -1
- package/dist/zero/next.js +3 -7
- package/dist/zero/next.js.map +1 -1
- package/docs/README.md +4 -1
- package/docs/adoption.md +25 -17
- package/docs/ai-agents.md +39 -34
- package/docs/comparison.md +9 -8
- package/docs/configuration.md +174 -58
- package/docs/debug.md +3 -2
- package/docs/design-system.md +117 -15
- package/docs/dsl.md +145 -81
- package/docs/getting-started.md +81 -24
- package/docs/injector.md +41 -40
- package/docs/methodology.md +46 -21
- package/docs/migration-v3.md +3 -3
- package/docs/pipeline.md +19 -16
- package/docs/plugins.md +21 -41
- package/docs/react-api.md +61 -9
- package/docs/runtime-benchmarks.md +4 -4
- package/docs/ssr.md +92 -66
- package/docs/styles.md +33 -17
- package/docs/tasty-static.md +129 -58
- package/package.json +23 -9
- package/dist/chunks/async-storage-CG5HnUT5.js +0 -20
- package/dist/chunks/async-storage-CG5HnUT5.js.map +0 -1
- package/dist/chunks/css-definitions-DAIJxzFY.js.map +0 -1
- package/dist/chunks/dsl-DRhFWQzo.js.map +0 -1
- package/dist/chunks/react-runtime-oi6i3ELR.js.map +0 -1
- package/dist/chunks/runtime-engine-DoULyhPt.js.map +0 -1
- package/dist/chunks/runtime-state-CZ2J5QiZ.js.map +0 -1
- package/dist/chunks/style-engine-CeKsjtIr.js.map +0 -1
- package/dist/chunks/zero-engine-fxKu8Jbn.js.map +0 -1
package/docs/ai-agents.md
CHANGED
|
@@ -10,6 +10,8 @@ Notation: ❌ wrong → ✅ correct.
|
|
|
10
10
|
|
|
11
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
12
|
|
|
13
|
+
Only Tasty's built-ins exist without config: `#white`, `#black`, `#border`, `#outline`, `#current`, `#clear`, and `$gap`, `$radius`, `$border-width`, `$outline-width`, `$transition`, `$sharp-radius`, `$bold-font-weight`, `$font-sans-fallback`, `$font-mono-fallback`.
|
|
14
|
+
|
|
13
15
|
## 1. Where styles go
|
|
14
16
|
|
|
15
17
|
```jsx
|
|
@@ -64,19 +66,20 @@ The last row goes the other way — longhands over the shorthand — because `fl
|
|
|
64
66
|
|
|
65
67
|
### Tokens
|
|
66
68
|
|
|
67
|
-
| Syntax | Meaning
|
|
68
|
-
| --------------------- |
|
|
69
|
-
| `#name` | color token → `var(--name-color)`
|
|
70
|
-
| `#name.50` | token at 50% opacity
|
|
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).
|
|
69
|
+
| Syntax | Meaning |
|
|
70
|
+
| --------------------- | -------------------------------------------------------------------------------------------------- |
|
|
71
|
+
| `#name` | color token → `var(--name-color)` |
|
|
72
|
+
| `#name.50` | token at 50% opacity — digits are a decimal fraction: `.5` = `.50` = 50%, `.05` = 5%, `.100` = 10% |
|
|
73
|
+
| `#name.$opacity` | opacity from a custom property |
|
|
74
|
+
| `#clear` / `#current` | `transparent` / `currentcolor` |
|
|
75
|
+
| `$name` | custom property → `var(--name)` |
|
|
76
|
+
| `(#a, #b)` | fallback chain |
|
|
77
|
+
| `$$name` / `##name` | the custom property _name_ (not its value) — inside `transition` |
|
|
78
|
+
| `$$name(…)` | call a CSS `@function` declared via `@function`, `useFunction`, or config |
|
|
79
|
+
|
|
80
|
+
- ❌ `#f5f5f5`, `rgb(0 0 0)`, `oklch(…)`, `okhsl(…)`, `okhst(…)`, `red` → ✅ `#surface` (add the token to the config if it doesn't exist).
|
|
79
81
|
- ❌ `var(--gap)` → ✅ `$gap` · ❌ `$accent-color` → ✅ `#accent` · ❌ `transparent` → ✅ `#clear` · ❌ `currentColor` → ✅ `#current`.
|
|
82
|
+
- `#clear` is a default replace token for `transparent`, so `#clear.5` and `#clear.$opacity` stay transparent. It does not read `--clear-color`; override it through `replaceTokens` if needed.
|
|
80
83
|
- A `$name` must be declared as a `'$name': value` key in the same styles object, or in the config.
|
|
81
84
|
- Names are case-sensitive and start lowercase — `$myVar` → `--myVar`; a leading capital folds (`$Foo` → `--foo`). Prefer kebab-case.
|
|
82
85
|
- 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.
|
|
@@ -84,7 +87,7 @@ The last row goes the other way — longhands over the shorthand — because `fl
|
|
|
84
87
|
|
|
85
88
|
### Units
|
|
86
89
|
|
|
87
|
-
Tasty units: `x`
|
|
90
|
+
Tasty units and their defaults: `x` 8px spacing step, `r` 6px radius, `cr` 10px card radius, `bw` 1px border width, `ow` 3px outline width, `sf` → `minmax(0, Nfr)`. They compile to literal values; change them through `units` in the config, not with `$gap`/`$radius` tokens. All CSS units work too, plus anything in `units` in the config. Any other unit is an error. Inside `@media(…)` / `@(…)` conditions units resolve to their literal length (`@media(w < 40x)` → `width < 320px`), unless the project points that unit at a custom property; then write query lengths in `px`, `em`, or `rem`.
|
|
88
91
|
|
|
89
92
|
Prefer units over raw pixels: `8px` → `1x` … `64px` → `8x`; `radius: '6px'` → `'1r'`; `border: '1px …'` → `'1bw …'`.
|
|
90
93
|
|
|
@@ -94,7 +97,7 @@ Prefer units over raw pixels: `8px` → `1x` … `64px` → `8x`; `radius: '6px'
|
|
|
94
97
|
|
|
95
98
|
### `true`
|
|
96
99
|
|
|
97
|
-
`true` means "the design-system default" and is accepted **only** by: `border`, `radius`, `outline`, `shadow`, `padding`, `margin`, `gap`, `inset`, `width`, `height`, `
|
|
100
|
+
`true` means "the design-system default" and is accepted **only** by: `border`, `radius`, `outline`, `shadow`, `padding`, `margin`, `gap`, `inset`, `width`, `height`, `color`, `fade`, `preset`, `font`, `scrollbar`, `scrollMargin`, `scrollPadding`, and the enhanced logical category styles listed above, plus `hide`. Anywhere else it is an error.
|
|
98
101
|
|
|
99
102
|
### `!important`
|
|
100
103
|
|
|
@@ -104,21 +107,23 @@ Never use it. Tasty owns specificity through doubled selectors and state orderin
|
|
|
104
107
|
|
|
105
108
|
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
109
|
|
|
107
|
-
| Property
|
|
108
|
-
|
|
|
109
|
-
| `padding`, `margin`, `
|
|
110
|
-
| `
|
|
111
|
-
|
|
|
112
|
-
|
|
|
113
|
-
| `
|
|
114
|
-
| `
|
|
115
|
-
| `
|
|
116
|
-
| `
|
|
117
|
-
| `
|
|
118
|
-
| `
|
|
119
|
-
| `
|
|
120
|
-
| `
|
|
121
|
-
|
|
|
110
|
+
| Property | Modifiers |
|
|
111
|
+
| -------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
112
|
+
| `padding`, `margin`, `fade` | `top` `right` `bottom` `left` |
|
|
113
|
+
| `inset` | the four directions + `dock` |
|
|
114
|
+
| `border` | the four directions + `solid` `dashed` `dotted` `double` `groove` `ridge` `inset` `outset` `none` `hidden` |
|
|
115
|
+
| logical axis spacing/inset styles | `start` `end` |
|
|
116
|
+
| `blockBorder`, `inlineBorder` | `start` `end` + the border style keywords above |
|
|
117
|
+
| `outline` | the style keywords above |
|
|
118
|
+
| `radius` | `top` `right` `bottom` `left`, corners `top-left` `top-right` `bottom-right` `bottom-left`, `inherit` + shapes `round` `ellipse` `leaf` `backleaf` |
|
|
119
|
+
| `width`, `height`, `blockSize`, `inlineSize` | `min` `max` `fixed` |
|
|
120
|
+
| `flow` | `row` `column` `row-reverse` `column-reverse` `wrap` `nowrap` `dense` |
|
|
121
|
+
| `overflow` | `visible` `hidden` `scroll` `clip` `auto` `overlay` |
|
|
122
|
+
| `position` | `static` `relative` `absolute` `fixed` `sticky` |
|
|
123
|
+
| `shadow` | `inset` |
|
|
124
|
+
| `preset` | `name / strong` (or `bold`) `italic` `icon` `tight` `normal` |
|
|
125
|
+
| `scrollbar` | `thin` `auto` `none` `stable` `both-edges` `always` |
|
|
126
|
+
| box properties | `longhand` — emit CSS longhands instead of the shorthand |
|
|
122
127
|
|
|
123
128
|
Directional modifiers beat placeholder zeros:
|
|
124
129
|
|
|
@@ -128,11 +133,11 @@ Directional modifiers beat placeholder zeros:
|
|
|
128
133
|
- ❌ `padding: '2x 4x top right'` — a directional group takes one value, so `4x` is dropped → ✅ `padding: '2x top, 4x right'`
|
|
129
134
|
- ❌ `fade: '3x 1x top bottom'` → ✅ `fade: '3x top, 1x bottom'`
|
|
130
135
|
|
|
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 `
|
|
136
|
+
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`, `color`, `caretColor`, and `accentColor` take a color only (use `#clear` for transparent).
|
|
132
137
|
|
|
133
138
|
### `transition`
|
|
134
139
|
|
|
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
|
|
140
|
+
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`.
|
|
136
141
|
|
|
137
142
|
❌ `transition: 'background-color 0.2s'` → ✅ `transition: 'fill'`
|
|
138
143
|
|
|
@@ -168,13 +173,13 @@ Precedence `!` > `^` > `|` > `&`; use parentheses to override.
|
|
|
168
173
|
|
|
169
174
|
Rules:
|
|
170
175
|
|
|
171
|
-
1. **`''` comes first.** The bare default is the lowest-priority state
|
|
176
|
+
1. **`''` comes first.** The bare default is the lowest-priority state. Written later, it triggers a `MISPLACED_DEFAULT_STATE` warning and Tasty moves it to the front.
|
|
172
177
|
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
178
|
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
179
|
4. **No nested maps:** ❌ `{ hovered: { pressed: 'x' } }` → ✅ `{ 'hovered & pressed': 'x' }`
|
|
175
180
|
5. **State keys never sit at the top level** of a styles object — `:hover`, `.active`, `[open]` belong inside a property value.
|
|
176
181
|
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 `
|
|
182
|
+
7. **Prefer automatic native states:** use `disabled`, `checked`, and `hidden` in Tasty component styles instead of `[disabled]`, `[checked]`, and `[hidden]`. Tasty sets `data-disabled`, `data-checked`, and `data-hidden` from the native props automatically.
|
|
178
183
|
|
|
179
184
|
```jsx
|
|
180
185
|
color: { '': '#text', hovered: '#accent', disabled: '#text.40' }
|
|
@@ -204,7 +209,7 @@ When extending a parent's state map: `'@inherit'` reuses the parent's value for
|
|
|
204
209
|
A **capitalized** key targets `[data-element="Name"]`, and its value must be a style object.
|
|
205
210
|
|
|
206
211
|
```jsx
|
|
207
|
-
styles: { Title: { preset: 'h3' }, Icon: { $: '
|
|
212
|
+
styles: { Title: { preset: 'h3' }, Icon: { $: '>', color: { '': '#text', '@own(:last-child)': '#accent' } } }
|
|
208
213
|
```
|
|
209
214
|
|
|
210
215
|
❌ 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).
|
package/docs/comparison.md
CHANGED
|
@@ -6,7 +6,7 @@ Tasty is CSS-in-JS for React design systems. It is built for the point where reu
|
|
|
6
6
|
|
|
7
7
|
Its distinctive guarantee is narrow and concrete: each property’s state map compiles into selectors that cannot compete, so one declared branch wins regardless of CSS source order.
|
|
8
8
|
|
|
9
|
-
That does not require a big upfront configuration step. Tasty's built-in units and normal CSS color values work out of the box, and `okhsl(...)`
|
|
9
|
+
That does not require a big upfront configuration step. Tasty's built-in units and normal CSS color values work out of the box, and the built-in `okhsl(...)` and `okhst(...)` color functions are available immediately as the recommended path for color authoring. The extra setup comes later if a team wants shared tokens, aliases, recipes, or stricter conventions.
|
|
10
10
|
|
|
11
11
|
Most styling tools focus on one of these layers:
|
|
12
12
|
|
|
@@ -95,11 +95,12 @@ That makes Tasty less of a "better way to write CSS objects" and more of a **sta
|
|
|
95
95
|
|
|
96
96
|
Beyond state resolution, Tasty also provides several structural capabilities that reinforce the design-system layer:
|
|
97
97
|
|
|
98
|
-
- **
|
|
99
|
-
- **Sub-element styling** — Compound components declare inner parts via capitalized keys in `styles` and `
|
|
98
|
+
- **Typed component APIs** — `styleProps` exposes selected style properties as normal props (`<Button placeSelf="end">`, with state maps for responsive values), `modProps` turns modifiers into typed props (`size: ['sm', 'md', 'lg'] as const`), `tokenProps` passes per-instance values without generating new CSS, and `variants` gives a typed `variant` prop. Layout and composition controls stay inside a governed component API instead of pushing teams back to ad hoc styling escapes.
|
|
99
|
+
- **Sub-element styling** — Compound components declare inner parts via capitalized keys in `styles` and the `elements` option, which produces typed sub-components (`<Card.Title>`) that share the root's state context. States, tokens, and recipes apply across the entire element tree from a single definition. See [React API — Sub-element Styling](react-api.md#sub-element-styling).
|
|
100
|
+
- **Modern CSS as state keys** — Container queries, `@supports`, `:has()`, root and parent states, and `@starting-style` live in the same state maps as `:hover`, and `@keyframes`, `@property`, `@font-face`, `@counter-style`, and `@function` are built in.
|
|
100
101
|
- **Auto-inferred `@property`** — When a custom property is assigned a concrete value, Tasty infers the CSS `@property` syntax and registers it automatically, enabling smooth transitions on custom properties without manual declarations.
|
|
101
102
|
- **AI-friendly style definitions** — Style definitions are declarative, self-contained, and structurally consistent. AI tools can read, refactor, and generate Tasty styles as confidently as a human — no hidden cascade logic or implicit ordering to second-guess.
|
|
102
|
-
- **Companion ecosystem** — An [ESLint plugin](https://github.com/tenphi/eslint-plugin-tasty) with
|
|
103
|
+
- **Companion ecosystem** — An [ESLint and oxlint plugin](https://github.com/tenphi/eslint-plugin-tasty) with 40+ rules that validate styles against your project's `tasty.config.ts`, TypeScript augmentation for autocompleting your own token and preset names, a [VS Code extension](https://github.com/tenphi/tasty-vscode-extension) for syntax highlighting and snippets, and [Glaze](https://github.com/tenphi/glaze) for OKHST palette generation with WCAG and APCA contrast solving.
|
|
103
104
|
|
|
104
105
|
---
|
|
105
106
|
|
|
@@ -184,7 +185,7 @@ Every new state doubles the selector count. Miss one intersection and you ship a
|
|
|
184
185
|
)}>
|
|
185
186
|
```
|
|
186
187
|
|
|
187
|
-
The `theme` branch is runtime JS, not CSS.
|
|
188
|
+
The `theme` branch is runtime JS, not CSS. Tailwind resolves `hover:` against `disabled:` by its global variant order, not per component; when your priority differs from that order, or two base utilities conflict (`bg-primary` vs `bg-danger`), you need `tailwind-merge`, `!` modifiers, or more conditional class logic.
|
|
188
189
|
|
|
189
190
|
**Tasty** — each property declares all its states in one map. The engine generates mutually exclusive selectors:
|
|
190
191
|
|
|
@@ -196,10 +197,10 @@ const Button = tasty({
|
|
|
196
197
|
'': '#primary',
|
|
197
198
|
':hover': '#primary-hover',
|
|
198
199
|
':active': '#primary-pressed',
|
|
199
|
-
disabled: '#surface',
|
|
200
200
|
'theme=danger': '#danger',
|
|
201
201
|
'theme=danger & :hover': '#danger-hover',
|
|
202
202
|
'theme=danger & :active': '#danger-pressed',
|
|
203
|
+
disabled: '#surface',
|
|
203
204
|
},
|
|
204
205
|
color: {
|
|
205
206
|
'': '#on-primary',
|
|
@@ -213,7 +214,7 @@ const Button = tasty({
|
|
|
213
214
|
});
|
|
214
215
|
```
|
|
215
216
|
|
|
216
|
-
`disabled`
|
|
217
|
+
`disabled` is declared last, so it wins over `:hover`, `:active`, and `theme=danger`: Tasty guards every earlier branch with `:not([data-disabled])` — no source-order dependence, no manual intersection management, no `!important`.
|
|
217
218
|
|
|
218
219
|
---
|
|
219
220
|
|
|
@@ -328,7 +329,7 @@ That makes it narrower in audience, but deeper in architectural ambition.
|
|
|
328
329
|
|
|
329
330
|
There is also a fundamental architectural difference: Emotion and styled-components rely on React context and hooks internally, which means they require `'use client'` in modern React and cannot run as React Server Components. Tasty's style functions and `tasty()` components are hook-free, so they work as server components by default and ship no Tasty styling runtime in server-only contexts. This is not a minor compatibility detail — it means Tasty-based components stay as server components until _your_ code needs interactivity, while Emotion and styled-components force the client boundary at the styling layer.
|
|
330
331
|
|
|
331
|
-
For teams evaluating runtime styling at scale, Tasty
|
|
332
|
+
For teams evaluating runtime styling at scale, Tasty publishes reproducible [Runtime Benchmarks](runtime-benchmarks.md) and summarizes its caching model in the [README](../README.md#performance). That matters, but it is still secondary to the core question of whether you want Tasty's deterministic selector model.
|
|
332
333
|
|
|
333
334
|
---
|
|
334
335
|
|
package/docs/configuration.md
CHANGED
|
@@ -23,9 +23,8 @@ configure({
|
|
|
23
23
|
|
|
24
24
|
// Custom units (merged with built-in units)
|
|
25
25
|
units: {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
custom: (n) => `${n * 10}px`, // Function-based unit
|
|
26
|
+
gu: '4px', // `2gu` → `8px`
|
|
27
|
+
cols: (n) => `calc(${n} * var(--col-width))`, // function-based unit
|
|
29
28
|
},
|
|
30
29
|
|
|
31
30
|
// Custom functions — a single map for both flavors, discriminated by value type:
|
|
@@ -47,35 +46,35 @@ These docs use `data-schema="dark"` in examples. If your app already standardize
|
|
|
47
46
|
|
|
48
47
|
## Options
|
|
49
48
|
|
|
50
|
-
| Option | Type | Default
|
|
51
|
-
| -------------------- | ------------------------------------------------ |
|
|
52
|
-
| `nonce` | `string` | -
|
|
53
|
-
| `maxRulesPerSheet` | `number` | `8192`
|
|
54
|
-
| `forceTextInjection` | `boolean` |
|
|
55
|
-
| `devMode` | `boolean` | auto
|
|
56
|
-
| `states` | `Record<string, string>` | -
|
|
57
|
-
| `parserCacheSize` | `number` | `1000`
|
|
58
|
-
| `units` | `Record<string, string \| UnitHandler>` | Built-in
|
|
59
|
-
| `functions` | `Record<string, FunctionDefinition \| Function>` | -
|
|
60
|
-
| `handlers` | `Record<string, StyleHandlerDefinition>` | Built-in
|
|
61
|
-
| `propHandlers` | `Record<string, PropHandlerDefinition>` | -
|
|
62
|
-
| `baseStyleProps` | `readonly string[]` | -
|
|
63
|
-
| `tokens` | `Record<string, value \| stateMap>` | -
|
|
64
|
-
| `replaceTokens` | `Record<string, string \| number \| boolean>` |
|
|
65
|
-
| `keyframes` | `Record<string, KeyframesSteps>` | -
|
|
66
|
-
| `properties` | `Record<string, PropertyDefinition>` | -
|
|
67
|
-
| `fontFaces` | `Record<string, FontFaceInput>` | -
|
|
68
|
-
| `counterStyles` | `Record<string, CounterStyleDescriptors>` | -
|
|
69
|
-
| `polyfills` | `{ functions?: boolean }` | `{}`
|
|
70
|
-
| `autoPropertyTypes` | `boolean` | `true`
|
|
71
|
-
| `recipes` | `Record<string, RecipeStyles>` | -
|
|
72
|
-
| `presets` | `Record<string, TypographyPreset>` | -
|
|
73
|
-
| `globalStyles` | `Record<string, Styles>` | -
|
|
74
|
-
| `plugins` | `TastyPlugin[]` | -
|
|
75
|
-
| `gc` | `GCConfig` | -
|
|
76
|
-
| `batchInjection` | `boolean \| 'always'` | `false`
|
|
77
|
-
| `colorSpace` | `'rgb' \| 'hsl' \| 'oklch'` | -
|
|
78
|
-
| `namePrefix` | `string` | `'t'` (`tasty`) / `'ts'` (build-time)
|
|
49
|
+
| Option | Type | Default | Description |
|
|
50
|
+
| -------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
51
|
+
| `nonce` | `string` | - | CSP nonce for style elements |
|
|
52
|
+
| `maxRulesPerSheet` | `number` | `8192` | Maximum rules per injected stylesheet |
|
|
53
|
+
| `forceTextInjection` | `boolean` | `false` (`true` in test environments only while `configure()` has never been called) | Force text-node CSS injection instead of constructable stylesheets. Set it explicitly in test setup |
|
|
54
|
+
| `devMode` | `boolean` | auto | Enable development-mode features: performance metrics and debug info. Does not turn on dev warnings; those follow `NODE_ENV` (or `localStorage.TASTY_DEBUG = 'true'` in a browser) |
|
|
55
|
+
| `states` | `Record<string, string>` | - | Global state aliases for advanced state mapping |
|
|
56
|
+
| `parserCacheSize` | `number` | `1000` | Parser LRU cache size |
|
|
57
|
+
| `units` | `Record<string, string \| UnitHandler>` | Built-in | Custom units (merged with built-in). See [built-in units](dsl.md#built-in-units) |
|
|
58
|
+
| `functions` | `Record<string, FunctionDefinition \| Function>` | - | Custom functions (merged). Bare keys → parse functions; `$$name` keys → declarative CSS `@function` definitions |
|
|
59
|
+
| `handlers` | `Record<string, StyleHandlerDefinition>` | Built-in | Custom style handlers (replace built-in). See [Custom Style Handlers](#custom-style-handlers) |
|
|
60
|
+
| `propHandlers` | `Record<string, PropHandlerDefinition>` | - | Props middleware for every component — props in, props out. See [Props Middleware](#props-middleware) |
|
|
61
|
+
| `baseStyleProps` | `readonly string[]` | - | Style names exposed as props on **every** component. See [Base Style Props](#base-style-props) |
|
|
62
|
+
| `tokens` | `Record<string, value \| stateMap>` | - | Design tokens injected as `:root` CSS custom properties |
|
|
63
|
+
| `replaceTokens` | `Record<string, string \| number \| boolean>` | `{ '#clear': 'transparent' }` | Parse-time token substitution (inline replacement). `boolean` is allowed for `#` color tokens |
|
|
64
|
+
| `keyframes` | `Record<string, KeyframesSteps>` | - | Global keyframes for animations |
|
|
65
|
+
| `properties` | `Record<string, PropertyDefinition>` | - | Global CSS @property definitions |
|
|
66
|
+
| `fontFaces` | `Record<string, FontFaceInput>` | - | Global @font-face definitions |
|
|
67
|
+
| `counterStyles` | `Record<string, CounterStyleDescriptors>` | - | Global @counter-style definitions |
|
|
68
|
+
| `polyfills` | `{ functions?: boolean }` | `{}` | Opt-in polyfills for not-yet-baseline features. `functions: true` inlines `@function` calls into plain CSS at parse time |
|
|
69
|
+
| `autoPropertyTypes` | `boolean` | `true` | Auto-infer and register `@property` types from values |
|
|
70
|
+
| `recipes` | `Record<string, RecipeStyles>` | - | Predefined style recipes (named style bundles) |
|
|
71
|
+
| `presets` | `Record<string, TypographyPreset>` | - | Typography presets — shorthand for `generateTypographyTokens()` |
|
|
72
|
+
| `globalStyles` | `Record<string, Styles>` | - | Global Tasty styles keyed by CSS selector |
|
|
73
|
+
| `plugins` | `TastyPlugin[]` | - | Plugins that bundle `functions`, `units`, `states`, `handlers`, `propHandlers`, `baseStyleProps`, `tokens`, `replaceTokens`, `recipes`, `presets`, `globalStyles`, `properties`, `keyframes`, `fontFaces`, and `counterStyles` (processed in order; later override earlier, and direct config wins over all). See [Plugins](plugins.md) |
|
|
74
|
+
| `gc` | `GCConfig` | - | Turns on automatic collection of unused styles: `{ touchInterval?, capacity?, grace? }` (defaults `1000`, `1000`, `10000` ms). Without it nothing is collected automatically; call `gc()` yourself |
|
|
75
|
+
| `batchInjection` | `boolean \| 'always'` | `false` | Defer stylesheet writes and apply them in one batch. See [Batched injection](#batched-injection) |
|
|
76
|
+
| `colorSpace` | `'rgb' \| 'hsl' \| 'oklch'` | - | **Deprecated** — no longer has any effect. See [Color space](#color-space) |
|
|
77
|
+
| `namePrefix` | `string` | `'t'` (`tasty`) / `'ts'` (build-time) | Prefix prepended to every generated identifier (class, keyframe, counter-style names). Must match `^[a-zA-Z_][a-zA-Z0-9_-]{0,31}$`. See [Name prefix](#name-prefix). |
|
|
79
78
|
|
|
80
79
|
---
|
|
81
80
|
|
|
@@ -334,8 +333,7 @@ Tokens support state maps for responsive or theme-aware values:
|
|
|
334
333
|
```jsx
|
|
335
334
|
configure({
|
|
336
335
|
tokens: {
|
|
337
|
-
$
|
|
338
|
-
$radius: '6px',
|
|
336
|
+
'$sidebar-width': '280px',
|
|
339
337
|
'#primary': {
|
|
340
338
|
'': '#purple',
|
|
341
339
|
'@dark': '#light-purple',
|
|
@@ -350,9 +348,10 @@ configure({
|
|
|
350
348
|
|
|
351
349
|
- `$name` keys become `--name` CSS custom properties
|
|
352
350
|
- `#name` keys become `--name-color` custom properties
|
|
351
|
+
- Tokens do not change units: `2x` stays `16px` whatever `$gap` is. To drive a unit from a token, point the unit at it: `units: { x: 'var(--gap)' }` makes `2x` compile to `calc(2 * var(--gap))`
|
|
353
352
|
- Names keep their case, since CSS custom properties are case-sensitive: `$myVar` is `--myVar` and is referenced as `$myVar`. A leading capital is not supported and folds — `$Foo` and `#Purple` become `--foo` and `--purple-color` — so start names lowercase. Kebab-case (`$my-var`) remains the convention.
|
|
354
353
|
|
|
355
|
-
Tokens are
|
|
354
|
+
Tokens are emitted by the client injector, by the SSR collector (`TastyRegistry`, `tastyIntegration()`, `runWithCollector()`), by the `withTastyNext()` shared stylesheet, and by the Babel plugin. The inline React Server Component path without a collector does not emit them — see [Server-Side Rendering](ssr.md).
|
|
356
355
|
|
|
357
356
|
---
|
|
358
357
|
|
|
@@ -375,6 +374,17 @@ configure({
|
|
|
375
374
|
|
|
376
375
|
When a component uses `padding: '$card-padding'`, the parser replaces it with `'4x'` before generating CSS. When a component uses `fill: '#accent'`, it is replaced with `'#purple'`, which in turn resolves to `var(--purple-color)`.
|
|
377
376
|
|
|
377
|
+
Tasty includes `'#clear': 'transparent'` as a default replace token, available
|
|
378
|
+
without calling `configure()`. It survives `resetConfig()`, and explicit
|
|
379
|
+
`replaceTokens` entries can override it. A replace token resolving to the literal
|
|
380
|
+
`transparent` ignores numeric and custom-property opacity suffixes:
|
|
381
|
+
`#clear`, `#clear.5`, and `#clear.$opacity` all emit `transparent`.
|
|
382
|
+
|
|
383
|
+
`#clear` no longer reads `--clear-color`, and Tasty no longer registers that
|
|
384
|
+
property by default. If you previously customized `#clear` through `tokens`,
|
|
385
|
+
local token definitions, or CSS, use a different color token or explicitly
|
|
386
|
+
override `replaceTokens['#clear']` instead.
|
|
387
|
+
|
|
378
388
|
See [Replace Tokens](dsl.md#replace-tokens) in the Style DSL reference.
|
|
379
389
|
|
|
380
390
|
---
|
|
@@ -471,7 +481,7 @@ See [Functions (`@function`)](dsl.md#functions-function) for inline usage inside
|
|
|
471
481
|
|
|
472
482
|
### Custom color functions
|
|
473
483
|
|
|
474
|
-
A parse function whose output is an already-supported color (`rgb`, `hsl`, `#…`, `oklch`, …) is treated as a **color function**: it works everywhere a color is accepted — style values, `#token.alpha` opacity
|
|
484
|
+
A parse function whose output is an already-supported color (`rgb`, `hsl`, `#…`, `oklch`, …) is treated as a **color function**: it works everywhere a color is accepted — style values, `parseColor`, and the `#token.alpha` opacity suffix, which calls the function with a `/ alpha` part appended (read `groups[0].parts[1]`, as below) — with no extra registration. This is the same mechanism the built-in `okhsl`/`okhst` plugins use; they are ordinary plugins registered by default.
|
|
475
485
|
|
|
476
486
|
```ts
|
|
477
487
|
import { configure, createColorFunc } from '@tenphi/tasty';
|
|
@@ -482,8 +492,12 @@ const myColorPlugin = () => ({
|
|
|
482
492
|
functions: {
|
|
483
493
|
// Hand-written parse function:
|
|
484
494
|
mycolor: (groups) => {
|
|
485
|
-
|
|
486
|
-
|
|
495
|
+
// `mycolor(255 0 0 / .5)` and `#brand.5` both arrive as two parts
|
|
496
|
+
const [main, alpha] = groups[0].parts;
|
|
497
|
+
const [r, g, b] = main.all;
|
|
498
|
+
return alpha?.all.length
|
|
499
|
+
? `rgb(${r} ${g} ${b} / ${alpha.output})`
|
|
500
|
+
: `rgb(${r} ${g} ${b})`;
|
|
487
501
|
},
|
|
488
502
|
},
|
|
489
503
|
});
|
|
@@ -690,10 +704,16 @@ Override or extend the built-in style property handlers. A handler definition ca
|
|
|
690
704
|
| Single dep | `['styleName', handler]` | Triggered by the specified style property |
|
|
691
705
|
| Multi dep | `[['dep1', 'dep2', ...], handler]` | Triggered by any of the listed properties; receives all of them |
|
|
692
706
|
|
|
693
|
-
The multi-dep form is useful when output depends on several style properties together (e.g., `gap` needs to know `display` and `flow` to decide the CSS strategy). Use `defineHandler` for it and the dependency types are inferred from the dependency list, so a typo in the destructure is a type error instead of a silent `undefined`.
|
|
707
|
+
The multi-dep form is useful when output depends on several style properties together (e.g., `gap` needs to know `display` and `flow` to decide the CSS strategy). A handler whose dependencies include a built-in style takes over that style's whole group — see [Replacing a built-in handler](#replacing-a-built-in-handler). Use `defineHandler` for it and the dependency types are inferred from the dependency list, so a typo in the destructure is a type error instead of a silent `undefined`.
|
|
694
708
|
|
|
695
709
|
```jsx
|
|
696
|
-
import {
|
|
710
|
+
import {
|
|
711
|
+
configure,
|
|
712
|
+
defineHandler,
|
|
713
|
+
parseColor,
|
|
714
|
+
parseStyle,
|
|
715
|
+
styleHandlers,
|
|
716
|
+
} from '@tenphi/tasty';
|
|
697
717
|
|
|
698
718
|
configure({
|
|
699
719
|
handlers: {
|
|
@@ -717,6 +737,10 @@ configure({
|
|
|
717
737
|
'backgroundPosition',
|
|
718
738
|
'backgroundSize',
|
|
719
739
|
'backgroundRepeat',
|
|
740
|
+
'backgroundAttachment',
|
|
741
|
+
'backgroundOrigin',
|
|
742
|
+
'backgroundClip',
|
|
743
|
+
'background',
|
|
720
744
|
],
|
|
721
745
|
(props) => {
|
|
722
746
|
if (
|
|
@@ -729,12 +753,18 @@ configure({
|
|
|
729
753
|
},
|
|
730
754
|
),
|
|
731
755
|
|
|
732
|
-
// Multi dep —
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
756
|
+
// Multi dep — triggered by either name, receives both
|
|
757
|
+
stripe: defineHandler(
|
|
758
|
+
['stripe', 'stripeColor'],
|
|
759
|
+
({ stripe, stripeColor }) => {
|
|
760
|
+
if (!stripe) return;
|
|
761
|
+
const width = parseStyle(String(stripe)).output;
|
|
762
|
+
const color = stripeColor
|
|
763
|
+
? parseColor(String(stripeColor)).color
|
|
764
|
+
: 'currentcolor';
|
|
765
|
+
return { 'border-left': `${width} solid ${color}` };
|
|
766
|
+
},
|
|
767
|
+
),
|
|
738
768
|
},
|
|
739
769
|
});
|
|
740
770
|
```
|
|
@@ -745,6 +775,7 @@ A handler returns a `CSSMap`, an array of them, or nothing:
|
|
|
745
775
|
|
|
746
776
|
- Keys are **kebab-case** CSS property names (`'background-color'`, not `backgroundColor`) or `--custom-property` names. A camelCase key warns in development and is emitted verbatim, which the browser ignores.
|
|
747
777
|
- Values are stringified, so numbers are fine (`{ '-webkit-line-clamp': 3 }`).
|
|
778
|
+
- An array value emits one declaration per entry, in order, so the browser keeps the last one it supports: `{ width: ['-webkit-fill-available', '-moz-available', 'stretch'] }` emits three `width` declarations.
|
|
748
779
|
- The reserved `$` key is a **selector suffix**: `{ $: '& > *:not(:last-child)', 'margin-right': gap }` applies the declarations to a nested selector. It accepts an array to fan out over several.
|
|
749
780
|
- Return an array of maps to emit several declaration sets, each with its own `$`.
|
|
750
781
|
|
|
@@ -756,12 +787,17 @@ Built-in handlers are shared across several style names. Registering a handler f
|
|
|
756
787
|
|
|
757
788
|
The shared groups worth knowing:
|
|
758
789
|
|
|
759
|
-
| Registering a handler for
|
|
760
|
-
|
|
|
761
|
-
| `fill`
|
|
762
|
-
| `display`
|
|
763
|
-
| `
|
|
764
|
-
| `
|
|
790
|
+
| Registering a handler for | Also takes over |
|
|
791
|
+
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
792
|
+
| `fill` | `backgroundColor`, `image`, `backgroundImage`, `backgroundPosition`, `backgroundSize`, `backgroundRepeat`, `backgroundAttachment`, `backgroundOrigin`, `backgroundClip`, `background` |
|
|
793
|
+
| `display` | `hide`, `whiteSpace`, `textOverflow`, `flow`, `gap` |
|
|
794
|
+
| `flow` | `gap` |
|
|
795
|
+
| `preset` | `font`, `fontFamily`, `fontSize`, `fontWeight`, `fontStyle`, `lineHeight`, `letterSpacing`, `textTransform` |
|
|
796
|
+
| `padding` / `margin` | their `Top`/`Right`/`Bottom`/`Left` longhands (`paddingTop`, `marginLeft`, …) |
|
|
797
|
+
| `inset` | `top`, `right`, `bottom`, `left` |
|
|
798
|
+
| `width` / `height` | `minWidth`/`maxWidth`, `minHeight`/`maxHeight` |
|
|
799
|
+
|
|
800
|
+
A multi-dep handler takes over every group its dependencies touch: `defineHandler(['display', 'flow', 'gap'], …)` takes over `display`, `flow`, and `gap` together, so it must emit `display` and `flex-flow` itself.
|
|
765
801
|
|
|
766
802
|
Either declare the whole group and delegate the rest to `styleHandlers.*`, or pick a name that isn't shared.
|
|
767
803
|
|
|
@@ -822,18 +858,98 @@ Each name costs one property check per render of every component, and the effect
|
|
|
822
858
|
|
|
823
859
|
---
|
|
824
860
|
|
|
825
|
-
##
|
|
861
|
+
## TypeScript
|
|
826
862
|
|
|
827
|
-
|
|
863
|
+
Component props are typed from the `tasty()` definition with no setup. Module augmentation adds the names your design system defines and the extensions your plugins add.
|
|
864
|
+
|
|
865
|
+
### Component props
|
|
866
|
+
|
|
867
|
+
| In `tasty({ … })` | Adds the props |
|
|
868
|
+
| --------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
869
|
+
| `styleProps: ['gap', 'padding'] as const` | `gap`, `padding`, each typed like the style: a value or a state map |
|
|
870
|
+
| `modProps: { isLoading: Boolean, size: ['sm', 'md', 'lg'] as const }` | `isLoading?: boolean`, `size?: 'sm' \| 'md' \| 'lg'` (`String` gives `string`, `Number` gives `number`) |
|
|
871
|
+
| `modProps: ['isLoading'] as const` | `isLoading?: ModValue` |
|
|
872
|
+
| `tokenProps: ['progress', 'accentColor'] as const` | `progress?: TokenValue`, `accentColor?: TokenValue` |
|
|
873
|
+
| `variants: { default: {…}, danger: {…} }` | `variant?: 'default' \| 'danger'` |
|
|
874
|
+
| `elements: { Title: 'h3' }` | `Card.Title`, with the `h3` props plus `qa`, `qaVal`, `mods`, `tokens`, `isDisabled`, `isHidden`, `isChecked`, and a `ref` typed as `HTMLHeadingElement`; no `styles` |
|
|
875
|
+
| `as: SomeComponent` | `SomeComponent`'s own props, required ones still required |
|
|
876
|
+
|
|
877
|
+
Write the array forms with `as const`. Without it, `modProps: ['isLoading']` widens to `string[]` and every prop on the component is typed `ModValue`, so an `onClick` handler no longer type-checks. A `modProps` value list without `as const` widens to `string`.
|
|
878
|
+
|
|
879
|
+
`tasty(Base, { … })` keeps `Base`'s prop types and sub-elements, and adds the props and sub-elements its own options above declare. Its `variant` accepts both its own variant names and `Base`'s. See [Extend an existing component](react-api.md#extend-an-existing-component).
|
|
880
|
+
|
|
881
|
+
### Registering your names and extensions
|
|
882
|
+
|
|
883
|
+
```ts
|
|
884
|
+
// tasty.d.ts — keep the import: it makes this file a module, so the block
|
|
885
|
+
// below extends '@tenphi/tasty' instead of replacing it.
|
|
886
|
+
import '@tenphi/tasty';
|
|
828
887
|
|
|
829
|
-
```tsx
|
|
830
|
-
// tasty.d.ts
|
|
831
888
|
declare module '@tenphi/tasty' {
|
|
889
|
+
interface TastyNamedColors {
|
|
890
|
+
surface: true;
|
|
891
|
+
text: true;
|
|
892
|
+
primary: true;
|
|
893
|
+
}
|
|
894
|
+
interface TastyPresetNames {
|
|
895
|
+
h1: true;
|
|
896
|
+
t2: true;
|
|
897
|
+
}
|
|
898
|
+
interface TastyThemeNames {
|
|
899
|
+
danger: true;
|
|
900
|
+
success: true;
|
|
901
|
+
}
|
|
832
902
|
interface StylesInterface {
|
|
833
|
-
|
|
834
|
-
|
|
903
|
+
stripe?: string;
|
|
904
|
+
}
|
|
905
|
+
interface TastyCustomProps {
|
|
906
|
+
glaze: 'soft' | 'strong' | { tone: string; intensity?: number };
|
|
907
|
+
}
|
|
908
|
+
interface TastyBaseStylePropNames {
|
|
909
|
+
radius: true;
|
|
910
|
+
stripe: true;
|
|
835
911
|
}
|
|
836
912
|
}
|
|
837
913
|
```
|
|
838
914
|
|
|
915
|
+
| Interface | Register | What it types |
|
|
916
|
+
| ------------------------- | -------------------------------------------------------------------- | ------------------------------------------------------------------------ |
|
|
917
|
+
| `TastyNamedColors` | Color token names from `tokens` and `replaceTokens`, without the `#` | Suggestions for `#name` and `#name.50` in `fill`, `color`, and `svgFill` |
|
|
918
|
+
| `TastyPresetNames` | `presets` names | The `preset` style and prop |
|
|
919
|
+
| `TastyThemeNames` | Theme names | The `theme` prop (rendered as `data-theme`) |
|
|
920
|
+
| `StylesInterface` | Style properties added with `handlers` | Their value type in `styles`, `styleProps`, and base style props |
|
|
921
|
+
| `TastyCustomProps` | Props added with `propHandlers` | An optional prop on every `tasty()` component (not on sub-elements) |
|
|
922
|
+
| `TastyBaseStylePropNames` | Names passed to `baseStyleProps` | A prop on every component, typed like the style it names |
|
|
923
|
+
|
|
924
|
+
- The first three add autocomplete, not checking: any other string still type-checks (`preset: 'h9'` compiles). The ESLint plugin is what reports unknown names.
|
|
925
|
+
- In `StylesInterface`, declare the plain value type (`stripe?: string`). Tasty adds state-map support itself, as it does for built-in styles.
|
|
926
|
+
- A custom style name in `styles` type-checks without augmentation, but `styleProps` and `TastyBaseStylePropNames` accept only names in `StylesInterface`. A base style prop name missing from it adds no prop.
|
|
927
|
+
- `TastyCustomProps` types the component prop, not the handler. A prop handler receives `Record<string, unknown>`, so cast inside it (`rest.styles as Styles`).
|
|
928
|
+
- State keys, `$` value tokens, units, and recipe names have no TypeScript types. The ESLint plugin checks them against `tasty.config.ts`.
|
|
929
|
+
|
|
930
|
+
### `tasty.config.ts`
|
|
931
|
+
|
|
932
|
+
TypeScript augmentation does not reach the linter or the editor. [`@tenphi/eslint-plugin-tasty`](https://github.com/tenphi/eslint-plugin-tasty) and the Tasty VS Code extension read a `tasty.config.ts` (or `.js`, `.json`) at the project root instead. It only lists names: it is not passed to `configure()`, and it is unrelated to the Babel plugin's `configFile` (such as `tasty-zero.config.ts`).
|
|
933
|
+
|
|
934
|
+
```ts
|
|
935
|
+
// tasty.config.ts
|
|
936
|
+
import type { TastyExtensionConfig } from '@tenphi/tasty';
|
|
937
|
+
import type { TastyValidationConfig } from '@tenphi/eslint-plugin-tasty';
|
|
938
|
+
|
|
939
|
+
export default {
|
|
940
|
+
tokens: ['#surface', '#text', '#primary', '$sidebar-width'],
|
|
941
|
+
units: ['cols'],
|
|
942
|
+
states: ['@mobile', '@dark'],
|
|
943
|
+
presets: ['h1', 't2'],
|
|
944
|
+
recipes: ['card', 'elevated'],
|
|
945
|
+
styles: ['stripe'],
|
|
946
|
+
importSources: ['@my-org/ui'],
|
|
947
|
+
presetDescriptions: { h1: 'Heading 1 (32px, bold)' },
|
|
948
|
+
} satisfies TastyExtensionConfig & TastyValidationConfig;
|
|
949
|
+
```
|
|
950
|
+
|
|
951
|
+
`TastyExtensionConfig` from `@tenphi/tasty` types the fields the VS Code extension reads: `extends`, `tokens`, `units`, `functions`, `states`, `presets`, `styles`, `presetDescriptions`, and `stateDescriptions`. It has no `recipes`, `importSources`, `ownedSources`, or `styleFunctions`, so annotating the file with it alone rejects those ESLint fields; intersect it with the plugin's `TastyValidationConfig`, as above. An omitted list is not checked; `tokens: false`, `units: false`, or `functions: false` turns a check off even when an `extends` parent sets it.
|
|
952
|
+
|
|
953
|
+
Keep it in step with `configure()`: token names with their `#` or `$`, custom units, parse-function names, state aliases, presets, recipes, and custom style names.
|
|
954
|
+
|
|
839
955
|
See [Style DSL](dsl.md) for state maps, tokens, units, and extending semantics, and [React API](react-api.md) for `tasty()`, style functions, and component props.
|