@tenphi/cookbook 0.28.0 → 0.30.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 +13 -11
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/docs/ai-agents.md +5 -3
- package/docs/architecture.md +10 -7
- package/docs/authoring.mdx +94 -36
- package/docs/cli.md +92 -33
- package/docs/component-styles.md +286 -282
- package/docs/configuration.md +232 -153
- package/docs/content-sources.md +39 -21
- package/docs/custom-components.md +117 -169
- package/docs/customization-rules.md +23 -14
- package/docs/deployment.md +101 -53
- package/docs/es/getting-started.md +7 -5
- package/docs/es/index.md +1 -1
- package/docs/examples.md +65 -28
- package/docs/fonts-and-typography.md +32 -27
- package/docs/getting-started.md +17 -10
- package/docs/ja/getting-started.md +7 -5
- package/docs/ja/index.md +1 -1
- package/docs/migration.md +188 -65
- package/docs/plugins.md +28 -16
- package/docs/publishing.md +3 -2
- package/docs/quality-checks.md +18 -42
- package/docs/recipes.md +38 -54
- package/docs/ru/getting-started.md +7 -5
- package/docs/ru/index.md +1 -1
- package/docs/site-navigation.md +47 -47
- package/docs/theme-and-components.md +126 -59
- package/docs/troubleshooting.md +4 -3
- package/docs/upstream/manifest.json +2 -2
- package/docs/upstream/tasty/README.md +404 -381
- package/docs/upstream/tasty/docs/README.md +4 -1
- package/docs/upstream/tasty/docs/adoption.md +25 -17
- package/docs/upstream/tasty/docs/ai-agents.md +42 -34
- package/docs/upstream/tasty/docs/comparison.md +9 -8
- package/docs/upstream/tasty/docs/configuration.md +187 -69
- package/docs/upstream/tasty/docs/debug.md +3 -2
- package/docs/upstream/tasty/docs/design-system.md +117 -15
- package/docs/upstream/tasty/docs/dsl.md +211 -117
- package/docs/upstream/tasty/docs/getting-started.md +81 -24
- package/docs/upstream/tasty/docs/injector.md +41 -40
- package/docs/upstream/tasty/docs/methodology.md +46 -21
- package/docs/upstream/tasty/docs/migration-v3.md +3 -3
- package/docs/upstream/tasty/docs/pipeline.md +111 -116
- package/docs/upstream/tasty/docs/plugins.md +21 -41
- package/docs/upstream/tasty/docs/react-api.md +73 -10
- package/docs/upstream/tasty/docs/runtime-benchmarks.md +46 -46
- package/docs/upstream/tasty/docs/ssr.md +92 -66
- package/docs/upstream/tasty/docs/styles.md +37 -17
- package/docs/upstream/tasty/docs/tasty-static.md +175 -57
- package/docs/zh/getting-started.md +7 -5
- package/docs/zh/index.md +1 -1
- package/package.json +3 -3
|
@@ -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
|
|
|
@@ -213,9 +212,9 @@ useLayoutEffect(() => {
|
|
|
213
212
|
A `#name` token's value used to be rewritten into a configured color space, so
|
|
214
213
|
`#brand: '#ff8800'` declared `--brand-color: oklch(0.75 0.16 55)`. That existed
|
|
215
214
|
to serve the opacity suffix, which needed numeric channels to write an alpha
|
|
216
|
-
into. Opacity now
|
|
217
|
-
`
|
|
218
|
-
|
|
215
|
+
into. Opacity now mixes the token with `transparent` —
|
|
216
|
+
`color-mix(in oklab, var(--brand-color) 50%, transparent)` — which the browser
|
|
217
|
+
resolves against any color, so there is nothing left for the setting to decide.
|
|
219
218
|
|
|
220
219
|
A color is emitted exactly as authored:
|
|
221
220
|
|
|
@@ -236,9 +235,10 @@ background: oklch(from var(--brand-color) calc(l * 1.2) c h);
|
|
|
236
235
|
```
|
|
237
236
|
|
|
238
237
|
This works on any `<color>`, including the ones no build-time conversion could
|
|
239
|
-
have evaluated
|
|
240
|
-
|
|
241
|
-
|
|
238
|
+
have evaluated, and `oklch` is unbounded, so a wide-gamut color survives a round
|
|
239
|
+
trip a narrower space would clamp. Relative color syntax is Baseline 2024 (Chrome
|
|
240
|
+
125, Firefox 128, Safari 18), so the [opacity suffix](dsl.md#color-tokens--opacity)
|
|
241
|
+
uses the widely available `color-mix()` instead.
|
|
242
242
|
|
|
243
243
|
### Migrating off `colorSpace`
|
|
244
244
|
|
|
@@ -271,14 +271,15 @@ color: oklch(var(--brand-color-oklch) / 0.5);
|
|
|
271
271
|
background: oklch(var(--brand-color-oklch));
|
|
272
272
|
|
|
273
273
|
/* after */
|
|
274
|
-
color:
|
|
274
|
+
color: color-mix(in oklab, var(--brand-color) 50%, transparent);
|
|
275
275
|
background: var(--brand-color);
|
|
276
276
|
```
|
|
277
277
|
|
|
278
278
|
The replacement is strictly more capable — the companion could only be
|
|
279
|
-
decomposed for a color the engine could evaluate at build time, while
|
|
280
|
-
color
|
|
281
|
-
`light-dark()`, and a variable Tasty never defined.
|
|
279
|
+
decomposed for a color the engine could evaluate at build time, while
|
|
280
|
+
`color-mix()` works on every `<color>`, including a `color-mix()`, a
|
|
281
|
+
`light-dark()`, and a variable Tasty never defined. It is also what
|
|
282
|
+
`#brand.5` emits.
|
|
282
283
|
|
|
283
284
|
---
|
|
284
285
|
|
|
@@ -334,8 +335,7 @@ Tokens support state maps for responsive or theme-aware values:
|
|
|
334
335
|
```jsx
|
|
335
336
|
configure({
|
|
336
337
|
tokens: {
|
|
337
|
-
$
|
|
338
|
-
$radius: '6px',
|
|
338
|
+
'$sidebar-width': '280px',
|
|
339
339
|
'#primary': {
|
|
340
340
|
'': '#purple',
|
|
341
341
|
'@dark': '#light-purple',
|
|
@@ -350,9 +350,10 @@ configure({
|
|
|
350
350
|
|
|
351
351
|
- `$name` keys become `--name` CSS custom properties
|
|
352
352
|
- `#name` keys become `--name-color` custom properties
|
|
353
|
+
- 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
354
|
- 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
355
|
|
|
355
|
-
Tokens are
|
|
356
|
+
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
357
|
|
|
357
358
|
---
|
|
358
359
|
|
|
@@ -375,6 +376,17 @@ configure({
|
|
|
375
376
|
|
|
376
377
|
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
378
|
|
|
379
|
+
Tasty includes `'#clear': 'transparent'` as a default replace token, available
|
|
380
|
+
without calling `configure()`. It survives `resetConfig()`, and explicit
|
|
381
|
+
`replaceTokens` entries can override it. A replace token resolving to the literal
|
|
382
|
+
`transparent` ignores numeric and custom-property opacity suffixes:
|
|
383
|
+
`#clear`, `#clear.5`, and `#clear.$opacity` all emit `transparent`.
|
|
384
|
+
|
|
385
|
+
`#clear` no longer reads `--clear-color`, and Tasty no longer registers that
|
|
386
|
+
property by default. If you previously customized `#clear` through `tokens`,
|
|
387
|
+
local token definitions, or CSS, use a different color token or explicitly
|
|
388
|
+
override `replaceTokens['#clear']` instead.
|
|
389
|
+
|
|
378
390
|
See [Replace Tokens](dsl.md#replace-tokens) in the Style DSL reference.
|
|
379
391
|
|
|
380
392
|
---
|
|
@@ -471,7 +483,7 @@ See [Functions (`@function`)](dsl.md#functions-function) for inline usage inside
|
|
|
471
483
|
|
|
472
484
|
### Custom color functions
|
|
473
485
|
|
|
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
|
|
486
|
+
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
487
|
|
|
476
488
|
```ts
|
|
477
489
|
import { configure, createColorFunc } from '@tenphi/tasty';
|
|
@@ -482,8 +494,12 @@ const myColorPlugin = () => ({
|
|
|
482
494
|
functions: {
|
|
483
495
|
// Hand-written parse function:
|
|
484
496
|
mycolor: (groups) => {
|
|
485
|
-
|
|
486
|
-
|
|
497
|
+
// `mycolor(255 0 0 / .5)` and `#brand.5` both arrive as two parts
|
|
498
|
+
const [main, alpha] = groups[0].parts;
|
|
499
|
+
const [r, g, b] = main.all;
|
|
500
|
+
return alpha?.all.length
|
|
501
|
+
? `rgb(${r} ${g} ${b} / ${alpha.output})`
|
|
502
|
+
: `rgb(${r} ${g} ${b})`;
|
|
487
503
|
},
|
|
488
504
|
},
|
|
489
505
|
});
|
|
@@ -631,7 +647,7 @@ configure({
|
|
|
631
647
|
});
|
|
632
648
|
```
|
|
633
649
|
|
|
634
|
-
Each key is a CSS selector, and each value is a Tasty `Styles` object supporting the full style syntax including style properties, tokens, state maps, and selector-based sub-styling (e.g. `$: '> .app'` for elements outside React scope). Global styles are injected alongside `:root` tokens when the first style is rendered.
|
|
650
|
+
Each key is a CSS selector (a list such as `'.uni-checkbox, uni-checkbox'` styles every item), and each value is a Tasty `Styles` object supporting the full style syntax including style properties, tokens, state maps, `@keyframes`, and selector-based sub-styling (e.g. `$: '> .app'` for elements outside React scope). Global styles are injected alongside `:root` tokens when the first style is rendered.
|
|
635
651
|
|
|
636
652
|
Global styles are automatically emitted in all delivery modes: client rendering, server rendering, and build-time extraction with the Babel plugin. Plugins can also provide `globalStyles`; they are merged per selector with config global styles (config wins on conflict).
|
|
637
653
|
|
|
@@ -690,10 +706,16 @@ Override or extend the built-in style property handlers. A handler definition ca
|
|
|
690
706
|
| Single dep | `['styleName', handler]` | Triggered by the specified style property |
|
|
691
707
|
| Multi dep | `[['dep1', 'dep2', ...], handler]` | Triggered by any of the listed properties; receives all of them |
|
|
692
708
|
|
|
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`.
|
|
709
|
+
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
710
|
|
|
695
711
|
```jsx
|
|
696
|
-
import {
|
|
712
|
+
import {
|
|
713
|
+
configure,
|
|
714
|
+
defineHandler,
|
|
715
|
+
parseColor,
|
|
716
|
+
parseStyle,
|
|
717
|
+
styleHandlers,
|
|
718
|
+
} from '@tenphi/tasty';
|
|
697
719
|
|
|
698
720
|
configure({
|
|
699
721
|
handlers: {
|
|
@@ -717,6 +739,10 @@ configure({
|
|
|
717
739
|
'backgroundPosition',
|
|
718
740
|
'backgroundSize',
|
|
719
741
|
'backgroundRepeat',
|
|
742
|
+
'backgroundAttachment',
|
|
743
|
+
'backgroundOrigin',
|
|
744
|
+
'backgroundClip',
|
|
745
|
+
'background',
|
|
720
746
|
],
|
|
721
747
|
(props) => {
|
|
722
748
|
if (
|
|
@@ -729,12 +755,18 @@ configure({
|
|
|
729
755
|
},
|
|
730
756
|
),
|
|
731
757
|
|
|
732
|
-
// Multi dep —
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
758
|
+
// Multi dep — triggered by either name, receives both
|
|
759
|
+
stripe: defineHandler(
|
|
760
|
+
['stripe', 'stripeColor'],
|
|
761
|
+
({ stripe, stripeColor }) => {
|
|
762
|
+
if (!stripe) return;
|
|
763
|
+
const width = parseStyle(String(stripe)).output;
|
|
764
|
+
const color = stripeColor
|
|
765
|
+
? parseColor(String(stripeColor)).color
|
|
766
|
+
: 'currentcolor';
|
|
767
|
+
return { 'border-left': `${width} solid ${color}` };
|
|
768
|
+
},
|
|
769
|
+
),
|
|
738
770
|
},
|
|
739
771
|
});
|
|
740
772
|
```
|
|
@@ -745,6 +777,7 @@ A handler returns a `CSSMap`, an array of them, or nothing:
|
|
|
745
777
|
|
|
746
778
|
- 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
779
|
- Values are stringified, so numbers are fine (`{ '-webkit-line-clamp': 3 }`).
|
|
780
|
+
- 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
781
|
- 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
782
|
- Return an array of maps to emit several declaration sets, each with its own `$`.
|
|
750
783
|
|
|
@@ -756,12 +789,17 @@ Built-in handlers are shared across several style names. Registering a handler f
|
|
|
756
789
|
|
|
757
790
|
The shared groups worth knowing:
|
|
758
791
|
|
|
759
|
-
| Registering a handler for
|
|
760
|
-
|
|
|
761
|
-
| `fill`
|
|
762
|
-
| `display`
|
|
763
|
-
| `
|
|
764
|
-
| `
|
|
792
|
+
| Registering a handler for | Also takes over |
|
|
793
|
+
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
794
|
+
| `fill` | `backgroundColor`, `image`, `backgroundImage`, `backgroundPosition`, `backgroundSize`, `backgroundRepeat`, `backgroundAttachment`, `backgroundOrigin`, `backgroundClip`, `background` |
|
|
795
|
+
| `display` | `hide`, `whiteSpace`, `textOverflow`, `flow`, `gap` |
|
|
796
|
+
| `flow` | `gap` |
|
|
797
|
+
| `preset` | `font`, `fontFamily`, `fontSize`, `fontWeight`, `fontStyle`, `lineHeight`, `letterSpacing`, `textTransform` |
|
|
798
|
+
| `padding` / `margin` | their `Top`/`Right`/`Bottom`/`Left` longhands (`paddingTop`, `marginLeft`, …) |
|
|
799
|
+
| `inset` | `top`, `right`, `bottom`, `left` |
|
|
800
|
+
| `width` / `height` | `minWidth`/`maxWidth`, `minHeight`/`maxHeight` |
|
|
801
|
+
|
|
802
|
+
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
803
|
|
|
766
804
|
Either declare the whole group and delegate the rest to `styleHandlers.*`, or pick a name that isn't shared.
|
|
767
805
|
|
|
@@ -822,18 +860,98 @@ Each name costs one property check per render of every component, and the effect
|
|
|
822
860
|
|
|
823
861
|
---
|
|
824
862
|
|
|
825
|
-
##
|
|
863
|
+
## TypeScript
|
|
826
864
|
|
|
827
|
-
|
|
865
|
+
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.
|
|
866
|
+
|
|
867
|
+
### Component props
|
|
868
|
+
|
|
869
|
+
| In `tasty({ … })` | Adds the props |
|
|
870
|
+
| --------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
871
|
+
| `styleProps: ['gap', 'padding'] as const` | `gap`, `padding`, each typed like the style: a value or a state map |
|
|
872
|
+
| `modProps: { isLoading: Boolean, size: ['sm', 'md', 'lg'] as const }` | `isLoading?: boolean`, `size?: 'sm' \| 'md' \| 'lg'` (`String` gives `string`, `Number` gives `number`) |
|
|
873
|
+
| `modProps: ['isLoading'] as const` | `isLoading?: ModValue` |
|
|
874
|
+
| `tokenProps: ['progress', 'accentColor'] as const` | `progress?: TokenValue`, `accentColor?: TokenValue` |
|
|
875
|
+
| `variants: { default: {…}, danger: {…} }` | `variant?: 'default' \| 'danger'` |
|
|
876
|
+
| `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` |
|
|
877
|
+
| `as: SomeComponent` | `SomeComponent`'s own props, required ones still required |
|
|
878
|
+
|
|
879
|
+
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`.
|
|
880
|
+
|
|
881
|
+
`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).
|
|
882
|
+
|
|
883
|
+
### Registering your names and extensions
|
|
884
|
+
|
|
885
|
+
```ts
|
|
886
|
+
// tasty.d.ts — keep the import: it makes this file a module, so the block
|
|
887
|
+
// below extends '@tenphi/tasty' instead of replacing it.
|
|
888
|
+
import '@tenphi/tasty';
|
|
828
889
|
|
|
829
|
-
```tsx
|
|
830
|
-
// tasty.d.ts
|
|
831
890
|
declare module '@tenphi/tasty' {
|
|
891
|
+
interface TastyNamedColors {
|
|
892
|
+
surface: true;
|
|
893
|
+
text: true;
|
|
894
|
+
primary: true;
|
|
895
|
+
}
|
|
896
|
+
interface TastyPresetNames {
|
|
897
|
+
h1: true;
|
|
898
|
+
t2: true;
|
|
899
|
+
}
|
|
900
|
+
interface TastyThemeNames {
|
|
901
|
+
danger: true;
|
|
902
|
+
success: true;
|
|
903
|
+
}
|
|
832
904
|
interface StylesInterface {
|
|
833
|
-
|
|
834
|
-
|
|
905
|
+
stripe?: string;
|
|
906
|
+
}
|
|
907
|
+
interface TastyCustomProps {
|
|
908
|
+
glaze: 'soft' | 'strong' | { tone: string; intensity?: number };
|
|
909
|
+
}
|
|
910
|
+
interface TastyBaseStylePropNames {
|
|
911
|
+
radius: true;
|
|
912
|
+
stripe: true;
|
|
835
913
|
}
|
|
836
914
|
}
|
|
837
915
|
```
|
|
838
916
|
|
|
917
|
+
| Interface | Register | What it types |
|
|
918
|
+
| ------------------------- | -------------------------------------------------------------------- | ------------------------------------------------------------------------ |
|
|
919
|
+
| `TastyNamedColors` | Color token names from `tokens` and `replaceTokens`, without the `#` | Suggestions for `#name` and `#name.50` in `fill`, `color`, and `svgFill` |
|
|
920
|
+
| `TastyPresetNames` | `presets` names | The `preset` style and prop |
|
|
921
|
+
| `TastyThemeNames` | Theme names | The `theme` prop (rendered as `data-theme`) |
|
|
922
|
+
| `StylesInterface` | Style properties added with `handlers` | Their value type in `styles`, `styleProps`, and base style props |
|
|
923
|
+
| `TastyCustomProps` | Props added with `propHandlers` | An optional prop on every `tasty()` component (not on sub-elements) |
|
|
924
|
+
| `TastyBaseStylePropNames` | Names passed to `baseStyleProps` | A prop on every component, typed like the style it names |
|
|
925
|
+
|
|
926
|
+
- The first three add autocomplete, not checking: any other string still type-checks (`preset: 'h9'` compiles). The ESLint plugin is what reports unknown names.
|
|
927
|
+
- In `StylesInterface`, declare the plain value type (`stripe?: string`). Tasty adds state-map support itself, as it does for built-in styles.
|
|
928
|
+
- 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.
|
|
929
|
+
- `TastyCustomProps` types the component prop, not the handler. A prop handler receives `Record<string, unknown>`, so cast inside it (`rest.styles as Styles`).
|
|
930
|
+
- State keys, `$` value tokens, units, and recipe names have no TypeScript types. The ESLint plugin checks them against `tasty.config.ts`.
|
|
931
|
+
|
|
932
|
+
### `tasty.config.ts`
|
|
933
|
+
|
|
934
|
+
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`).
|
|
935
|
+
|
|
936
|
+
```ts
|
|
937
|
+
// tasty.config.ts
|
|
938
|
+
import type { TastyExtensionConfig } from '@tenphi/tasty';
|
|
939
|
+
import type { TastyValidationConfig } from '@tenphi/eslint-plugin-tasty';
|
|
940
|
+
|
|
941
|
+
export default {
|
|
942
|
+
tokens: ['#surface', '#text', '#primary', '$sidebar-width'],
|
|
943
|
+
units: ['cols'],
|
|
944
|
+
states: ['@mobile', '@dark'],
|
|
945
|
+
presets: ['h1', 't2'],
|
|
946
|
+
recipes: ['card', 'elevated'],
|
|
947
|
+
styles: ['stripe'],
|
|
948
|
+
importSources: ['@my-org/ui'],
|
|
949
|
+
presetDescriptions: { h1: 'Heading 1 (32px, bold)' },
|
|
950
|
+
} satisfies TastyExtensionConfig & TastyValidationConfig;
|
|
951
|
+
```
|
|
952
|
+
|
|
953
|
+
`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.
|
|
954
|
+
|
|
955
|
+
Keep it in step with `configure()`: token names with their `#` or `$`, custom units, parse-function names, state aliases, presets, recipes, and custom style names.
|
|
956
|
+
|
|
839
957
|
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.
|
|
@@ -78,7 +78,7 @@ Retrieves CSS text for a given target. Logs the result with rule count and size.
|
|
|
78
78
|
|---|---|
|
|
79
79
|
| `'all'` | All tasty CSS (component + global + raw) |
|
|
80
80
|
| `'active'` | CSS for classes currently in the DOM |
|
|
81
|
-
| `'unused'` | CSS for
|
|
81
|
+
| `'unused'` | CSS for classes `gc({ force: true })` would delete now — not rendered, not pinned, past the `gc.grace` window |
|
|
82
82
|
| `'global'` | Only global CSS (from `injectGlobal`) |
|
|
83
83
|
| `'page'` | All CSS on the page (including non-tasty) |
|
|
84
84
|
| `'t3a5f'` | CSS for a specific tasty class (class names are `t` + base36 hash) |
|
|
@@ -154,7 +154,8 @@ One-shot overview of the entire Tasty CSS state. Logs a compact report.
|
|
|
154
154
|
```typescript
|
|
155
155
|
interface Summary {
|
|
156
156
|
activeClasses: string[];
|
|
157
|
-
unusedClasses: string[];
|
|
157
|
+
unusedClasses: string[]; // classes gc({ force: true }) would delete now
|
|
158
|
+
hotClasses: string[]; // not rendered, but pinned or inside gc.grace — not yet collectable
|
|
158
159
|
totalStyledClasses: string[];
|
|
159
160
|
|
|
160
161
|
activeCSSSize: number;
|