@tenphi/tasty 0.2.0 → 0.3.0

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 CHANGED
@@ -5,7 +5,8 @@
5
5
  <h1 align="center">Tasty</h1>
6
6
 
7
7
  <p align="center">
8
- A design-system-integrated styling system and DSL for concise, state-aware UI styling
8
+ <strong>The styling engine built for design systems.</strong><br>
9
+ Deterministic CSS generation. State-aware DSL. Zero specificity conflicts. Ever.
9
10
  </p>
10
11
 
11
12
  <p align="center">
@@ -16,21 +17,21 @@
16
17
 
17
18
  ---
18
19
 
19
- Tasty is a powerful CSS-in-JS styling system for React that combines declarative state-aware styling with design system integration. It provides a concise DSL for creating maintainable, themeable components with built-in support for responsive design, dark mode, container queries, and more.
20
+ Most CSS-in-JS libraries generate CSS. Tasty generates **mutually exclusive CSS** — for any combination of states, exactly one rule matches per property. No cascade conflicts, no specificity wars, no `!important` hacks. Components compose and extend without breaking each other. That's the foundation everything else is built on.
20
21
 
21
- ## Features
22
+ On top of that foundation, Tasty gives you a concise, CSS-like DSL with design tokens, custom units, responsive states, container queries, dark mode, sub-element styling, and zero-runtime extraction — all in one coherent system that scales from a single component to an enterprise design system.
22
23
 
23
- - **Declarative state-aware styling** — style objects with state keys (`hovered`, `disabled`, `@media`, `@root`, etc.)
24
- - **Design token integration** — color tokens (`#purple`), custom units (`2x`, `1r`), typography presets
25
- - **Sub-element styling** — style inner elements via capitalized keys with `data-element` attributes
26
- - **Advanced state mapping** — media queries, container queries, root states, supports queries with boolean logic
27
- - **Zero-runtime mode** — Babel plugin extracts CSS at build time for static sites
28
- - **Plugin system** — extensible with custom color functions (OKHSL, etc.)
29
- - **React hooks** — `useStyles`, `useGlobalStyles`, `useRawCSS` for programmatic style injection
30
- - **Style extension** — compose and extend styled components with proper merge semantics
31
- - **Recipes** — named style bundles for reusable patterns
32
- - **TypeScript-first** — full type definitions with module augmentation support
33
- - **Tree-shakeable ESM** — unbundled output with `sideEffects: false`
24
+ ## Why Tasty
25
+
26
+ - **Deterministic at any scale** — Exclusive selector generation eliminates the entire class of cascade/specificity bugs. Every state combination resolves to exactly one CSS rule per property. Refactor freely.
27
+ - **DSL that feels like CSS** — Property names you already know (`padding`, `color`, `display`) with syntax sugar that removes boilerplate. Learn the DSL in minutes, not days.
28
+ - **Design-system native** — Color tokens (`#primary`), spacing units (`2x`), typography presets (`h1`, `t2`), border radius (`1r`), and recipes are first-class primitives, not afterthoughts.
29
+ - **Full modern CSS coverage** — Media queries, container queries, `@supports`, `:has()`, `@starting-style`, `@property`, keyframes, boolean state logic with `&`, `|`, `!` operators. If CSS can do it, Tasty can express it — concisely.
30
+ - **Runtime or zero-runtime — your call** — Use `tasty()` for dynamic React components with runtime injection, or `tastyStatic()` with the Babel plugin for zero-runtime CSS extraction. Same DSL, same tokens, same output.
31
+ - **Only generate what is used** — In runtime mode, Tasty injects CSS on demand for mounted components/variants, so your app avoids shipping style rules for UI states that are never rendered.
32
+ - **Runtime performance that holds at scale** — The runtime path is tested against enterprise-scale applications and tuned with multi-level caching, chunk-level style reuse, style garbage collection, and a dedicated injector.
33
+ - **Composable and extensible by design** — Extend any component's styles with proper merge semantics, and evolve built-in behavior through configuration and plugins.
34
+ - **TypeScript-first** — Full type definitions, module augmentation for custom properties, and autocomplete for tokens, presets, and themes.
34
35
 
35
36
  ## Installation
36
37
 
@@ -38,17 +39,9 @@ Tasty is a powerful CSS-in-JS styling system for React that combines declarative
38
39
  pnpm add @tenphi/tasty
39
40
  ```
40
41
 
41
- ```bash
42
- npm install @tenphi/tasty
43
- ```
44
-
45
- ```bash
46
- yarn add @tenphi/tasty
47
- ```
48
-
49
42
  ## Quick Start
50
43
 
51
- ### Creating Styled Components
44
+ ### Create a styled component
52
45
 
53
46
  ```tsx
54
47
  import { tasty } from '@tenphi/tasty';
@@ -56,50 +49,68 @@ import { tasty } from '@tenphi/tasty';
56
49
  const Card = tasty({
57
50
  as: 'div',
58
51
  styles: {
52
+ display: 'flex',
53
+ flow: 'column',
59
54
  padding: '4x',
60
- fill: '#white',
61
- border: true,
62
- radius: true,
55
+ gap: '2x',
56
+ fill: '#surface',
57
+ border: '#border bottom',
58
+ radius: '1r',
63
59
  },
64
60
  });
65
61
 
62
+ // Just a React component
66
63
  <Card>Hello World</Card>
67
64
  ```
68
65
 
69
- ### State-Based Styling
66
+ Every value maps to CSS you'd recognize — but with tokens and units that keep your design system consistent by default.
67
+
68
+ ### Add state-driven styles
70
69
 
71
70
  ```tsx
72
- const InteractiveCard = tasty({
71
+ const Button = tasty({
72
+ as: 'button',
73
73
  styles: {
74
+ padding: '1.5x 3x',
74
75
  fill: {
75
- '': '#white',
76
- 'hovered': '#gray.05',
77
- 'pressed': '#gray.10',
78
- '@media(w < 768px)': '#surface',
76
+ '': '#primary',
77
+ ':hover': '#primary-hover',
78
+ ':active': '#primary-pressed',
79
+ '[disabled]': '#surface',
79
80
  },
80
- padding: {
81
- '': '4x',
82
- '@media(w < 768px)': '2x',
81
+ color: {
82
+ '': '#on-primary',
83
+ '[disabled]': '#text.40',
83
84
  },
85
+ cursor: {
86
+ '': 'pointer',
87
+ '[disabled]': 'not-allowed',
88
+ },
89
+ transition: 'theme',
84
90
  },
85
91
  });
86
92
  ```
87
93
 
88
- ### Extending Components
94
+ State keys support pseudo-classes first (`:hover`, `:active`), then modifiers (`theme=danger`), attributes (`[disabled]`), media/container queries, root states, and more. Tasty compiles them into exclusive selectors automatically.
95
+
96
+ ### Extend any component
89
97
 
90
98
  ```tsx
91
99
  import { Button } from 'my-ui-lib';
92
100
 
93
- const PrimaryButton = tasty(Button, {
101
+ const DangerButton = tasty(Button, {
94
102
  styles: {
95
- fill: '#purple',
96
- color: '#white',
97
- padding: '2x 4x',
103
+ fill: {
104
+ '': '#danger',
105
+ ':hover': '#danger-hover',
106
+ },
98
107
  },
99
108
  });
100
109
  ```
101
110
 
102
- ### Configuration
111
+ Child styles merge with parent styles intelligently — state maps can extend or replace parent states per-property.
112
+
113
+ ### Configure once, use everywhere
103
114
 
104
115
  ```tsx
105
116
  import { configure } from '@tenphi/tasty';
@@ -107,154 +118,346 @@ import { configure } from '@tenphi/tasty';
107
118
  configure({
108
119
  states: {
109
120
  '@mobile': '@media(w < 768px)',
110
- '@dark': '@root(theme=dark)',
121
+ '@tablet': '@media(w < 1024px)',
122
+ '@dark': '@root(schema=dark) | @media(prefers-color-scheme: dark)',
111
123
  },
112
124
  recipes: {
113
- card: {
114
- padding: '4x',
115
- fill: '#surface',
116
- radius: '1r',
117
- border: true,
118
- },
125
+ card: { padding: '4x', fill: '#surface', radius: '1r', border: true },
119
126
  },
120
127
  });
121
128
  ```
122
129
 
123
- ## Entry Points
130
+ Predefined states turn complex selector logic into single tokens. Use `@mobile` instead of writing media query expressions in every component.
124
131
 
125
- | Import | Description | Platform |
126
- |--------|-------------|----------|
127
- | `@tenphi/tasty` | Runtime style engine | Browser |
128
- | `@tenphi/tasty/static` | Build-time static style generation | Browser |
129
- | `@tenphi/tasty/babel-plugin` | Babel plugin for zero-runtime | Node |
130
- | `@tenphi/tasty/zero` | Programmatic extraction API | Node |
131
- | `@tenphi/tasty/next` | Next.js integration wrapper | Node |
132
+ ## How It Actually Works
133
+
134
+ This is the core idea that makes everything else possible.
132
135
 
133
- ## Core Concepts
136
+ Traditional CSS uses the cascade to resolve conflicts: when multiple selectors match, the one with the highest specificity wins, or — if specificity is equal — the last one in source order wins. This makes styles inherently fragile. Reordering imports, adding a new media query, or composing components from different libraries can silently break styling.
134
137
 
135
- ### Design Tokens
138
+ Tasty takes a fundamentally different approach: **every state mapping compiles into selectors that are guaranteed to never overlap.**
136
139
 
137
140
  ```tsx
138
- const TokenCard = tasty({
141
+ const Text = tasty({
139
142
  styles: {
140
- fill: '#surface', // Color token → var(--surface-color)
141
- color: '#text', // Color token
142
- padding: '2x', // Gap multiplier → calc(var(--gap) * 2)
143
- radius: '1r', // Border radius → var(--radius)
144
- border: '1bw solid #border',
143
+ color: {
144
+ '': '#text',
145
+ '@dark': '#text-on-dark',
146
+ },
147
+ padding: {
148
+ '': '4x',
149
+ '@mobile': '2x',
150
+ },
145
151
  },
146
152
  });
147
153
  ```
148
154
 
155
+ If `@dark` expands to `@root(schema=dark) | @media(prefers-color-scheme: dark)`, Tasty generates:
156
+
157
+ ```css
158
+ /* Explicit dark mode */
159
+ :root[data-schema="dark"] .t0.t0 {
160
+ color: var(--text-on-dark-color);
161
+ }
162
+
163
+ /* OS dark preference, no explicit override */
164
+ @media (prefers-color-scheme: dark) {
165
+ :root:not([data-schema="dark"]) .t0.t0 {
166
+ color: var(--text-on-dark-color);
167
+ }
168
+ }
169
+
170
+ /* Light mode — neither condition */
171
+ @media (not (prefers-color-scheme: dark)) {
172
+ :root:not([data-schema="dark"]) .t0.t0 {
173
+ color: var(--text-color);
174
+ }
175
+ }
176
+ ```
177
+
178
+ Every rule is guarded by the negation of all higher-priority rules. No two rules can ever match simultaneously. No specificity arithmetic. No source-order dependence. Components compose and extend without ever colliding.
179
+
180
+ ## Capabilities
181
+
182
+ ### Design Tokens and Custom Units
183
+
184
+ Tokens are first-class. Colors use `#name` syntax. Spacing, radius, and border width use multiplier units tied to CSS custom properties:
185
+
186
+ ```tsx
187
+ fill: '#surface', // → var(--surface-color)
188
+ color: '#text.80', // → 80% opacity text token
189
+ padding: '2x', // → calc(var(--gap) * 2)
190
+ radius: '1r', // → var(--radius)
191
+ border: '1bw solid #border',
192
+ ```
193
+
194
+ | Unit | Maps to | Example |
195
+ |------|---------|---------|
196
+ | `x` | `--gap` multiplier | `2x` → `calc(var(--gap) * 2)` |
197
+ | `r` | `--radius` multiplier | `1r` → `var(--radius)` |
198
+ | `bw` | `--border-width` multiplier | `1bw` → `var(--border-width)` |
199
+ | `ow` | `--outline-width` multiplier | `1ow` → `var(--outline-width)` |
200
+ | `cr` | `--card-radius` multiplier | `1cr` → `var(--card-radius)` |
201
+
202
+ Define your own units via `configure({ units: { ... } })`.
203
+
204
+ ### State System
205
+
206
+ Every style property accepts a state mapping object. Keys can be combined with boolean logic:
207
+
208
+ | State type | Syntax | CSS output |
209
+ |------------|--------|------------|
210
+ | Data attribute (boolean modifier) | `disabled` | `[data-disabled]` |
211
+ | Data attribute (value modifier) | `theme=danger` | `[data-theme="danger"]` |
212
+ | Pseudo-class | `:hover` | `:hover` |
213
+ | Attribute selector | `[role="tab"]` | `[role="tab"]` |
214
+ | Class selector (supported) | `.is-active` | `.is-active` |
215
+ | Media query | `@media(w < 768px)` | `@media (width < 768px)` |
216
+ | Container query | `@(panel, w >= 300px)` | `@container panel (width >= 300px)` |
217
+ | Root state | `@root(theme=dark)` | `:root[data-theme="dark"]` |
218
+ | Parent state | `@parent(theme=danger)` | `:is([data-theme="danger"] *)` |
219
+ | Feature query | `@supports(display: grid)` | `@supports (display: grid)` |
220
+ | Entry animation | `@starting` | `@starting-style` |
221
+
222
+ Combine with `&` (AND), `|` (OR), `!` (NOT):
223
+
224
+ ```tsx
225
+ fill: {
226
+ '': '#surface',
227
+ 'theme=danger & :hover': '#danger-hover',
228
+ '[aria-selected="true"]': '#accent-subtle',
229
+ }
230
+ ```
231
+
149
232
  ### Sub-Element Styling
150
233
 
234
+ Style inner elements from the parent component definition. No extra components, no CSS leakage:
235
+
151
236
  ```tsx
152
237
  const Card = tasty({
153
238
  styles: {
154
239
  padding: '4x',
155
240
  Title: { preset: 'h3', color: '#primary' },
156
- Content: { color: '#text' },
157
- },
158
- elements: {
159
- Title: 'h2',
160
- Content: 'div',
241
+ Content: { color: '#text', preset: 't2' },
161
242
  },
243
+ elements: { Title: 'h2', Content: 'div' },
162
244
  });
163
245
 
164
246
  <Card>
165
- <Card.Title>Title</Card.Title>
166
- <Card.Content>Content</Card.Content>
247
+ <Card.Title>Heading</Card.Title>
248
+ <Card.Content>Body text</Card.Content>
167
249
  </Card>
168
250
  ```
169
251
 
170
- ### Hooks
252
+ Sub-elements use `data-element` attributes — no extra class names, no naming conventions.
253
+
254
+ By default, sub-elements participate in the same state context as the root component. That means mappings like `:hover`, `theme=danger`, `[role="button"]`, and other keys are evaluated as one unified block, which keeps styling logic predictable across the whole markup tree.
255
+
256
+ Use `@own(...)` when a sub-element should react to its own state instead of the root state context.
257
+
258
+ Class selectors are also supported, but modifiers/pseudo-classes are usually the better default in design-system code.
259
+
260
+ Use the sub-element selector `$` when you need precise descendant targeting to avoid leakage in deeply nested component trees.
261
+
262
+ ### Variants
263
+
264
+ Variants are designed to keep single-component CSS lean. Instead of generating dozens of static button classes up front, define all versions once and let runtime usage decide what CSS is actually emitted.
265
+
266
+ ```tsx
267
+ const Button = tasty({
268
+ styles: { padding: '2x 4x', radius: '1r' },
269
+ variants: {
270
+ default: { fill: '#primary', color: '#on-primary' },
271
+ danger: { fill: '#danger', color: '#on-danger' },
272
+ outline: { fill: 'transparent', border: '1bw solid #primary' },
273
+ },
274
+ });
275
+
276
+ <Button variant="danger">Delete</Button>
277
+ ```
278
+
279
+ ### Recipes
280
+
281
+ Recipes are predefined style sets that work like composable styling classes for Tasty. They can be pre-applied or post-applied to current styles, which lets you add reusable state logic while still allowing local style overrides.
171
282
 
172
283
  ```tsx
173
- import { useStyles, useGlobalStyles } from '@tenphi/tasty';
284
+ configure({
285
+ recipes: {
286
+ card: { padding: '4x', fill: '#surface', radius: '1r', border: true },
287
+ elevated: { shadow: '0 2x 4x #shadow' },
288
+ },
289
+ });
174
290
 
175
- function MyComponent() {
176
- const { className } = useStyles({
177
- padding: '2x',
178
- fill: '#surface',
179
- });
291
+ const ProfileCard = tasty({
292
+ styles: {
293
+ recipe: 'card elevated',
294
+ color: '#text',
295
+ },
296
+ });
297
+ ```
180
298
 
181
- useGlobalStyles('.card', {
182
- border: '1bw solid #border',
183
- radius: '1r',
184
- });
299
+ Use `/` to post-apply recipes after local styles when you need recipe states/styles to win the final merge order. Use `none` to skip base recipes: `recipe: 'none / disabled'`.
300
+
301
+ ### Keyframes and `@property`
302
+
303
+ Modern CSS features are natively supported:
304
+
305
+ Color tokens are automatically registered as typed properties (`<color>`), so token-based transitions work without extra setup.
306
+
307
+ ```tsx
308
+ const Pulse = tasty({
309
+ styles: {
310
+ '@properties': {
311
+ '$pulse-scale': {
312
+ syntax: '<number>',
313
+ inherits: false,
314
+ initialValue: 1,
315
+ },
316
+ },
317
+ animation: 'pulse 2s infinite',
318
+ transform: 'scale($pulse-scale)',
319
+ '@keyframes': {
320
+ pulse: {
321
+ '0%, 100%': { '$pulse-scale': 1 },
322
+ '50%': { '$pulse-scale': 1.05 },
323
+ },
324
+ },
325
+ },
326
+ });
327
+ ```
328
+
329
+ ### React Hooks
330
+
331
+ For cases where you don't need a full component:
332
+
333
+ ```tsx
334
+ import { useStyles, useGlobalStyles, useRawCSS } from '@tenphi/tasty';
335
+
336
+ function App() {
337
+ const { className } = useStyles({ padding: '2x', fill: '#surface' });
338
+ useGlobalStyles(':root', { '#primary': 'purple', '$gap': '8px' });
339
+ useRawCSS('body { margin: 0; }');
185
340
 
186
- return <div className={className}>Styled</div>;
341
+ return <main className={className}>...</main>;
187
342
  }
188
343
  ```
189
344
 
190
345
  ### Zero-Runtime Mode
191
346
 
347
+ Extract all CSS at build time. Zero JavaScript overhead in production:
348
+
192
349
  ```tsx
193
350
  import { tastyStatic } from '@tenphi/tasty/static';
194
351
 
195
- const button = tastyStatic({
196
- display: 'inline-flex',
197
- padding: '2x 4x',
198
- fill: '#purple',
199
- color: '#white',
352
+ const card = tastyStatic({
353
+ padding: '4x',
354
+ fill: '#surface',
355
+ radius: '1r',
356
+ color: { '': '#text', '@dark': '#text-on-dark' },
200
357
  });
201
358
 
202
- <button className={button}>Click me</button>
359
+ // card is a CSS class name string
360
+ <div className={card}>Static styles, zero runtime</div>
203
361
  ```
204
362
 
205
363
  Configure the Babel plugin:
206
364
 
207
365
  ```js
208
- // babel.config.js
209
366
  module.exports = {
210
367
  plugins: [
211
- ['@tenphi/tasty/babel-plugin', { output: 'public/tasty.css' }],
368
+ ['@tenphi/tasty/babel-plugin', {
369
+ output: 'public/tasty.css',
370
+ config: {
371
+ states: { '@dark': '@root(theme=dark)' },
372
+ },
373
+ }],
212
374
  ],
213
375
  };
214
376
  ```
215
377
 
216
- ## Built-in Units
378
+ ### `tasty` vs `tastyStatic`
379
+
380
+ | | `tasty` (runtime) | `tastyStatic` (zero-runtime) |
381
+ |---|---|---|
382
+ | **Output** | React component | CSS class name |
383
+ | **CSS injection** | Runtime `<style>` tags | Build-time extraction |
384
+ | **Runtime cost** | Style generation on mount | None |
385
+ | **Generated CSS scope** | Only styles/variants used at runtime | All extracted static styles at build time |
386
+ | **Dynamic values** | Fully supported | Via CSS custom properties |
387
+ | **Sub-elements** | Built-in (`<C.Title>`) | Manual (`data-element`) |
388
+ | **Variants** | Built-in (`variants` option) | Separate static styles |
389
+ | **Framework** | React | Any (requires Babel) |
390
+ | **Best for** | Interactive apps, design systems | Static sites, SSG, landing pages |
391
+
392
+ Both share the same DSL, tokens, units, state mappings, and recipes.
393
+
394
+ ### Runtime Performance
395
+
396
+ If you choose the runtime approach, performance is usually a non-issue in practice:
397
+
398
+ - CSS is generated and injected only when styles are actually used.
399
+ - Multi-level caching avoids repeated parsing and style recomputation.
400
+ - Styles are split into reusable chunks and applied as multiple class names, so matching chunks can be reused across components instead of re-injected.
401
+ - Style normalization guarantees equivalent style input resolves to the same chunks, improving deduplication hit rates.
402
+ - A style garbage collector removes unused styles/chunks over time.
403
+ - A dedicated style injector minimizes DOM/style-tag overhead.
404
+ - This approach is validated in enterprise-scale apps where runtime styling overhead is not noticeable in normal UI flows.
405
+
406
+ ## Entry Points
407
+
408
+ | Import | Description | Platform |
409
+ |--------|-------------|----------|
410
+ | `@tenphi/tasty` | Runtime style engine | Browser |
411
+ | `@tenphi/tasty/static` | Zero-runtime static styles | Browser |
412
+ | `@tenphi/tasty/babel-plugin` | Babel plugin for CSS extraction | Node |
413
+ | `@tenphi/tasty/zero` | Programmatic extraction API | Node |
414
+ | `@tenphi/tasty/next` | Next.js integration | Node |
415
+
416
+ ## Ecosystem
217
417
 
218
- | Unit | Description | Example | CSS Output |
219
- |------|-------------|---------|------------|
220
- | `x` | Gap multiplier | `2x` | `calc(var(--gap) * 2)` |
221
- | `r` | Border radius | `1r` | `var(--radius)` |
222
- | `cr` | Card border radius | `1cr` | `var(--card-radius)` |
223
- | `bw` | Border width | `2bw` | `calc(var(--border-width) * 2)` |
224
- | `ow` | Outline width | `1ow` | `var(--outline-width)` |
225
- | `fs` | Font size | `1fs` | `var(--font-size)` |
226
- | `lh` | Line height | `1lh` | `var(--line-height)` |
227
- | `sf` | Stable fraction | `1sf` | `minmax(0, 1fr)` |
418
+ Tasty is the core of a production-ready styling platform. These companion tools complete the picture:
228
419
 
229
- ## `tasty` vs `tastyStatic`
420
+ ### [ESLint Plugin](https://github.com/tenphi/eslint-plugin-tasty)
230
421
 
231
- Tasty ships two styling APIs with different trade-offs. Pick the one that fits your project:
422
+ `@tenphi/eslint-plugin-tasty` 27 lint rules that validate style property names, value syntax, token existence, state keys, and enforce best practices. Catch typos and invalid styles at lint time, not at runtime.
232
423
 
233
- | | `tasty` (runtime) | `tastyStatic` (zero-runtime) |
234
- |---|---|---|
235
- | **Framework** | React only | Framework-agnostic (requires Babel) |
236
- | **Import** | `@tenphi/tasty` | `@tenphi/tasty/static` |
237
- | **Output** | React component | CSS class name (string) |
238
- | **CSS injection** | At runtime via `<style>` tags | At build time via Babel plugin |
239
- | **Runtime overhead** | Style generation + injection on mount | None — CSS is pre-extracted |
240
- | **Requires Babel plugin** | No | Yes (`@tenphi/tasty/babel-plugin`) |
241
- | **Component creation** | `tasty({ as, styles, ... })` | `tastyStatic({ ... })` returns a class |
242
- | **Extending components** | `tasty(BaseComponent, { styles })` | `tastyStatic(baseStyle, { ... })` |
243
- | **Global / selector styles** | `useGlobalStyles(selector, styles)` | `tastyStatic(selector, styles)` |
244
- | **Style props at runtime** | Yes `styleProps`, `styles`, `mods` | No all values must be static |
245
- | **Dynamic values** | Fully supported | Only via CSS custom properties |
246
- | **Sub-elements** | Built-in (`elements` + `<C.Title>`) | Manual (use `data-element` + CSS) |
247
- | **Variants** | Built-in (`variants` option) | Manual (create separate static styles) |
248
- | **Tokens** | `tokens` prop → inline CSS vars | `processTokens()` helper |
249
- | **Design tokens & units** | Full support (`#color`, `2x`, `1r`) | Full support (`#color`, `2x`, `1r`) |
250
- | **State mappings** | Full support (modifiers, media, etc.) | Full support (modifiers, media, etc.) |
251
- | **Recipes** | Supported via `configure()` | Supported via Babel plugin config |
252
- | **Best for** | Interactive React apps, design systems | Static sites, landing pages, SSG |
424
+ ```bash
425
+ pnpm add -D @tenphi/eslint-plugin-tasty
426
+ ```
427
+
428
+ ```js
429
+ import tasty from '@tenphi/eslint-plugin-tasty';
430
+ export default [tasty.configs.recommended];
431
+ ```
432
+
433
+ ### [Glaze](https://github.com/tenphi/glaze)
434
+
435
+ `@tenphi/glaze` OKHSL-based color theme generator with automatic WCAG contrast solving. Generate light, dark, and high-contrast palettes from a single hue, and export them directly as Tasty color tokens.
436
+
437
+ ```tsx
438
+ import { glaze } from '@tenphi/glaze';
439
+
440
+ const theme = glaze(280, 80);
441
+ theme.colors({
442
+ surface: { lightness: 97 },
443
+ text: { base: 'surface', lightness: '-52', contrast: 'AAA' },
444
+ });
445
+
446
+ const tokens = theme.tasty(); // Ready-to-use Tasty tokens
447
+ ```
448
+
449
+ ### [VS Code Extension](https://github.com/tenphi/tasty-vscode-extension)
450
+
451
+ Syntax highlighting for Tasty styles in TypeScript, TSX, JavaScript, and JSX. Highlights color tokens, custom units, state keys, presets, and style properties inside `tasty()`, `tastyStatic()`, and related APIs.
452
+
453
+ ### [Cube UI Kit](https://github.com/cube-js/cube-ui-kit)
454
+
455
+ Open-source React UI kit built on Tasty + React Aria. 100+ production components proving Tasty works at design-system scale. A reference implementation and a ready-to-use component library.
253
456
 
254
457
  ## Documentation
255
458
 
256
- - [Runtime API (tasty)](docs/tasty.md) — Full runtime styling documentation
257
- - [Zero Runtime (tastyStatic)](docs/tasty-static.md) — Build-time static styling documentation
459
+ - **[Runtime API (tasty)](docs/tasty.md)** — Full runtime styling documentation: component creation, state mappings, sub-elements, variants, hooks, configuration, and style property reference
460
+ - **[Zero Runtime (tastyStatic)](docs/tasty-static.md)** — Build-time static styling: Babel plugin setup, Next.js integration, and static style patterns
258
461
 
259
462
  ## License
260
463
 
package/dist/config.d.ts CHANGED
@@ -178,7 +178,8 @@ interface TastyConfig {
178
178
  * tokens (`$name`/`#name` definitions), local states, `@keyframes`, and `@properties`.
179
179
  *
180
180
  * Components reference recipes via: `recipe: 'name1 name2'` in their styles.
181
- * Use `|` to separate base recipes from post recipes: `recipe: 'base1 base2 | post1'`.
181
+ * Use `/` to separate base recipes from post recipes: `recipe: 'base1 base2 / post1'`.
182
+ * Use `none` to skip base recipes: `recipe: 'none / post1'`.
182
183
  * Resolution order: `base_recipes → component styles → post_recipes`.
183
184
  *
184
185
  * Recipes cannot reference other recipes.
package/dist/config.js CHANGED
@@ -223,9 +223,12 @@ function setGlobalRecipes(recipes) {
223
223
  warnOnce("recipes-after-styles", "[Tasty] Cannot update recipes after styles have been generated.\nThe new recipes will be ignored.");
224
224
  return;
225
225
  }
226
- if (devMode) for (const [name, recipeStyles] of Object.entries(recipes)) for (const key of Object.keys(recipeStyles)) {
227
- if (isSelector(key)) warnOnce(`recipe-selector-${name}-${key}`, `[Tasty] Recipe "${name}" contains sub-element key "${key}". Recipes must be flat styles without sub-element keys. Remove the sub-element key from the recipe definition.`);
228
- if (key === "recipe") warnOnce(`recipe-recursive-${name}`, `[Tasty] Recipe "${name}" contains a "recipe" key. Recipes cannot reference other recipes. Use space-separated names for composition: recipe: 'base elevated'.`);
226
+ if (devMode) for (const [name, recipeStyles] of Object.entries(recipes)) {
227
+ if (name === "none") warnOnce("recipe-reserved-none", "[Tasty] Recipe name \"none\" is reserved. It is used as a keyword meaning \"no base recipes\" (e.g. recipe: 'none / post-recipe'). Choose a different name for your recipe.");
228
+ for (const key of Object.keys(recipeStyles)) {
229
+ if (isSelector(key)) warnOnce(`recipe-selector-${name}-${key}`, `[Tasty] Recipe "${name}" contains sub-element key "${key}". Recipes must be flat styles without sub-element keys. Remove the sub-element key from the recipe definition.`);
230
+ if (key === "recipe") warnOnce(`recipe-recursive-${name}`, `[Tasty] Recipe "${name}" contains a "recipe" key. Recipes cannot reference other recipes. Use space-separated names for composition: recipe: 'base elevated'.`);
231
+ }
229
232
  }
230
233
  globalRecipes = recipes;
231
234
  }
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","names":[],"sources":["../src/config.ts"],"sourcesContent":["/**\n * Tasty Configuration Module\n *\n * Centralizes all tasty configuration, including:\n * - Style injector settings (nonce, cleanup thresholds, etc.)\n * - Global predefined states for advanced state mapping\n * - stylesGenerated flag that locks configuration after first style generation\n *\n * Configuration must be done BEFORE any styles are generated.\n * After the first `inject()` call, configuration is locked and attempts to\n * reconfigure will emit a warning and be ignored.\n */\n\nimport { StyleInjector } from './injector/injector';\nimport { clearPipelineCache, isSelector } from './pipeline';\nimport { setGlobalPredefinedStates } from './states';\nimport {\n normalizeHandlerDefinition,\n registerHandler,\n resetHandlers,\n} from './styles/predefined';\nimport { isDevEnv } from './utils/is-dev-env';\nimport {\n CUSTOM_UNITS,\n getGlobalFuncs,\n getGlobalParser,\n normalizeColorTokenValue,\n resetGlobalPredefinedTokens,\n setGlobalPredefinedTokens,\n} from './utils/styles';\n\nimport type { KeyframesSteps, PropertyDefinition } from './injector/types';\nimport type { StyleDetails, UnitHandler } from './parser/types';\nimport type { TastyPlugin } from './plugins/types';\nimport type { RecipeStyles } from './styles/types';\nimport type { StyleHandlerDefinition } from './utils/styles';\n\n/**\n * Configuration options for the Tasty style system\n */\nexport interface TastyConfig {\n /** CSP nonce for style elements */\n nonce?: string;\n /** Maximum rules per stylesheet (default: 8192) */\n maxRulesPerSheet?: number;\n /** Threshold for bulk cleanup of unused styles (default: 500) */\n unusedStylesThreshold?: number;\n /** Delay before bulk cleanup in ms, ignored if idleCleanup is true (default: 5000) */\n bulkCleanupDelay?: number;\n /** Use requestIdleCallback for cleanup when available (default: true) */\n idleCleanup?: boolean;\n /** Force text injection mode, auto-detected in test environments (default: auto) */\n forceTextInjection?: boolean;\n /** Enable development mode features: performance metrics and debug info (default: auto) */\n devMode?: boolean;\n /**\n * Ratio of unused styles to delete per bulk cleanup run (0..1).\n * Defaults to 0.5 (oldest half) to reduce risk of removing styles\n * that may be restored shortly after being marked unused.\n */\n bulkCleanupBatchRatio?: number;\n /**\n * Minimum age (in ms) a style must remain unused before eligible for deletion.\n * Helps avoid races during rapid mount/unmount cycles. Default: 10000ms.\n */\n unusedStylesMinAgeMs?: number;\n /**\n * Global predefined states for advanced state mapping.\n * These are state aliases that can be used in any component.\n * Example: { '@mobile': '@media(w < 920px)', '@dark': '@root(theme=dark)' }\n */\n states?: Record<string, string>;\n /**\n * Parser LRU cache size (default: 1000).\n * Larger values improve performance for apps with many unique style values.\n */\n parserCacheSize?: number;\n /**\n * Custom units for the style parser (merged with built-in units).\n * Units transform numeric values like `2x` → `calc(2 * var(--gap))`.\n * @example { em: 'em', vw: 'vw', custom: (n) => `${n * 10}px` }\n */\n units?: Record<string, string | UnitHandler>;\n /**\n * Custom functions for the style parser (merged with existing).\n * Functions process parsed style groups and return CSS values.\n * @example { myFunc: (groups) => groups.map(g => g.output).join(' ') }\n */\n funcs?: Record<string, (groups: StyleDetails[]) => string>;\n /**\n * Plugins that extend tasty with custom functions, units, or states.\n * Plugins are processed in order, with later plugins overriding earlier ones.\n * @example\n * ```ts\n * import { okhslPlugin } from '@tenphi/tasty';\n *\n * configure({\n * plugins: [okhslPlugin()],\n * });\n * ```\n */\n plugins?: TastyPlugin[];\n /**\n * Global keyframes definitions that can be referenced by animation names in styles.\n * Keys are animation names, values are keyframes step definitions.\n * Keyframes are only injected when actually used in styles.\n * @example\n * ```ts\n * configure({\n * keyframes: {\n * fadeIn: { from: { opacity: 0 }, to: { opacity: 1 } },\n * pulse: { '0%, 100%': { transform: 'scale(1)' }, '50%': { transform: 'scale(1.05)' } },\n * },\n * });\n * ```\n */\n keyframes?: Record<string, KeyframesSteps>;\n /**\n * Global CSS @property definitions for custom properties.\n * Keys use tasty token syntax ($name for properties, #name for colors).\n * Properties are only injected when the component using them is rendered.\n *\n * For color tokens (#name), `syntax: '<color>'` is auto-set and\n * `initialValue` defaults to `'transparent'` if not specified.\n *\n * @example\n * ```ts\n * configure({\n * properties: {\n * '$rotation': { syntax: '<angle>', initialValue: '0deg' },\n * '$scale': { syntax: '<number>', inherits: false, initialValue: 1 },\n * '#accent': { initialValue: 'purple' }, // syntax: '<color>' auto-set\n * },\n * });\n *\n * // Now use in styles - properties are registered when component renders:\n * const Spinner = tasty({\n * styles: {\n * transform: 'rotate($rotation)',\n * transition: '$$rotation 0.3s', // outputs: --rotation 0.3s\n * },\n * });\n * ```\n */\n properties?: Record<string, PropertyDefinition>;\n /**\n * Custom style handlers that transform style properties into CSS declarations.\n * Handlers replace built-in handlers for the same style name.\n * @example\n * ```ts\n * import { styleHandlers } from '@tenphi/tasty';\n *\n * configure({\n * handlers: {\n * // Override fill with custom behavior\n * fill: ({ fill }) => {\n * if (fill?.startsWith('gradient:')) {\n * return { background: fill.slice(9) };\n * }\n * return styleHandlers.fill({ fill });\n * },\n * // Add new custom style\n * elevation: ({ elevation }) => {\n * const level = parseInt(elevation) || 1;\n * return {\n * 'box-shadow': `0 ${level * 2}px ${level * 4}px rgba(0,0,0,0.1)`,\n * 'z-index': String(level * 100),\n * };\n * },\n * },\n * });\n * ```\n */\n handlers?: Record<string, StyleHandlerDefinition>;\n /**\n * Predefined tokens that are replaced during style parsing.\n * Token values are processed through the parser (like component tokens).\n * Use `$name` for custom properties and `#name` for color tokens.\n *\n * For color tokens (#name), boolean `true` is converted to `transparent`.\n *\n * @example\n * ```ts\n * configure({\n * tokens: {\n * $spacing: '2x',\n * '$card-padding': '4x',\n * '#accent': '#purple',\n * '#surface': '#white',\n * '#overlay': true, // → transparent\n * },\n * });\n *\n * // Now use in styles - tokens are replaced at parse time:\n * const Card = tasty({\n * styles: {\n * padding: '$card-padding', // → calc(4 * var(--gap))\n * fill: '#surface', // → var(--white-color)\n * },\n * });\n * ```\n */\n tokens?: Record<`$${string}`, string | number | boolean> &\n Record<`#${string}`, string | number | boolean>;\n /**\n * Predefined style recipes -- named style bundles that can be applied via `recipe` style property.\n * Recipe values are flat tasty styles (no sub-element keys). They may contain base styles,\n * tokens (`$name`/`#name` definitions), local states, `@keyframes`, and `@properties`.\n *\n * Components reference recipes via: `recipe: 'name1 name2'` in their styles.\n * Use `|` to separate base recipes from post recipes: `recipe: 'base1 base2 | post1'`.\n * Resolution order: `base_recipes → component styles → post_recipes`.\n *\n * Recipes cannot reference other recipes.\n *\n * @example\n * ```ts\n * configure({\n * recipes: {\n * card: { padding: '4x', fill: '#surface', radius: '1r', border: true },\n * elevated: { shadow: '2x 2x 4x #shadow' },\n * },\n * });\n *\n * // Usage in styles:\n * const Card = tasty({\n * styles: {\n * recipe: 'card elevated',\n * color: '#text', // Overrides recipe values\n * },\n * });\n * ```\n */\n recipes?: Record<string, RecipeStyles>;\n}\n\n// Warnings tracking to avoid duplicates\nconst emittedWarnings = new Set<string>();\n\nconst devMode = isDevEnv();\n\n/**\n * Emit a warning only once\n */\nfunction warnOnce(key: string, message: string): void {\n if (devMode && !emittedWarnings.has(key)) {\n emittedWarnings.add(key);\n console.warn(message);\n }\n}\n\n// ============================================================================\n// Configuration State\n// ============================================================================\n\n// Track whether styles have been generated (locks configuration)\nlet stylesGenerated = false;\n\n// Current configuration (null until first configure() or auto-configured on first use)\nlet currentConfig: TastyConfig | null = null;\n\n// Global keyframes storage (null = no keyframes configured, empty object checked via hasGlobalKeyframes)\nlet globalKeyframes: Record<string, KeyframesSteps> | null = null;\n\n// Global properties storage (null = no properties configured)\nlet globalProperties: Record<string, PropertyDefinition> | null = null;\n\n// Global recipes storage (null = no recipes configured)\nlet globalRecipes: Record<string, RecipeStyles> | null = null;\n\n/**\n * Internal properties required by tasty core features.\n * These are always injected when styles are first generated.\n * Keys use tasty token syntax (#name for colors, $name for other properties).\n *\n * For properties with CSS @property-compatible types (length, time, number, color),\n * an `initialValue` is provided so the property works even without a project-level token.\n */\nexport const INTERNAL_PROPERTIES: Record<string, PropertyDefinition> = {\n // Used by dual-fill feature to enable CSS transitions on the second fill color\n '#tasty-second-fill': {\n inherits: false,\n initialValue: 'transparent',\n },\n // Current color context variable (set by the color style handler).\n // Companion --current-color-rgb is auto-created.\n '#current': {\n inherits: true,\n initialValue: 'transparent',\n },\n // White and black are fundamental colors that need explicit initial values.\n // Companion -rgb properties are auto-created from the color initial values.\n '#white': {\n inherits: true,\n initialValue: 'rgb(255 255 255)',\n },\n '#black': {\n inherits: true,\n initialValue: 'rgb(0 0 0)',\n },\n\n // ---- Core design tokens used by style handlers ----\n // These provide sensible defaults so tasty works standalone without a design system.\n // Consuming projects (e.g. uikit) override these by defining tokens on :root.\n\n $gap: {\n syntax: '<length>',\n inherits: true,\n initialValue: '4px',\n },\n $radius: {\n syntax: '<length>',\n inherits: true,\n initialValue: '6px',\n },\n '$border-width': {\n syntax: '<length>',\n inherits: true,\n initialValue: '1px',\n },\n '$outline-width': {\n syntax: '<length>',\n inherits: true,\n initialValue: '3px',\n },\n $transition: {\n syntax: '<time>',\n inherits: true,\n initialValue: '80ms',\n },\n // Used by radius.ts for `radius=\"leaf\"` modifier\n '$sharp-radius': {\n syntax: '<length>',\n inherits: true,\n initialValue: '0px',\n },\n // Used by preset.ts for `preset=\"... strong\"`\n '$bold-font-weight': {\n syntax: '<number>',\n inherits: true,\n initialValue: '700',\n },\n};\n\n/**\n * Internal token defaults that cannot be expressed as CSS @property initial values\n * (e.g. font stacks, keyword colors). These are injected as :root CSS variables.\n * Consuming projects override them by setting their own tokens on :root.\n *\n * Keys are raw CSS custom property names (--name).\n */\nexport const INTERNAL_TOKENS: Record<string, string> = {\n '--font':\n 'system-ui, -apple-system, \"Segoe UI\", Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\", sans-serif',\n '--monospace-font':\n 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace',\n // Default border color to the element's current text color\n '--border-color': 'currentColor',\n};\n\n// Global injector instance key\nconst GLOBAL_INJECTOR_KEY = '__TASTY_GLOBAL_INJECTOR__';\n\ninterface TastyGlobalStorage {\n [GLOBAL_INJECTOR_KEY]?: StyleInjector;\n}\n\ndeclare global {\n interface Window {\n [GLOBAL_INJECTOR_KEY]?: StyleInjector;\n }\n\n var __TASTY_GLOBAL_INJECTOR__: StyleInjector | undefined;\n}\n\n/**\n * Detect if we're running in a test environment\n */\nexport function isTestEnvironment(): boolean {\n // Check Node.js environment\n if (typeof process !== 'undefined' && process.env?.NODE_ENV === 'test') {\n return true;\n }\n\n // Check for test runner globals (safely)\n if (typeof global !== 'undefined') {\n const g = global as unknown as Record<string, unknown>;\n if (g.vi || g.jest || g.expect || g.describe || g.it) {\n return true;\n }\n }\n\n // Check for jsdom environment (common in tests)\n if (\n typeof window !== 'undefined' &&\n window.navigator?.userAgent?.includes('jsdom')\n ) {\n return true;\n }\n\n // Check for other test runners\n if (typeof globalThis !== 'undefined') {\n const gt = globalThis as unknown as Record<string, unknown>;\n if (gt.vitest || gt.mocha) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Create default configuration with optional test environment detection\n */\nfunction createDefaultConfig(isTest?: boolean): TastyConfig {\n return {\n maxRulesPerSheet: 8192,\n unusedStylesThreshold: 500,\n bulkCleanupDelay: 5000,\n idleCleanup: true,\n forceTextInjection: isTest ?? false,\n devMode: isDevEnv(),\n bulkCleanupBatchRatio: 0.5,\n unusedStylesMinAgeMs: 10000,\n };\n}\n\n// ============================================================================\n// stylesGenerated Flag Management\n// ============================================================================\n\n/**\n * Mark that styles have been generated (called by injector on first inject)\n * This locks the configuration - no further changes allowed.\n * Also injects internal and global properties.\n */\nexport function markStylesGenerated(): void {\n if (stylesGenerated) return; // Already marked, skip\n\n stylesGenerated = true;\n\n const injector = getGlobalInjector();\n\n // Inject internal properties required by tasty core features\n for (const [token, definition] of Object.entries(INTERNAL_PROPERTIES)) {\n injector.property(token, definition);\n }\n\n // Inject internal token defaults as :root CSS variables.\n // Use injectGlobal (not injectRawCSS) so the rule goes through the same\n // injection path as all other tasty styles (consistent in both DOM and text/test mode).\n const internalTokenEntries = Object.entries(INTERNAL_TOKENS);\n if (internalTokenEntries.length > 0) {\n const declarations = internalTokenEntries\n .map(([name, value]) => `${name}: ${value}`)\n .join('; ');\n injector.injectGlobal([{ selector: ':root', declarations }]);\n }\n\n // Inject global properties if any were configured\n // Properties are permanent and only need to be injected once\n if (globalProperties && Object.keys(globalProperties).length > 0) {\n for (const [token, definition] of Object.entries(globalProperties)) {\n injector.property(token, definition);\n }\n }\n}\n\n/**\n * Check if styles have been generated (configuration is locked)\n */\nexport function hasStylesGenerated(): boolean {\n return stylesGenerated;\n}\n\n/**\n * Reset styles generated flag (for testing only)\n */\nexport function resetStylesGenerated(): void {\n stylesGenerated = false;\n emittedWarnings.clear();\n}\n\n// ============================================================================\n// Global Keyframes Management\n// ============================================================================\n\n/**\n * Check if any global keyframes are configured.\n * Fast path: returns false if no keyframes were ever set.\n */\nexport function hasGlobalKeyframes(): boolean {\n return globalKeyframes !== null && Object.keys(globalKeyframes).length > 0;\n}\n\n/**\n * Get global keyframes configuration.\n * Returns null if no keyframes configured (fast path for zero-overhead).\n */\nexport function getGlobalKeyframes(): Record<string, KeyframesSteps> | null {\n return globalKeyframes;\n}\n\n/**\n * Set global keyframes (called from configure).\n * Internal use only.\n */\nfunction setGlobalKeyframes(keyframes: Record<string, KeyframesSteps>): void {\n if (stylesGenerated) {\n warnOnce(\n 'keyframes-after-styles',\n `[Tasty] Cannot update keyframes after styles have been generated.\\n` +\n `The new keyframes will be ignored.`,\n );\n return;\n }\n globalKeyframes = keyframes;\n}\n\n// ============================================================================\n// Global Properties Management\n// ============================================================================\n\n/**\n * Check if any global properties are configured.\n * Fast path: returns false if no properties were ever set.\n */\nexport function hasGlobalProperties(): boolean {\n return globalProperties !== null && Object.keys(globalProperties).length > 0;\n}\n\n/**\n * Get global properties configuration.\n * Returns null if no properties configured (fast path for zero-overhead).\n */\nexport function getGlobalProperties(): Record<\n string,\n PropertyDefinition\n> | null {\n return globalProperties;\n}\n\n/**\n * Set global properties (called from configure).\n * Internal use only.\n */\nfunction setGlobalProperties(\n properties: Record<string, PropertyDefinition>,\n): void {\n if (stylesGenerated) {\n warnOnce(\n 'properties-after-styles',\n `[Tasty] Cannot update properties after styles have been generated.\\n` +\n `The new properties will be ignored.`,\n );\n return;\n }\n globalProperties = properties;\n}\n\n// ============================================================================\n// Global Recipes Management\n// ============================================================================\n\n/**\n * Check if any global recipes are configured.\n * Fast path: returns false if no recipes were ever set.\n */\nexport function hasGlobalRecipes(): boolean {\n return globalRecipes !== null && Object.keys(globalRecipes).length > 0;\n}\n\n/**\n * Get global recipes configuration.\n * Returns null if no recipes configured (fast path for zero-overhead).\n */\nexport function getGlobalRecipes(): Record<string, RecipeStyles> | null {\n return globalRecipes;\n}\n\n/**\n * Set global recipes (called from configure).\n * Internal use only.\n */\nfunction setGlobalRecipes(recipes: Record<string, RecipeStyles>): void {\n if (stylesGenerated) {\n warnOnce(\n 'recipes-after-styles',\n `[Tasty] Cannot update recipes after styles have been generated.\\n` +\n `The new recipes will be ignored.`,\n );\n return;\n }\n\n // Dev-mode validation\n if (devMode) {\n for (const [name, recipeStyles] of Object.entries(recipes)) {\n for (const key of Object.keys(recipeStyles)) {\n if (isSelector(key)) {\n warnOnce(\n `recipe-selector-${name}-${key}`,\n `[Tasty] Recipe \"${name}\" contains sub-element key \"${key}\". ` +\n `Recipes must be flat styles without sub-element keys. ` +\n `Remove the sub-element key from the recipe definition.`,\n );\n }\n if (key === 'recipe') {\n warnOnce(\n `recipe-recursive-${name}`,\n `[Tasty] Recipe \"${name}\" contains a \"recipe\" key. ` +\n `Recipes cannot reference other recipes. ` +\n `Use space-separated names for composition: recipe: 'base elevated'.`,\n );\n }\n }\n }\n }\n\n globalRecipes = recipes;\n}\n\n/**\n * Check if configuration is locked (styles have been generated)\n */\nexport function isConfigLocked(): boolean {\n return stylesGenerated;\n}\n\n// ============================================================================\n// Configuration API\n// ============================================================================\n\n/**\n * Configure the Tasty style system.\n *\n * Must be called BEFORE any styles are generated (before first render that uses tasty).\n * After styles are generated, configuration is locked and calls to configure() will\n * emit a warning and be ignored.\n *\n * @example\n * ```ts\n * import { configure } from '@tenphi/tasty';\n *\n * // Configure before app renders\n * configure({\n * nonce: 'abc123',\n * states: {\n * '@mobile': '@media(w < 768px)',\n * '@dark': '@root(theme=dark)',\n * },\n * });\n * ```\n */\nexport function configure(config: Partial<TastyConfig> = {}): void {\n if (stylesGenerated) {\n warnOnce(\n 'configure-after-styles',\n `[Tasty] Cannot call configure() after styles have been generated.\\n` +\n `Configuration must be done before the first render. The configuration will be ignored.`,\n );\n return;\n }\n\n // Collect merged values from plugins first, then override with direct config\n let mergedStates: Record<string, string> = {};\n let mergedUnits: Record<string, string | UnitHandler> = {};\n let mergedFuncs: Record<string, (groups: StyleDetails[]) => string> = {};\n let mergedHandlers: Record<string, StyleHandlerDefinition> = {};\n let mergedTokens: Record<string, string | number | boolean> = {};\n let mergedRecipes: Record<string, RecipeStyles> = {};\n\n // Process plugins in order\n if (config.plugins) {\n for (const plugin of config.plugins) {\n if (plugin.states) {\n mergedStates = { ...mergedStates, ...plugin.states };\n }\n if (plugin.units) {\n mergedUnits = { ...mergedUnits, ...plugin.units };\n }\n if (plugin.funcs) {\n mergedFuncs = { ...mergedFuncs, ...plugin.funcs };\n }\n if (plugin.handlers) {\n mergedHandlers = { ...mergedHandlers, ...plugin.handlers };\n }\n if (plugin.tokens) {\n mergedTokens = { ...mergedTokens, ...plugin.tokens };\n }\n if (plugin.recipes) {\n mergedRecipes = { ...mergedRecipes, ...plugin.recipes };\n }\n }\n }\n\n // Direct config overrides plugins\n if (config.states) {\n mergedStates = { ...mergedStates, ...config.states };\n }\n if (config.units) {\n mergedUnits = { ...mergedUnits, ...config.units };\n }\n if (config.funcs) {\n mergedFuncs = { ...mergedFuncs, ...config.funcs };\n }\n if (config.handlers) {\n mergedHandlers = { ...mergedHandlers, ...config.handlers };\n }\n if (config.tokens) {\n mergedTokens = { ...mergedTokens, ...config.tokens };\n }\n if (config.recipes) {\n mergedRecipes = { ...mergedRecipes, ...config.recipes };\n }\n\n // Handle predefined states\n if (Object.keys(mergedStates).length > 0) {\n setGlobalPredefinedStates(mergedStates);\n }\n\n // Handle parser configuration (merge semantics - extend, not replace)\n const parser = getGlobalParser();\n\n if (config.parserCacheSize !== undefined) {\n parser.updateOptions({ cacheSize: config.parserCacheSize });\n }\n\n if (Object.keys(mergedUnits).length > 0) {\n // Merge with existing units\n const currentUnits = parser.getUnits() ?? CUSTOM_UNITS;\n parser.setUnits({ ...currentUnits, ...mergedUnits });\n }\n\n if (Object.keys(mergedFuncs).length > 0) {\n // Merge with existing funcs\n const currentFuncs = getGlobalFuncs();\n const finalFuncs = { ...currentFuncs, ...mergedFuncs };\n parser.setFuncs(finalFuncs);\n // Also update the global registry so customFunc() continues to work\n Object.assign(currentFuncs, mergedFuncs);\n }\n\n // Handle keyframes\n if (config.keyframes) {\n setGlobalKeyframes(config.keyframes);\n }\n\n // Handle properties\n if (config.properties) {\n setGlobalProperties(config.properties);\n }\n\n // Handle custom handlers\n if (Object.keys(mergedHandlers).length > 0) {\n for (const [name, definition] of Object.entries(mergedHandlers)) {\n const handler = normalizeHandlerDefinition(name, definition);\n registerHandler(handler);\n }\n }\n\n // Handle predefined tokens\n // Note: Tokens are processed by the classifier, not here.\n // We just store the raw values; the classifier will process them when encountered.\n if (Object.keys(mergedTokens).length > 0) {\n // Store tokens (keys are normalized to lowercase by setGlobalPredefinedTokens)\n const processedTokens: Record<string, string> = {};\n for (const [key, value] of Object.entries(mergedTokens)) {\n if (key.startsWith('#')) {\n // Color token - use shared helper for boolean handling\n const normalized = normalizeColorTokenValue(value);\n if (normalized === null) continue; // Skip false values\n processedTokens[key] = String(normalized);\n } else if (value === false) {\n // Skip false values for non-color tokens\n continue;\n } else {\n processedTokens[key] = String(value);\n }\n }\n setGlobalPredefinedTokens(processedTokens);\n }\n\n // Handle recipes\n if (Object.keys(mergedRecipes).length > 0) {\n setGlobalRecipes(mergedRecipes);\n }\n\n const {\n states: _states,\n parserCacheSize: _parserCacheSize,\n units: _units,\n funcs: _funcs,\n plugins: _plugins,\n keyframes: _keyframes,\n properties: _properties,\n handlers: _handlers,\n tokens: _tokens,\n recipes: _recipes,\n ...injectorConfig\n } = config;\n\n const fullConfig: TastyConfig = {\n ...createDefaultConfig(),\n ...currentConfig,\n ...injectorConfig,\n };\n\n // Store the config\n currentConfig = fullConfig;\n\n // Create/replace the global injector\n const storage: TastyGlobalStorage =\n typeof window !== 'undefined' ? window : globalThis;\n storage[GLOBAL_INJECTOR_KEY] = new StyleInjector(fullConfig);\n}\n\n/**\n * Get the current configuration.\n * If not configured, returns default configuration.\n */\nexport function getConfig(): TastyConfig {\n if (!currentConfig) {\n currentConfig = createDefaultConfig(isTestEnvironment());\n }\n return currentConfig;\n}\n\n/**\n * Get the global injector instance.\n * Auto-configures with defaults if not already configured.\n */\nexport function getGlobalInjector(): StyleInjector {\n const storage: TastyGlobalStorage =\n typeof window !== 'undefined' ? window : globalThis;\n\n if (!storage[GLOBAL_INJECTOR_KEY]) {\n configure();\n }\n\n return storage[GLOBAL_INJECTOR_KEY]!;\n}\n\n/**\n * Reset configuration (for testing only).\n * Clears the global injector and allows reconfiguration.\n */\nexport function resetConfig(): void {\n stylesGenerated = false;\n currentConfig = null;\n globalKeyframes = null;\n globalProperties = null;\n globalRecipes = null;\n resetGlobalPredefinedTokens();\n resetHandlers();\n clearPipelineCache();\n emittedWarnings.clear();\n\n const storage: TastyGlobalStorage =\n typeof window !== 'undefined' ? window : globalThis;\n delete storage[GLOBAL_INJECTOR_KEY];\n}\n\n// Re-export TastyConfig as StyleInjectorConfig for backward compatibility\nexport type { TastyConfig as StyleInjectorConfig };\n"],"mappings":"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"}
1
+ {"version":3,"file":"config.js","names":[],"sources":["../src/config.ts"],"sourcesContent":["/**\n * Tasty Configuration Module\n *\n * Centralizes all tasty configuration, including:\n * - Style injector settings (nonce, cleanup thresholds, etc.)\n * - Global predefined states for advanced state mapping\n * - stylesGenerated flag that locks configuration after first style generation\n *\n * Configuration must be done BEFORE any styles are generated.\n * After the first `inject()` call, configuration is locked and attempts to\n * reconfigure will emit a warning and be ignored.\n */\n\nimport { StyleInjector } from './injector/injector';\nimport { clearPipelineCache, isSelector } from './pipeline';\nimport { setGlobalPredefinedStates } from './states';\nimport {\n normalizeHandlerDefinition,\n registerHandler,\n resetHandlers,\n} from './styles/predefined';\nimport { isDevEnv } from './utils/is-dev-env';\nimport {\n CUSTOM_UNITS,\n getGlobalFuncs,\n getGlobalParser,\n normalizeColorTokenValue,\n resetGlobalPredefinedTokens,\n setGlobalPredefinedTokens,\n} from './utils/styles';\n\nimport type { KeyframesSteps, PropertyDefinition } from './injector/types';\nimport type { StyleDetails, UnitHandler } from './parser/types';\nimport type { TastyPlugin } from './plugins/types';\nimport type { RecipeStyles } from './styles/types';\nimport type { StyleHandlerDefinition } from './utils/styles';\n\n/**\n * Configuration options for the Tasty style system\n */\nexport interface TastyConfig {\n /** CSP nonce for style elements */\n nonce?: string;\n /** Maximum rules per stylesheet (default: 8192) */\n maxRulesPerSheet?: number;\n /** Threshold for bulk cleanup of unused styles (default: 500) */\n unusedStylesThreshold?: number;\n /** Delay before bulk cleanup in ms, ignored if idleCleanup is true (default: 5000) */\n bulkCleanupDelay?: number;\n /** Use requestIdleCallback for cleanup when available (default: true) */\n idleCleanup?: boolean;\n /** Force text injection mode, auto-detected in test environments (default: auto) */\n forceTextInjection?: boolean;\n /** Enable development mode features: performance metrics and debug info (default: auto) */\n devMode?: boolean;\n /**\n * Ratio of unused styles to delete per bulk cleanup run (0..1).\n * Defaults to 0.5 (oldest half) to reduce risk of removing styles\n * that may be restored shortly after being marked unused.\n */\n bulkCleanupBatchRatio?: number;\n /**\n * Minimum age (in ms) a style must remain unused before eligible for deletion.\n * Helps avoid races during rapid mount/unmount cycles. Default: 10000ms.\n */\n unusedStylesMinAgeMs?: number;\n /**\n * Global predefined states for advanced state mapping.\n * These are state aliases that can be used in any component.\n * Example: { '@mobile': '@media(w < 920px)', '@dark': '@root(theme=dark)' }\n */\n states?: Record<string, string>;\n /**\n * Parser LRU cache size (default: 1000).\n * Larger values improve performance for apps with many unique style values.\n */\n parserCacheSize?: number;\n /**\n * Custom units for the style parser (merged with built-in units).\n * Units transform numeric values like `2x` → `calc(2 * var(--gap))`.\n * @example { em: 'em', vw: 'vw', custom: (n) => `${n * 10}px` }\n */\n units?: Record<string, string | UnitHandler>;\n /**\n * Custom functions for the style parser (merged with existing).\n * Functions process parsed style groups and return CSS values.\n * @example { myFunc: (groups) => groups.map(g => g.output).join(' ') }\n */\n funcs?: Record<string, (groups: StyleDetails[]) => string>;\n /**\n * Plugins that extend tasty with custom functions, units, or states.\n * Plugins are processed in order, with later plugins overriding earlier ones.\n * @example\n * ```ts\n * import { okhslPlugin } from '@tenphi/tasty';\n *\n * configure({\n * plugins: [okhslPlugin()],\n * });\n * ```\n */\n plugins?: TastyPlugin[];\n /**\n * Global keyframes definitions that can be referenced by animation names in styles.\n * Keys are animation names, values are keyframes step definitions.\n * Keyframes are only injected when actually used in styles.\n * @example\n * ```ts\n * configure({\n * keyframes: {\n * fadeIn: { from: { opacity: 0 }, to: { opacity: 1 } },\n * pulse: { '0%, 100%': { transform: 'scale(1)' }, '50%': { transform: 'scale(1.05)' } },\n * },\n * });\n * ```\n */\n keyframes?: Record<string, KeyframesSteps>;\n /**\n * Global CSS @property definitions for custom properties.\n * Keys use tasty token syntax ($name for properties, #name for colors).\n * Properties are only injected when the component using them is rendered.\n *\n * For color tokens (#name), `syntax: '<color>'` is auto-set and\n * `initialValue` defaults to `'transparent'` if not specified.\n *\n * @example\n * ```ts\n * configure({\n * properties: {\n * '$rotation': { syntax: '<angle>', initialValue: '0deg' },\n * '$scale': { syntax: '<number>', inherits: false, initialValue: 1 },\n * '#accent': { initialValue: 'purple' }, // syntax: '<color>' auto-set\n * },\n * });\n *\n * // Now use in styles - properties are registered when component renders:\n * const Spinner = tasty({\n * styles: {\n * transform: 'rotate($rotation)',\n * transition: '$$rotation 0.3s', // outputs: --rotation 0.3s\n * },\n * });\n * ```\n */\n properties?: Record<string, PropertyDefinition>;\n /**\n * Custom style handlers that transform style properties into CSS declarations.\n * Handlers replace built-in handlers for the same style name.\n * @example\n * ```ts\n * import { styleHandlers } from '@tenphi/tasty';\n *\n * configure({\n * handlers: {\n * // Override fill with custom behavior\n * fill: ({ fill }) => {\n * if (fill?.startsWith('gradient:')) {\n * return { background: fill.slice(9) };\n * }\n * return styleHandlers.fill({ fill });\n * },\n * // Add new custom style\n * elevation: ({ elevation }) => {\n * const level = parseInt(elevation) || 1;\n * return {\n * 'box-shadow': `0 ${level * 2}px ${level * 4}px rgba(0,0,0,0.1)`,\n * 'z-index': String(level * 100),\n * };\n * },\n * },\n * });\n * ```\n */\n handlers?: Record<string, StyleHandlerDefinition>;\n /**\n * Predefined tokens that are replaced during style parsing.\n * Token values are processed through the parser (like component tokens).\n * Use `$name` for custom properties and `#name` for color tokens.\n *\n * For color tokens (#name), boolean `true` is converted to `transparent`.\n *\n * @example\n * ```ts\n * configure({\n * tokens: {\n * $spacing: '2x',\n * '$card-padding': '4x',\n * '#accent': '#purple',\n * '#surface': '#white',\n * '#overlay': true, // → transparent\n * },\n * });\n *\n * // Now use in styles - tokens are replaced at parse time:\n * const Card = tasty({\n * styles: {\n * padding: '$card-padding', // → calc(4 * var(--gap))\n * fill: '#surface', // → var(--white-color)\n * },\n * });\n * ```\n */\n tokens?: Record<`$${string}`, string | number | boolean> &\n Record<`#${string}`, string | number | boolean>;\n /**\n * Predefined style recipes -- named style bundles that can be applied via `recipe` style property.\n * Recipe values are flat tasty styles (no sub-element keys). They may contain base styles,\n * tokens (`$name`/`#name` definitions), local states, `@keyframes`, and `@properties`.\n *\n * Components reference recipes via: `recipe: 'name1 name2'` in their styles.\n * Use `/` to separate base recipes from post recipes: `recipe: 'base1 base2 / post1'`.\n * Use `none` to skip base recipes: `recipe: 'none / post1'`.\n * Resolution order: `base_recipes → component styles → post_recipes`.\n *\n * Recipes cannot reference other recipes.\n *\n * @example\n * ```ts\n * configure({\n * recipes: {\n * card: { padding: '4x', fill: '#surface', radius: '1r', border: true },\n * elevated: { shadow: '2x 2x 4x #shadow' },\n * },\n * });\n *\n * // Usage in styles:\n * const Card = tasty({\n * styles: {\n * recipe: 'card elevated',\n * color: '#text', // Overrides recipe values\n * },\n * });\n * ```\n */\n recipes?: Record<string, RecipeStyles>;\n}\n\n// Warnings tracking to avoid duplicates\nconst emittedWarnings = new Set<string>();\n\nconst devMode = isDevEnv();\n\n/**\n * Emit a warning only once\n */\nfunction warnOnce(key: string, message: string): void {\n if (devMode && !emittedWarnings.has(key)) {\n emittedWarnings.add(key);\n console.warn(message);\n }\n}\n\n// ============================================================================\n// Configuration State\n// ============================================================================\n\n// Track whether styles have been generated (locks configuration)\nlet stylesGenerated = false;\n\n// Current configuration (null until first configure() or auto-configured on first use)\nlet currentConfig: TastyConfig | null = null;\n\n// Global keyframes storage (null = no keyframes configured, empty object checked via hasGlobalKeyframes)\nlet globalKeyframes: Record<string, KeyframesSteps> | null = null;\n\n// Global properties storage (null = no properties configured)\nlet globalProperties: Record<string, PropertyDefinition> | null = null;\n\n// Global recipes storage (null = no recipes configured)\nlet globalRecipes: Record<string, RecipeStyles> | null = null;\n\n/**\n * Internal properties required by tasty core features.\n * These are always injected when styles are first generated.\n * Keys use tasty token syntax (#name for colors, $name for other properties).\n *\n * For properties with CSS @property-compatible types (length, time, number, color),\n * an `initialValue` is provided so the property works even without a project-level token.\n */\nexport const INTERNAL_PROPERTIES: Record<string, PropertyDefinition> = {\n // Used by dual-fill feature to enable CSS transitions on the second fill color\n '#tasty-second-fill': {\n inherits: false,\n initialValue: 'transparent',\n },\n // Current color context variable (set by the color style handler).\n // Companion --current-color-rgb is auto-created.\n '#current': {\n inherits: true,\n initialValue: 'transparent',\n },\n // White and black are fundamental colors that need explicit initial values.\n // Companion -rgb properties are auto-created from the color initial values.\n '#white': {\n inherits: true,\n initialValue: 'rgb(255 255 255)',\n },\n '#black': {\n inherits: true,\n initialValue: 'rgb(0 0 0)',\n },\n\n // ---- Core design tokens used by style handlers ----\n // These provide sensible defaults so tasty works standalone without a design system.\n // Consuming projects (e.g. uikit) override these by defining tokens on :root.\n\n $gap: {\n syntax: '<length>',\n inherits: true,\n initialValue: '4px',\n },\n $radius: {\n syntax: '<length>',\n inherits: true,\n initialValue: '6px',\n },\n '$border-width': {\n syntax: '<length>',\n inherits: true,\n initialValue: '1px',\n },\n '$outline-width': {\n syntax: '<length>',\n inherits: true,\n initialValue: '3px',\n },\n $transition: {\n syntax: '<time>',\n inherits: true,\n initialValue: '80ms',\n },\n // Used by radius.ts for `radius=\"leaf\"` modifier\n '$sharp-radius': {\n syntax: '<length>',\n inherits: true,\n initialValue: '0px',\n },\n // Used by preset.ts for `preset=\"... strong\"`\n '$bold-font-weight': {\n syntax: '<number>',\n inherits: true,\n initialValue: '700',\n },\n};\n\n/**\n * Internal token defaults that cannot be expressed as CSS @property initial values\n * (e.g. font stacks, keyword colors). These are injected as :root CSS variables.\n * Consuming projects override them by setting their own tokens on :root.\n *\n * Keys are raw CSS custom property names (--name).\n */\nexport const INTERNAL_TOKENS: Record<string, string> = {\n '--font':\n 'system-ui, -apple-system, \"Segoe UI\", Roboto, Helvetica, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\", sans-serif',\n '--monospace-font':\n 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace',\n // Default border color to the element's current text color\n '--border-color': 'currentColor',\n};\n\n// Global injector instance key\nconst GLOBAL_INJECTOR_KEY = '__TASTY_GLOBAL_INJECTOR__';\n\ninterface TastyGlobalStorage {\n [GLOBAL_INJECTOR_KEY]?: StyleInjector;\n}\n\ndeclare global {\n interface Window {\n [GLOBAL_INJECTOR_KEY]?: StyleInjector;\n }\n\n var __TASTY_GLOBAL_INJECTOR__: StyleInjector | undefined;\n}\n\n/**\n * Detect if we're running in a test environment\n */\nexport function isTestEnvironment(): boolean {\n // Check Node.js environment\n if (typeof process !== 'undefined' && process.env?.NODE_ENV === 'test') {\n return true;\n }\n\n // Check for test runner globals (safely)\n if (typeof global !== 'undefined') {\n const g = global as unknown as Record<string, unknown>;\n if (g.vi || g.jest || g.expect || g.describe || g.it) {\n return true;\n }\n }\n\n // Check for jsdom environment (common in tests)\n if (\n typeof window !== 'undefined' &&\n window.navigator?.userAgent?.includes('jsdom')\n ) {\n return true;\n }\n\n // Check for other test runners\n if (typeof globalThis !== 'undefined') {\n const gt = globalThis as unknown as Record<string, unknown>;\n if (gt.vitest || gt.mocha) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Create default configuration with optional test environment detection\n */\nfunction createDefaultConfig(isTest?: boolean): TastyConfig {\n return {\n maxRulesPerSheet: 8192,\n unusedStylesThreshold: 500,\n bulkCleanupDelay: 5000,\n idleCleanup: true,\n forceTextInjection: isTest ?? false,\n devMode: isDevEnv(),\n bulkCleanupBatchRatio: 0.5,\n unusedStylesMinAgeMs: 10000,\n };\n}\n\n// ============================================================================\n// stylesGenerated Flag Management\n// ============================================================================\n\n/**\n * Mark that styles have been generated (called by injector on first inject)\n * This locks the configuration - no further changes allowed.\n * Also injects internal and global properties.\n */\nexport function markStylesGenerated(): void {\n if (stylesGenerated) return; // Already marked, skip\n\n stylesGenerated = true;\n\n const injector = getGlobalInjector();\n\n // Inject internal properties required by tasty core features\n for (const [token, definition] of Object.entries(INTERNAL_PROPERTIES)) {\n injector.property(token, definition);\n }\n\n // Inject internal token defaults as :root CSS variables.\n // Use injectGlobal (not injectRawCSS) so the rule goes through the same\n // injection path as all other tasty styles (consistent in both DOM and text/test mode).\n const internalTokenEntries = Object.entries(INTERNAL_TOKENS);\n if (internalTokenEntries.length > 0) {\n const declarations = internalTokenEntries\n .map(([name, value]) => `${name}: ${value}`)\n .join('; ');\n injector.injectGlobal([{ selector: ':root', declarations }]);\n }\n\n // Inject global properties if any were configured\n // Properties are permanent and only need to be injected once\n if (globalProperties && Object.keys(globalProperties).length > 0) {\n for (const [token, definition] of Object.entries(globalProperties)) {\n injector.property(token, definition);\n }\n }\n}\n\n/**\n * Check if styles have been generated (configuration is locked)\n */\nexport function hasStylesGenerated(): boolean {\n return stylesGenerated;\n}\n\n/**\n * Reset styles generated flag (for testing only)\n */\nexport function resetStylesGenerated(): void {\n stylesGenerated = false;\n emittedWarnings.clear();\n}\n\n// ============================================================================\n// Global Keyframes Management\n// ============================================================================\n\n/**\n * Check if any global keyframes are configured.\n * Fast path: returns false if no keyframes were ever set.\n */\nexport function hasGlobalKeyframes(): boolean {\n return globalKeyframes !== null && Object.keys(globalKeyframes).length > 0;\n}\n\n/**\n * Get global keyframes configuration.\n * Returns null if no keyframes configured (fast path for zero-overhead).\n */\nexport function getGlobalKeyframes(): Record<string, KeyframesSteps> | null {\n return globalKeyframes;\n}\n\n/**\n * Set global keyframes (called from configure).\n * Internal use only.\n */\nfunction setGlobalKeyframes(keyframes: Record<string, KeyframesSteps>): void {\n if (stylesGenerated) {\n warnOnce(\n 'keyframes-after-styles',\n `[Tasty] Cannot update keyframes after styles have been generated.\\n` +\n `The new keyframes will be ignored.`,\n );\n return;\n }\n globalKeyframes = keyframes;\n}\n\n// ============================================================================\n// Global Properties Management\n// ============================================================================\n\n/**\n * Check if any global properties are configured.\n * Fast path: returns false if no properties were ever set.\n */\nexport function hasGlobalProperties(): boolean {\n return globalProperties !== null && Object.keys(globalProperties).length > 0;\n}\n\n/**\n * Get global properties configuration.\n * Returns null if no properties configured (fast path for zero-overhead).\n */\nexport function getGlobalProperties(): Record<\n string,\n PropertyDefinition\n> | null {\n return globalProperties;\n}\n\n/**\n * Set global properties (called from configure).\n * Internal use only.\n */\nfunction setGlobalProperties(\n properties: Record<string, PropertyDefinition>,\n): void {\n if (stylesGenerated) {\n warnOnce(\n 'properties-after-styles',\n `[Tasty] Cannot update properties after styles have been generated.\\n` +\n `The new properties will be ignored.`,\n );\n return;\n }\n globalProperties = properties;\n}\n\n// ============================================================================\n// Global Recipes Management\n// ============================================================================\n\n/**\n * Check if any global recipes are configured.\n * Fast path: returns false if no recipes were ever set.\n */\nexport function hasGlobalRecipes(): boolean {\n return globalRecipes !== null && Object.keys(globalRecipes).length > 0;\n}\n\n/**\n * Get global recipes configuration.\n * Returns null if no recipes configured (fast path for zero-overhead).\n */\nexport function getGlobalRecipes(): Record<string, RecipeStyles> | null {\n return globalRecipes;\n}\n\n/**\n * Set global recipes (called from configure).\n * Internal use only.\n */\nfunction setGlobalRecipes(recipes: Record<string, RecipeStyles>): void {\n if (stylesGenerated) {\n warnOnce(\n 'recipes-after-styles',\n `[Tasty] Cannot update recipes after styles have been generated.\\n` +\n `The new recipes will be ignored.`,\n );\n return;\n }\n\n // Dev-mode validation\n if (devMode) {\n for (const [name, recipeStyles] of Object.entries(recipes)) {\n if (name === 'none') {\n warnOnce(\n 'recipe-reserved-none',\n `[Tasty] Recipe name \"none\" is reserved. ` +\n `It is used as a keyword meaning \"no base recipes\" ` +\n `(e.g. recipe: 'none / post-recipe'). ` +\n `Choose a different name for your recipe.`,\n );\n }\n\n for (const key of Object.keys(recipeStyles)) {\n if (isSelector(key)) {\n warnOnce(\n `recipe-selector-${name}-${key}`,\n `[Tasty] Recipe \"${name}\" contains sub-element key \"${key}\". ` +\n `Recipes must be flat styles without sub-element keys. ` +\n `Remove the sub-element key from the recipe definition.`,\n );\n }\n if (key === 'recipe') {\n warnOnce(\n `recipe-recursive-${name}`,\n `[Tasty] Recipe \"${name}\" contains a \"recipe\" key. ` +\n `Recipes cannot reference other recipes. ` +\n `Use space-separated names for composition: recipe: 'base elevated'.`,\n );\n }\n }\n }\n }\n\n globalRecipes = recipes;\n}\n\n/**\n * Check if configuration is locked (styles have been generated)\n */\nexport function isConfigLocked(): boolean {\n return stylesGenerated;\n}\n\n// ============================================================================\n// Configuration API\n// ============================================================================\n\n/**\n * Configure the Tasty style system.\n *\n * Must be called BEFORE any styles are generated (before first render that uses tasty).\n * After styles are generated, configuration is locked and calls to configure() will\n * emit a warning and be ignored.\n *\n * @example\n * ```ts\n * import { configure } from '@tenphi/tasty';\n *\n * // Configure before app renders\n * configure({\n * nonce: 'abc123',\n * states: {\n * '@mobile': '@media(w < 768px)',\n * '@dark': '@root(theme=dark)',\n * },\n * });\n * ```\n */\nexport function configure(config: Partial<TastyConfig> = {}): void {\n if (stylesGenerated) {\n warnOnce(\n 'configure-after-styles',\n `[Tasty] Cannot call configure() after styles have been generated.\\n` +\n `Configuration must be done before the first render. The configuration will be ignored.`,\n );\n return;\n }\n\n // Collect merged values from plugins first, then override with direct config\n let mergedStates: Record<string, string> = {};\n let mergedUnits: Record<string, string | UnitHandler> = {};\n let mergedFuncs: Record<string, (groups: StyleDetails[]) => string> = {};\n let mergedHandlers: Record<string, StyleHandlerDefinition> = {};\n let mergedTokens: Record<string, string | number | boolean> = {};\n let mergedRecipes: Record<string, RecipeStyles> = {};\n\n // Process plugins in order\n if (config.plugins) {\n for (const plugin of config.plugins) {\n if (plugin.states) {\n mergedStates = { ...mergedStates, ...plugin.states };\n }\n if (plugin.units) {\n mergedUnits = { ...mergedUnits, ...plugin.units };\n }\n if (plugin.funcs) {\n mergedFuncs = { ...mergedFuncs, ...plugin.funcs };\n }\n if (plugin.handlers) {\n mergedHandlers = { ...mergedHandlers, ...plugin.handlers };\n }\n if (plugin.tokens) {\n mergedTokens = { ...mergedTokens, ...plugin.tokens };\n }\n if (plugin.recipes) {\n mergedRecipes = { ...mergedRecipes, ...plugin.recipes };\n }\n }\n }\n\n // Direct config overrides plugins\n if (config.states) {\n mergedStates = { ...mergedStates, ...config.states };\n }\n if (config.units) {\n mergedUnits = { ...mergedUnits, ...config.units };\n }\n if (config.funcs) {\n mergedFuncs = { ...mergedFuncs, ...config.funcs };\n }\n if (config.handlers) {\n mergedHandlers = { ...mergedHandlers, ...config.handlers };\n }\n if (config.tokens) {\n mergedTokens = { ...mergedTokens, ...config.tokens };\n }\n if (config.recipes) {\n mergedRecipes = { ...mergedRecipes, ...config.recipes };\n }\n\n // Handle predefined states\n if (Object.keys(mergedStates).length > 0) {\n setGlobalPredefinedStates(mergedStates);\n }\n\n // Handle parser configuration (merge semantics - extend, not replace)\n const parser = getGlobalParser();\n\n if (config.parserCacheSize !== undefined) {\n parser.updateOptions({ cacheSize: config.parserCacheSize });\n }\n\n if (Object.keys(mergedUnits).length > 0) {\n // Merge with existing units\n const currentUnits = parser.getUnits() ?? CUSTOM_UNITS;\n parser.setUnits({ ...currentUnits, ...mergedUnits });\n }\n\n if (Object.keys(mergedFuncs).length > 0) {\n // Merge with existing funcs\n const currentFuncs = getGlobalFuncs();\n const finalFuncs = { ...currentFuncs, ...mergedFuncs };\n parser.setFuncs(finalFuncs);\n // Also update the global registry so customFunc() continues to work\n Object.assign(currentFuncs, mergedFuncs);\n }\n\n // Handle keyframes\n if (config.keyframes) {\n setGlobalKeyframes(config.keyframes);\n }\n\n // Handle properties\n if (config.properties) {\n setGlobalProperties(config.properties);\n }\n\n // Handle custom handlers\n if (Object.keys(mergedHandlers).length > 0) {\n for (const [name, definition] of Object.entries(mergedHandlers)) {\n const handler = normalizeHandlerDefinition(name, definition);\n registerHandler(handler);\n }\n }\n\n // Handle predefined tokens\n // Note: Tokens are processed by the classifier, not here.\n // We just store the raw values; the classifier will process them when encountered.\n if (Object.keys(mergedTokens).length > 0) {\n // Store tokens (keys are normalized to lowercase by setGlobalPredefinedTokens)\n const processedTokens: Record<string, string> = {};\n for (const [key, value] of Object.entries(mergedTokens)) {\n if (key.startsWith('#')) {\n // Color token - use shared helper for boolean handling\n const normalized = normalizeColorTokenValue(value);\n if (normalized === null) continue; // Skip false values\n processedTokens[key] = String(normalized);\n } else if (value === false) {\n // Skip false values for non-color tokens\n continue;\n } else {\n processedTokens[key] = String(value);\n }\n }\n setGlobalPredefinedTokens(processedTokens);\n }\n\n // Handle recipes\n if (Object.keys(mergedRecipes).length > 0) {\n setGlobalRecipes(mergedRecipes);\n }\n\n const {\n states: _states,\n parserCacheSize: _parserCacheSize,\n units: _units,\n funcs: _funcs,\n plugins: _plugins,\n keyframes: _keyframes,\n properties: _properties,\n handlers: _handlers,\n tokens: _tokens,\n recipes: _recipes,\n ...injectorConfig\n } = config;\n\n const fullConfig: TastyConfig = {\n ...createDefaultConfig(),\n ...currentConfig,\n ...injectorConfig,\n };\n\n // Store the config\n currentConfig = fullConfig;\n\n // Create/replace the global injector\n const storage: TastyGlobalStorage =\n typeof window !== 'undefined' ? window : globalThis;\n storage[GLOBAL_INJECTOR_KEY] = new StyleInjector(fullConfig);\n}\n\n/**\n * Get the current configuration.\n * If not configured, returns default configuration.\n */\nexport function getConfig(): TastyConfig {\n if (!currentConfig) {\n currentConfig = createDefaultConfig(isTestEnvironment());\n }\n return currentConfig;\n}\n\n/**\n * Get the global injector instance.\n * Auto-configures with defaults if not already configured.\n */\nexport function getGlobalInjector(): StyleInjector {\n const storage: TastyGlobalStorage =\n typeof window !== 'undefined' ? window : globalThis;\n\n if (!storage[GLOBAL_INJECTOR_KEY]) {\n configure();\n }\n\n return storage[GLOBAL_INJECTOR_KEY]!;\n}\n\n/**\n * Reset configuration (for testing only).\n * Clears the global injector and allows reconfiguration.\n */\nexport function resetConfig(): void {\n stylesGenerated = false;\n currentConfig = null;\n globalKeyframes = null;\n globalProperties = null;\n globalRecipes = null;\n resetGlobalPredefinedTokens();\n resetHandlers();\n clearPipelineCache();\n emittedWarnings.clear();\n\n const storage: TastyGlobalStorage =\n typeof window !== 'undefined' ? window : globalThis;\n delete storage[GLOBAL_INJECTOR_KEY];\n}\n\n// Re-export TastyConfig as StyleInjectorConfig for backward compatibility\nexport type { TastyConfig as StyleInjectorConfig };\n"],"mappings":"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"}
@@ -441,13 +441,15 @@ interface StylesInterface extends Omit<CSSProperties, 'color' | 'fill' | 'font'
441
441
  /**
442
442
  * Apply one or more predefined style recipes by name.
443
443
  * Recipes are defined globally via `configure({ recipes: { ... } })`.
444
- * Multiple recipes are space-separated. Use `|` to separate base recipes
444
+ * Multiple recipes are space-separated. Use `/` to separate base recipes
445
445
  * (applied before component styles) from post recipes (applied after, via mergeStyles).
446
+ * Use `none` to explicitly skip base recipes.
446
447
  *
447
448
  * Examples:
448
449
  * - `recipe: 'card'` // Apply the 'card' recipe
449
450
  * - `recipe: 'card elevated'` // Apply 'card' then 'elevated', then component styles
450
- * - `recipe: 'reset input | input-autofill'` // Base recipes, then post recipe
451
+ * - `recipe: 'reset input / input-autofill'` // Base recipes, then post recipe
452
+ * - `recipe: 'none / disabled'` // No base recipes, only post recipe
451
453
  * - `recipe: ''` // Clear recipes from base styles when extending
452
454
  */
453
455
  recipe?: string;
package/dist/tasty.d.ts CHANGED
@@ -100,8 +100,12 @@ type TastyComponentPropsWithDefaults<Props extends PropsWithStyles, DefaultProps
100
100
  declare function tasty<K extends StyleList, V extends VariantMap, E extends ElementsDefinition = Record<string, never>, Tag extends keyof JSX.IntrinsicElements = 'div'>(options: TastyElementOptions<K, V, E, Tag>, secondArg?: never): ForwardRefExoticComponent<PropsWithoutRef<TastyElementProps<K, V, Tag>> & RefAttributes<unknown>> & SubElementComponents<E>;
101
101
  declare function tasty<Props extends PropsWithStyles, DefaultProps extends Partial<Props> = Partial<Props>>(Component: ComponentType<Props>, options?: TastyProps<never, never, Record<string, never>, Props>): ComponentType<TastyComponentPropsWithDefaults<Props, DefaultProps>>;
102
102
  declare const Element: ForwardRefExoticComponent<AllBaseProps<keyof HTMLElementTagNameMap> & {
103
+ top?: StyleValue<csstype.Property.Top<string | number> | undefined> | StyleValueStateMap<csstype.Property.Top<string | number> | undefined>;
104
+ right?: StyleValue<csstype.Property.Right<string | number> | undefined> | StyleValueStateMap<csstype.Property.Right<string | number> | undefined>;
105
+ bottom?: StyleValue<csstype.Property.Bottom<string | number> | undefined> | StyleValueStateMap<csstype.Property.Bottom<string | number> | undefined>;
106
+ left?: StyleValue<csstype.Property.Left<string | number> | undefined> | StyleValueStateMap<csstype.Property.Left<string | number> | undefined>;
103
107
  color?: StyleValue<string | boolean | undefined> | StyleValueStateMap<string | boolean | undefined>;
104
- fill?: StyleValue<boolean | (string & {}) | `#${string}` | `#${string}.0` | `#${string}.7` | `#${string}.1` | `#${string}.2` | `#${string}.3` | `#${string}.4` | `#${string}.5` | `#${string}.6` | `#${string}.8` | `#${string}.9` | `#${string}.00` | `#${string}.07` | `#${string}.01` | `#${string}.02` | `#${string}.03` | `#${string}.04` | `#${string}.05` | `#${string}.06` | `#${string}.08` | `#${string}.09` | `#${string}.70` | `#${string}.77` | `#${string}.71` | `#${string}.72` | `#${string}.73` | `#${string}.74` | `#${string}.75` | `#${string}.76` | `#${string}.78` | `#${string}.79` | `#${string}.10` | `#${string}.17` | `#${string}.11` | `#${string}.12` | `#${string}.13` | `#${string}.14` | `#${string}.15` | `#${string}.16` | `#${string}.18` | `#${string}.19` | `#${string}.20` | `#${string}.27` | `#${string}.21` | `#${string}.22` | `#${string}.23` | `#${string}.24` | `#${string}.25` | `#${string}.26` | `#${string}.28` | `#${string}.29` | `#${string}.30` | `#${string}.37` | `#${string}.31` | `#${string}.32` | `#${string}.33` | `#${string}.34` | `#${string}.35` | `#${string}.36` | `#${string}.38` | `#${string}.39` | `#${string}.40` | `#${string}.47` | `#${string}.41` | `#${string}.42` | `#${string}.43` | `#${string}.44` | `#${string}.45` | `#${string}.46` | `#${string}.48` | `#${string}.49` | `#${string}.50` | `#${string}.57` | `#${string}.51` | `#${string}.52` | `#${string}.53` | `#${string}.54` | `#${string}.55` | `#${string}.56` | `#${string}.58` | `#${string}.59` | `#${string}.60` | `#${string}.67` | `#${string}.61` | `#${string}.62` | `#${string}.63` | `#${string}.64` | `#${string}.65` | `#${string}.66` | `#${string}.68` | `#${string}.69` | `#${string}.80` | `#${string}.87` | `#${string}.81` | `#${string}.82` | `#${string}.83` | `#${string}.84` | `#${string}.85` | `#${string}.86` | `#${string}.88` | `#${string}.89` | `#${string}.90` | `#${string}.97` | `#${string}.91` | `#${string}.92` | `#${string}.93` | `#${string}.94` | `#${string}.95` | `#${string}.96` | `#${string}.98` | `#${string}.99` | `#${string}.100` | `rgb(${string})` | `rgba(${string})` | undefined> | StyleValueStateMap<boolean | (string & {}) | `#${string}` | `#${string}.0` | `#${string}.7` | `#${string}.1` | `#${string}.2` | `#${string}.3` | `#${string}.4` | `#${string}.5` | `#${string}.6` | `#${string}.8` | `#${string}.9` | `#${string}.00` | `#${string}.07` | `#${string}.01` | `#${string}.02` | `#${string}.03` | `#${string}.04` | `#${string}.05` | `#${string}.06` | `#${string}.08` | `#${string}.09` | `#${string}.70` | `#${string}.77` | `#${string}.71` | `#${string}.72` | `#${string}.73` | `#${string}.74` | `#${string}.75` | `#${string}.76` | `#${string}.78` | `#${string}.79` | `#${string}.10` | `#${string}.17` | `#${string}.11` | `#${string}.12` | `#${string}.13` | `#${string}.14` | `#${string}.15` | `#${string}.16` | `#${string}.18` | `#${string}.19` | `#${string}.20` | `#${string}.27` | `#${string}.21` | `#${string}.22` | `#${string}.23` | `#${string}.24` | `#${string}.25` | `#${string}.26` | `#${string}.28` | `#${string}.29` | `#${string}.30` | `#${string}.37` | `#${string}.31` | `#${string}.32` | `#${string}.33` | `#${string}.34` | `#${string}.35` | `#${string}.36` | `#${string}.38` | `#${string}.39` | `#${string}.40` | `#${string}.47` | `#${string}.41` | `#${string}.42` | `#${string}.43` | `#${string}.44` | `#${string}.45` | `#${string}.46` | `#${string}.48` | `#${string}.49` | `#${string}.50` | `#${string}.57` | `#${string}.51` | `#${string}.52` | `#${string}.53` | `#${string}.54` | `#${string}.55` | `#${string}.56` | `#${string}.58` | `#${string}.59` | `#${string}.60` | `#${string}.67` | `#${string}.61` | `#${string}.62` | `#${string}.63` | `#${string}.64` | `#${string}.65` | `#${string}.66` | `#${string}.68` | `#${string}.69` | `#${string}.80` | `#${string}.87` | `#${string}.81` | `#${string}.82` | `#${string}.83` | `#${string}.84` | `#${string}.85` | `#${string}.86` | `#${string}.88` | `#${string}.89` | `#${string}.90` | `#${string}.97` | `#${string}.91` | `#${string}.92` | `#${string}.93` | `#${string}.94` | `#${string}.95` | `#${string}.96` | `#${string}.98` | `#${string}.99` | `#${string}.100` | `rgb(${string})` | `rgba(${string})` | undefined>;
108
+ fill?: StyleValue<boolean | (string & {}) | `#${string}` | `#${string}.0` | `#${string}.1` | `#${string}.3` | `#${string}.2` | `#${string}.7` | `#${string}.4` | `#${string}.5` | `#${string}.6` | `#${string}.8` | `#${string}.9` | `#${string}.00` | `#${string}.01` | `#${string}.03` | `#${string}.02` | `#${string}.07` | `#${string}.04` | `#${string}.05` | `#${string}.06` | `#${string}.08` | `#${string}.09` | `#${string}.10` | `#${string}.11` | `#${string}.13` | `#${string}.12` | `#${string}.17` | `#${string}.14` | `#${string}.15` | `#${string}.16` | `#${string}.18` | `#${string}.19` | `#${string}.30` | `#${string}.31` | `#${string}.33` | `#${string}.32` | `#${string}.37` | `#${string}.34` | `#${string}.35` | `#${string}.36` | `#${string}.38` | `#${string}.39` | `#${string}.20` | `#${string}.21` | `#${string}.23` | `#${string}.22` | `#${string}.27` | `#${string}.24` | `#${string}.25` | `#${string}.26` | `#${string}.28` | `#${string}.29` | `#${string}.70` | `#${string}.71` | `#${string}.73` | `#${string}.72` | `#${string}.77` | `#${string}.74` | `#${string}.75` | `#${string}.76` | `#${string}.78` | `#${string}.79` | `#${string}.40` | `#${string}.41` | `#${string}.43` | `#${string}.42` | `#${string}.47` | `#${string}.44` | `#${string}.45` | `#${string}.46` | `#${string}.48` | `#${string}.49` | `#${string}.50` | `#${string}.51` | `#${string}.53` | `#${string}.52` | `#${string}.57` | `#${string}.54` | `#${string}.55` | `#${string}.56` | `#${string}.58` | `#${string}.59` | `#${string}.60` | `#${string}.61` | `#${string}.63` | `#${string}.62` | `#${string}.67` | `#${string}.64` | `#${string}.65` | `#${string}.66` | `#${string}.68` | `#${string}.69` | `#${string}.80` | `#${string}.81` | `#${string}.83` | `#${string}.82` | `#${string}.87` | `#${string}.84` | `#${string}.85` | `#${string}.86` | `#${string}.88` | `#${string}.89` | `#${string}.90` | `#${string}.91` | `#${string}.93` | `#${string}.92` | `#${string}.97` | `#${string}.94` | `#${string}.95` | `#${string}.96` | `#${string}.98` | `#${string}.99` | `#${string}.100` | `rgb(${string})` | `rgba(${string})` | undefined> | StyleValueStateMap<boolean | (string & {}) | `#${string}` | `#${string}.0` | `#${string}.1` | `#${string}.3` | `#${string}.2` | `#${string}.7` | `#${string}.4` | `#${string}.5` | `#${string}.6` | `#${string}.8` | `#${string}.9` | `#${string}.00` | `#${string}.01` | `#${string}.03` | `#${string}.02` | `#${string}.07` | `#${string}.04` | `#${string}.05` | `#${string}.06` | `#${string}.08` | `#${string}.09` | `#${string}.10` | `#${string}.11` | `#${string}.13` | `#${string}.12` | `#${string}.17` | `#${string}.14` | `#${string}.15` | `#${string}.16` | `#${string}.18` | `#${string}.19` | `#${string}.30` | `#${string}.31` | `#${string}.33` | `#${string}.32` | `#${string}.37` | `#${string}.34` | `#${string}.35` | `#${string}.36` | `#${string}.38` | `#${string}.39` | `#${string}.20` | `#${string}.21` | `#${string}.23` | `#${string}.22` | `#${string}.27` | `#${string}.24` | `#${string}.25` | `#${string}.26` | `#${string}.28` | `#${string}.29` | `#${string}.70` | `#${string}.71` | `#${string}.73` | `#${string}.72` | `#${string}.77` | `#${string}.74` | `#${string}.75` | `#${string}.76` | `#${string}.78` | `#${string}.79` | `#${string}.40` | `#${string}.41` | `#${string}.43` | `#${string}.42` | `#${string}.47` | `#${string}.44` | `#${string}.45` | `#${string}.46` | `#${string}.48` | `#${string}.49` | `#${string}.50` | `#${string}.51` | `#${string}.53` | `#${string}.52` | `#${string}.57` | `#${string}.54` | `#${string}.55` | `#${string}.56` | `#${string}.58` | `#${string}.59` | `#${string}.60` | `#${string}.61` | `#${string}.63` | `#${string}.62` | `#${string}.67` | `#${string}.64` | `#${string}.65` | `#${string}.66` | `#${string}.68` | `#${string}.69` | `#${string}.80` | `#${string}.81` | `#${string}.83` | `#${string}.82` | `#${string}.87` | `#${string}.84` | `#${string}.85` | `#${string}.86` | `#${string}.88` | `#${string}.89` | `#${string}.90` | `#${string}.91` | `#${string}.93` | `#${string}.92` | `#${string}.97` | `#${string}.94` | `#${string}.95` | `#${string}.96` | `#${string}.98` | `#${string}.99` | `#${string}.100` | `rgb(${string})` | `rgba(${string})` | undefined>;
105
109
  font?: StyleValue<boolean | csstype.Property.FontFamily | undefined> | StyleValueStateMap<boolean | csstype.Property.FontFamily | undefined>;
106
110
  outline?: StyleValue<string | boolean | undefined> | StyleValueStateMap<string | boolean | undefined>;
107
111
  gap?: StyleValue<boolean | csstype.Property.Gap<string | number> | undefined> | StyleValueStateMap<boolean | csstype.Property.Gap<string | number> | undefined>;
@@ -188,7 +192,6 @@ declare const Element: ForwardRefExoticComponent<AllBaseProps<keyof HTMLElementT
188
192
  borderTopRightRadius?: StyleValue<csstype.Property.BorderTopRightRadius<string | number> | undefined> | StyleValueStateMap<csstype.Property.BorderTopRightRadius<string | number> | undefined>;
189
193
  borderTopStyle?: StyleValue<csstype.Property.BorderTopStyle | undefined> | StyleValueStateMap<csstype.Property.BorderTopStyle | undefined>;
190
194
  borderTopWidth?: StyleValue<csstype.Property.BorderTopWidth<string | number> | undefined> | StyleValueStateMap<csstype.Property.BorderTopWidth<string | number> | undefined>;
191
- bottom?: StyleValue<csstype.Property.Bottom<string | number> | undefined> | StyleValueStateMap<csstype.Property.Bottom<string | number> | undefined>;
192
195
  boxDecorationBreak?: StyleValue<csstype.Property.BoxDecorationBreak | undefined> | StyleValueStateMap<csstype.Property.BoxDecorationBreak | undefined>;
193
196
  boxShadow?: StyleValue<csstype.Property.BoxShadow | undefined> | StyleValueStateMap<csstype.Property.BoxShadow | undefined>;
194
197
  boxSizing?: StyleValue<csstype.Property.BoxSizing | undefined> | StyleValueStateMap<csstype.Property.BoxSizing | undefined>;
@@ -301,7 +304,6 @@ declare const Element: ForwardRefExoticComponent<AllBaseProps<keyof HTMLElementT
301
304
  justifyItems?: StyleValue<csstype.Property.JustifyItems | undefined> | StyleValueStateMap<csstype.Property.JustifyItems | undefined>;
302
305
  justifySelf?: StyleValue<csstype.Property.JustifySelf | undefined> | StyleValueStateMap<csstype.Property.JustifySelf | undefined>;
303
306
  justifyTracks?: StyleValue<csstype.Property.JustifyTracks | undefined> | StyleValueStateMap<csstype.Property.JustifyTracks | undefined>;
304
- left?: StyleValue<csstype.Property.Left<string | number> | undefined> | StyleValueStateMap<csstype.Property.Left<string | number> | undefined>;
305
307
  letterSpacing?: StyleValue<csstype.Property.LetterSpacing<string | number> | undefined> | StyleValueStateMap<csstype.Property.LetterSpacing<string | number> | undefined>;
306
308
  lightingColor?: StyleValue<csstype.Property.LightingColor | undefined> | StyleValueStateMap<csstype.Property.LightingColor | undefined>;
307
309
  lineBreak?: StyleValue<csstype.Property.LineBreak | undefined> | StyleValueStateMap<csstype.Property.LineBreak | undefined>;
@@ -407,7 +409,6 @@ declare const Element: ForwardRefExoticComponent<AllBaseProps<keyof HTMLElementT
407
409
  quotes?: StyleValue<csstype.Property.Quotes | undefined> | StyleValueStateMap<csstype.Property.Quotes | undefined>;
408
410
  r?: StyleValue<csstype.Property.R<string | number> | undefined> | StyleValueStateMap<csstype.Property.R<string | number> | undefined>;
409
411
  resize?: StyleValue<csstype.Property.Resize | undefined> | StyleValueStateMap<csstype.Property.Resize | undefined>;
410
- right?: StyleValue<csstype.Property.Right<string | number> | undefined> | StyleValueStateMap<csstype.Property.Right<string | number> | undefined>;
411
412
  rotate?: StyleValue<csstype.Property.Rotate | undefined> | StyleValueStateMap<csstype.Property.Rotate | undefined>;
412
413
  rowGap?: StyleValue<csstype.Property.RowGap<string | number> | undefined> | StyleValueStateMap<csstype.Property.RowGap<string | number> | undefined>;
413
414
  rubyAlign?: StyleValue<csstype.Property.RubyAlign | undefined> | StyleValueStateMap<csstype.Property.RubyAlign | undefined>;
@@ -496,7 +497,6 @@ declare const Element: ForwardRefExoticComponent<AllBaseProps<keyof HTMLElementT
496
497
  textWrapMode?: StyleValue<csstype.Property.TextWrapMode | undefined> | StyleValueStateMap<csstype.Property.TextWrapMode | undefined>;
497
498
  textWrapStyle?: StyleValue<csstype.Property.TextWrapStyle | undefined> | StyleValueStateMap<csstype.Property.TextWrapStyle | undefined>;
498
499
  timelineScope?: StyleValue<csstype.Property.TimelineScope | undefined> | StyleValueStateMap<csstype.Property.TimelineScope | undefined>;
499
- top?: StyleValue<csstype.Property.Top<string | number> | undefined> | StyleValueStateMap<csstype.Property.Top<string | number> | undefined>;
500
500
  touchAction?: StyleValue<csstype.Property.TouchAction | undefined> | StyleValueStateMap<csstype.Property.TouchAction | undefined>;
501
501
  transform?: StyleValue<csstype.Property.Transform | undefined> | StyleValueStateMap<csstype.Property.Transform | undefined>;
502
502
  transformBox?: StyleValue<csstype.Property.TransformBox | undefined> | StyleValueStateMap<csstype.Property.TransformBox | undefined>;
@@ -958,7 +958,7 @@ declare const Element: ForwardRefExoticComponent<AllBaseProps<keyof HTMLElementT
958
958
  colorRendering?: StyleValue<csstype.Property.ColorRendering | undefined> | StyleValueStateMap<csstype.Property.ColorRendering | undefined>;
959
959
  glyphOrientationVertical?: StyleValue<csstype.Property.GlyphOrientationVertical | undefined> | StyleValueStateMap<csstype.Property.GlyphOrientationVertical | undefined>;
960
960
  image?: StyleValue<csstype.Property.BackgroundImage | undefined> | StyleValueStateMap<csstype.Property.BackgroundImage | undefined>;
961
- svgFill?: StyleValue<(string & {}) | `#${string}` | `#${string}.0` | `#${string}.7` | `#${string}.1` | `#${string}.2` | `#${string}.3` | `#${string}.4` | `#${string}.5` | `#${string}.6` | `#${string}.8` | `#${string}.9` | `#${string}.00` | `#${string}.07` | `#${string}.01` | `#${string}.02` | `#${string}.03` | `#${string}.04` | `#${string}.05` | `#${string}.06` | `#${string}.08` | `#${string}.09` | `#${string}.70` | `#${string}.77` | `#${string}.71` | `#${string}.72` | `#${string}.73` | `#${string}.74` | `#${string}.75` | `#${string}.76` | `#${string}.78` | `#${string}.79` | `#${string}.10` | `#${string}.17` | `#${string}.11` | `#${string}.12` | `#${string}.13` | `#${string}.14` | `#${string}.15` | `#${string}.16` | `#${string}.18` | `#${string}.19` | `#${string}.20` | `#${string}.27` | `#${string}.21` | `#${string}.22` | `#${string}.23` | `#${string}.24` | `#${string}.25` | `#${string}.26` | `#${string}.28` | `#${string}.29` | `#${string}.30` | `#${string}.37` | `#${string}.31` | `#${string}.32` | `#${string}.33` | `#${string}.34` | `#${string}.35` | `#${string}.36` | `#${string}.38` | `#${string}.39` | `#${string}.40` | `#${string}.47` | `#${string}.41` | `#${string}.42` | `#${string}.43` | `#${string}.44` | `#${string}.45` | `#${string}.46` | `#${string}.48` | `#${string}.49` | `#${string}.50` | `#${string}.57` | `#${string}.51` | `#${string}.52` | `#${string}.53` | `#${string}.54` | `#${string}.55` | `#${string}.56` | `#${string}.58` | `#${string}.59` | `#${string}.60` | `#${string}.67` | `#${string}.61` | `#${string}.62` | `#${string}.63` | `#${string}.64` | `#${string}.65` | `#${string}.66` | `#${string}.68` | `#${string}.69` | `#${string}.80` | `#${string}.87` | `#${string}.81` | `#${string}.82` | `#${string}.83` | `#${string}.84` | `#${string}.85` | `#${string}.86` | `#${string}.88` | `#${string}.89` | `#${string}.90` | `#${string}.97` | `#${string}.91` | `#${string}.92` | `#${string}.93` | `#${string}.94` | `#${string}.95` | `#${string}.96` | `#${string}.98` | `#${string}.99` | `#${string}.100` | `rgb(${string})` | `rgba(${string})` | undefined> | StyleValueStateMap<(string & {}) | `#${string}` | `#${string}.0` | `#${string}.7` | `#${string}.1` | `#${string}.2` | `#${string}.3` | `#${string}.4` | `#${string}.5` | `#${string}.6` | `#${string}.8` | `#${string}.9` | `#${string}.00` | `#${string}.07` | `#${string}.01` | `#${string}.02` | `#${string}.03` | `#${string}.04` | `#${string}.05` | `#${string}.06` | `#${string}.08` | `#${string}.09` | `#${string}.70` | `#${string}.77` | `#${string}.71` | `#${string}.72` | `#${string}.73` | `#${string}.74` | `#${string}.75` | `#${string}.76` | `#${string}.78` | `#${string}.79` | `#${string}.10` | `#${string}.17` | `#${string}.11` | `#${string}.12` | `#${string}.13` | `#${string}.14` | `#${string}.15` | `#${string}.16` | `#${string}.18` | `#${string}.19` | `#${string}.20` | `#${string}.27` | `#${string}.21` | `#${string}.22` | `#${string}.23` | `#${string}.24` | `#${string}.25` | `#${string}.26` | `#${string}.28` | `#${string}.29` | `#${string}.30` | `#${string}.37` | `#${string}.31` | `#${string}.32` | `#${string}.33` | `#${string}.34` | `#${string}.35` | `#${string}.36` | `#${string}.38` | `#${string}.39` | `#${string}.40` | `#${string}.47` | `#${string}.41` | `#${string}.42` | `#${string}.43` | `#${string}.44` | `#${string}.45` | `#${string}.46` | `#${string}.48` | `#${string}.49` | `#${string}.50` | `#${string}.57` | `#${string}.51` | `#${string}.52` | `#${string}.53` | `#${string}.54` | `#${string}.55` | `#${string}.56` | `#${string}.58` | `#${string}.59` | `#${string}.60` | `#${string}.67` | `#${string}.61` | `#${string}.62` | `#${string}.63` | `#${string}.64` | `#${string}.65` | `#${string}.66` | `#${string}.68` | `#${string}.69` | `#${string}.80` | `#${string}.87` | `#${string}.81` | `#${string}.82` | `#${string}.83` | `#${string}.84` | `#${string}.85` | `#${string}.86` | `#${string}.88` | `#${string}.89` | `#${string}.90` | `#${string}.97` | `#${string}.91` | `#${string}.92` | `#${string}.93` | `#${string}.94` | `#${string}.95` | `#${string}.96` | `#${string}.98` | `#${string}.99` | `#${string}.100` | `rgb(${string})` | `rgba(${string})` | undefined>;
961
+ svgFill?: StyleValue<(string & {}) | `#${string}` | `#${string}.0` | `#${string}.1` | `#${string}.3` | `#${string}.2` | `#${string}.7` | `#${string}.4` | `#${string}.5` | `#${string}.6` | `#${string}.8` | `#${string}.9` | `#${string}.00` | `#${string}.01` | `#${string}.03` | `#${string}.02` | `#${string}.07` | `#${string}.04` | `#${string}.05` | `#${string}.06` | `#${string}.08` | `#${string}.09` | `#${string}.10` | `#${string}.11` | `#${string}.13` | `#${string}.12` | `#${string}.17` | `#${string}.14` | `#${string}.15` | `#${string}.16` | `#${string}.18` | `#${string}.19` | `#${string}.30` | `#${string}.31` | `#${string}.33` | `#${string}.32` | `#${string}.37` | `#${string}.34` | `#${string}.35` | `#${string}.36` | `#${string}.38` | `#${string}.39` | `#${string}.20` | `#${string}.21` | `#${string}.23` | `#${string}.22` | `#${string}.27` | `#${string}.24` | `#${string}.25` | `#${string}.26` | `#${string}.28` | `#${string}.29` | `#${string}.70` | `#${string}.71` | `#${string}.73` | `#${string}.72` | `#${string}.77` | `#${string}.74` | `#${string}.75` | `#${string}.76` | `#${string}.78` | `#${string}.79` | `#${string}.40` | `#${string}.41` | `#${string}.43` | `#${string}.42` | `#${string}.47` | `#${string}.44` | `#${string}.45` | `#${string}.46` | `#${string}.48` | `#${string}.49` | `#${string}.50` | `#${string}.51` | `#${string}.53` | `#${string}.52` | `#${string}.57` | `#${string}.54` | `#${string}.55` | `#${string}.56` | `#${string}.58` | `#${string}.59` | `#${string}.60` | `#${string}.61` | `#${string}.63` | `#${string}.62` | `#${string}.67` | `#${string}.64` | `#${string}.65` | `#${string}.66` | `#${string}.68` | `#${string}.69` | `#${string}.80` | `#${string}.81` | `#${string}.83` | `#${string}.82` | `#${string}.87` | `#${string}.84` | `#${string}.85` | `#${string}.86` | `#${string}.88` | `#${string}.89` | `#${string}.90` | `#${string}.91` | `#${string}.93` | `#${string}.92` | `#${string}.97` | `#${string}.94` | `#${string}.95` | `#${string}.96` | `#${string}.98` | `#${string}.99` | `#${string}.100` | `rgb(${string})` | `rgba(${string})` | undefined> | StyleValueStateMap<(string & {}) | `#${string}` | `#${string}.0` | `#${string}.1` | `#${string}.3` | `#${string}.2` | `#${string}.7` | `#${string}.4` | `#${string}.5` | `#${string}.6` | `#${string}.8` | `#${string}.9` | `#${string}.00` | `#${string}.01` | `#${string}.03` | `#${string}.02` | `#${string}.07` | `#${string}.04` | `#${string}.05` | `#${string}.06` | `#${string}.08` | `#${string}.09` | `#${string}.10` | `#${string}.11` | `#${string}.13` | `#${string}.12` | `#${string}.17` | `#${string}.14` | `#${string}.15` | `#${string}.16` | `#${string}.18` | `#${string}.19` | `#${string}.30` | `#${string}.31` | `#${string}.33` | `#${string}.32` | `#${string}.37` | `#${string}.34` | `#${string}.35` | `#${string}.36` | `#${string}.38` | `#${string}.39` | `#${string}.20` | `#${string}.21` | `#${string}.23` | `#${string}.22` | `#${string}.27` | `#${string}.24` | `#${string}.25` | `#${string}.26` | `#${string}.28` | `#${string}.29` | `#${string}.70` | `#${string}.71` | `#${string}.73` | `#${string}.72` | `#${string}.77` | `#${string}.74` | `#${string}.75` | `#${string}.76` | `#${string}.78` | `#${string}.79` | `#${string}.40` | `#${string}.41` | `#${string}.43` | `#${string}.42` | `#${string}.47` | `#${string}.44` | `#${string}.45` | `#${string}.46` | `#${string}.48` | `#${string}.49` | `#${string}.50` | `#${string}.51` | `#${string}.53` | `#${string}.52` | `#${string}.57` | `#${string}.54` | `#${string}.55` | `#${string}.56` | `#${string}.58` | `#${string}.59` | `#${string}.60` | `#${string}.61` | `#${string}.63` | `#${string}.62` | `#${string}.67` | `#${string}.64` | `#${string}.65` | `#${string}.66` | `#${string}.68` | `#${string}.69` | `#${string}.80` | `#${string}.81` | `#${string}.83` | `#${string}.82` | `#${string}.87` | `#${string}.84` | `#${string}.85` | `#${string}.86` | `#${string}.88` | `#${string}.89` | `#${string}.90` | `#${string}.91` | `#${string}.93` | `#${string}.92` | `#${string}.97` | `#${string}.94` | `#${string}.95` | `#${string}.96` | `#${string}.98` | `#${string}.99` | `#${string}.100` | `rgb(${string})` | `rgba(${string})` | undefined>;
962
962
  fade?: StyleValue<string | undefined> | StyleValueStateMap<string | undefined>;
963
963
  styledScrollbar?: StyleValue<boolean | undefined> | StyleValueStateMap<boolean | undefined>;
964
964
  scrollbar?: StyleValue<string | number | boolean | undefined> | StyleValueStateMap<string | number | boolean | undefined>;
@@ -972,12 +972,12 @@ declare const Element: ForwardRefExoticComponent<AllBaseProps<keyof HTMLElementT
972
972
  gridRows?: StyleValue<csstype.Property.GridTemplateRows<string | number> | undefined> | StyleValueStateMap<csstype.Property.GridTemplateRows<string | number> | undefined>;
973
973
  preset?: StyleValue<string | (string & {}) | undefined> | StyleValueStateMap<string | (string & {}) | undefined>;
974
974
  align?: StyleValue<(string & {}) | "center" | "start" | "baseline" | "inherit" | "initial" | "end" | "-moz-initial" | "revert" | "revert-layer" | "unset" | "normal" | "space-around" | "space-between" | "space-evenly" | "stretch" | "flex-end" | "flex-start" | "self-end" | "self-start" | "anchor-center" | undefined> | StyleValueStateMap<(string & {}) | "center" | "start" | "baseline" | "inherit" | "initial" | "end" | "-moz-initial" | "revert" | "revert-layer" | "unset" | "normal" | "space-around" | "space-between" | "space-evenly" | "stretch" | "flex-end" | "flex-start" | "self-end" | "self-start" | "anchor-center" | undefined>;
975
- justify?: StyleValue<(string & {}) | "left" | "right" | "center" | "start" | "baseline" | "inherit" | "initial" | "end" | "-moz-initial" | "revert" | "revert-layer" | "unset" | "normal" | "space-around" | "space-between" | "space-evenly" | "stretch" | "flex-end" | "flex-start" | "self-end" | "self-start" | "anchor-center" | "legacy" | undefined> | StyleValueStateMap<(string & {}) | "left" | "right" | "center" | "start" | "baseline" | "inherit" | "initial" | "end" | "-moz-initial" | "revert" | "revert-layer" | "unset" | "normal" | "space-around" | "space-between" | "space-evenly" | "stretch" | "flex-end" | "flex-start" | "self-end" | "self-start" | "anchor-center" | "legacy" | undefined>;
975
+ justify?: StyleValue<"right" | "left" | (string & {}) | "center" | "start" | "baseline" | "inherit" | "initial" | "end" | "-moz-initial" | "revert" | "revert-layer" | "unset" | "normal" | "space-around" | "space-between" | "space-evenly" | "stretch" | "flex-end" | "flex-start" | "self-end" | "self-start" | "anchor-center" | "legacy" | undefined> | StyleValueStateMap<"right" | "left" | (string & {}) | "center" | "start" | "baseline" | "inherit" | "initial" | "end" | "-moz-initial" | "revert" | "revert-layer" | "unset" | "normal" | "space-around" | "space-between" | "space-evenly" | "stretch" | "flex-end" | "flex-start" | "self-end" | "self-start" | "anchor-center" | "legacy" | undefined>;
976
976
  place?: StyleValue<string | (string & {}) | undefined> | StyleValueStateMap<string | (string & {}) | undefined>;
977
977
  "@keyframes"?: StyleValue<Record<string, KeyframesSteps> | undefined> | StyleValueStateMap<Record<string, KeyframesSteps> | undefined>;
978
978
  "@properties"?: StyleValue<Record<string, PropertyDefinition> | undefined> | StyleValueStateMap<Record<string, PropertyDefinition> | undefined>;
979
979
  recipe?: StyleValue<string | undefined> | StyleValueStateMap<string | undefined>;
980
- } & BaseStyleProps & WithVariant<VariantMap> & Omit<Omit<AllHTMLAttributes<HTMLElement>, keyof react.ClassAttributes<HTMLDivElement> | keyof react.HTMLAttributes<HTMLDivElement>> & react.ClassAttributes<HTMLDivElement> & react.HTMLAttributes<HTMLDivElement>, "qa" | "qaVal" | "color" | "fill" | "font" | "outline" | "gap" | "padding" | "margin" | "width" | "height" | "border" | "transition" | "placeContent" | "placeItems" | "accentColor" | "alignContent" | "alignItems" | "alignSelf" | "alignTracks" | "alignmentBaseline" | "anchorName" | "anchorScope" | "animationComposition" | "animationDelay" | "animationDirection" | "animationDuration" | "animationFillMode" | "animationIterationCount" | "animationName" | "animationPlayState" | "animationRangeEnd" | "animationRangeStart" | "animationTimeline" | "animationTimingFunction" | "appearance" | "aspectRatio" | "backdropFilter" | "backfaceVisibility" | "backgroundAttachment" | "backgroundBlendMode" | "backgroundClip" | "backgroundColor" | "backgroundImage" | "backgroundOrigin" | "backgroundPositionX" | "backgroundPositionY" | "backgroundRepeat" | "backgroundSize" | "baselineShift" | "blockSize" | "borderBlockEndColor" | "borderBlockEndStyle" | "borderBlockEndWidth" | "borderBlockStartColor" | "borderBlockStartStyle" | "borderBlockStartWidth" | "borderBottomColor" | "borderBottomLeftRadius" | "borderBottomRightRadius" | "borderBottomStyle" | "borderBottomWidth" | "borderCollapse" | "borderEndEndRadius" | "borderEndStartRadius" | "borderImageOutset" | "borderImageRepeat" | "borderImageSlice" | "borderImageSource" | "borderImageWidth" | "borderInlineEndColor" | "borderInlineEndStyle" | "borderInlineEndWidth" | "borderInlineStartColor" | "borderInlineStartStyle" | "borderInlineStartWidth" | "borderLeftColor" | "borderLeftStyle" | "borderLeftWidth" | "borderRightColor" | "borderRightStyle" | "borderRightWidth" | "borderSpacing" | "borderStartEndRadius" | "borderStartStartRadius" | "borderTopColor" | "borderTopLeftRadius" | "borderTopRightRadius" | "borderTopStyle" | "borderTopWidth" | "bottom" | "boxDecorationBreak" | "boxShadow" | "boxSizing" | "breakAfter" | "breakBefore" | "breakInside" | "captionSide" | "caretColor" | "caretShape" | "clear" | "clipPath" | "clipRule" | "colorAdjust" | "colorInterpolationFilters" | "colorScheme" | "columnCount" | "columnFill" | "columnGap" | "columnRuleColor" | "columnRuleStyle" | "columnRuleWidth" | "columnSpan" | "columnWidth" | "contain" | "containIntrinsicBlockSize" | "containIntrinsicHeight" | "containIntrinsicInlineSize" | "containIntrinsicWidth" | "containerName" | "containerType" | "content" | "contentVisibility" | "counterIncrement" | "counterReset" | "counterSet" | "cursor" | "cx" | "cy" | "d" | "direction" | "display" | "dominantBaseline" | "emptyCells" | "fieldSizing" | "fillOpacity" | "fillRule" | "filter" | "flexBasis" | "flexDirection" | "flexGrow" | "flexShrink" | "flexWrap" | "float" | "floodColor" | "floodOpacity" | "fontFamily" | "fontFeatureSettings" | "fontKerning" | "fontLanguageOverride" | "fontOpticalSizing" | "fontPalette" | "fontSize" | "fontSizeAdjust" | "fontSmooth" | "fontStyle" | "fontSynthesis" | "fontSynthesisPosition" | "fontSynthesisSmallCaps" | "fontSynthesisStyle" | "fontSynthesisWeight" | "fontVariant" | "fontVariantAlternates" | "fontVariantCaps" | "fontVariantEastAsian" | "fontVariantEmoji" | "fontVariantLigatures" | "fontVariantNumeric" | "fontVariantPosition" | "fontVariationSettings" | "fontWeight" | "fontWidth" | "forcedColorAdjust" | "gridAutoColumns" | "gridAutoFlow" | "gridAutoRows" | "gridColumnEnd" | "gridColumnStart" | "gridRowEnd" | "gridRowStart" | "gridTemplateAreas" | "gridTemplateColumns" | "gridTemplateRows" | "hangingPunctuation" | "hyphenateCharacter" | "hyphenateLimitChars" | "hyphens" | "imageOrientation" | "imageRendering" | "imageResolution" | "initialLetter" | "initialLetterAlign" | "inlineSize" | "insetBlockEnd" | "insetBlockStart" | "insetInlineEnd" | "insetInlineStart" | "interpolateSize" | "isolation" | "justifyContent" | "justifyItems" | "justifySelf" | "justifyTracks" | "left" | "letterSpacing" | "lightingColor" | "lineBreak" | "lineHeight" | "lineHeightStep" | "listStyleImage" | "listStylePosition" | "listStyleType" | "marginBlockEnd" | "marginBlockStart" | "marginBottom" | "marginInlineEnd" | "marginInlineStart" | "marginLeft" | "marginRight" | "marginTop" | "marginTrim" | "marker" | "markerEnd" | "markerMid" | "markerStart" | "maskBorderMode" | "maskBorderOutset" | "maskBorderRepeat" | "maskBorderSlice" | "maskBorderSource" | "maskBorderWidth" | "maskClip" | "maskComposite" | "maskImage" | "maskMode" | "maskOrigin" | "maskPosition" | "maskRepeat" | "maskSize" | "maskType" | "masonryAutoFlow" | "mathDepth" | "mathShift" | "mathStyle" | "maxBlockSize" | "maxHeight" | "maxInlineSize" | "maxLines" | "maxWidth" | "minBlockSize" | "minHeight" | "minInlineSize" | "minWidth" | "mixBlendMode" | "motionDistance" | "motionPath" | "motionRotation" | "objectFit" | "objectPosition" | "objectViewBox" | "offsetAnchor" | "offsetDistance" | "offsetPath" | "offsetPosition" | "offsetRotate" | "offsetRotation" | "opacity" | "order" | "orphans" | "outlineColor" | "outlineOffset" | "outlineStyle" | "outlineWidth" | "overflowAnchor" | "overflowBlock" | "overflowClipBox" | "overflowClipMargin" | "overflowInline" | "overflowWrap" | "overflowX" | "overflowY" | "overlay" | "overscrollBehaviorBlock" | "overscrollBehaviorInline" | "overscrollBehaviorX" | "overscrollBehaviorY" | "paddingBlockEnd" | "paddingBlockStart" | "paddingBottom" | "paddingInlineEnd" | "paddingInlineStart" | "paddingLeft" | "paddingRight" | "paddingTop" | "page" | "paintOrder" | "perspective" | "perspectiveOrigin" | "pointerEvents" | "position" | "positionAnchor" | "positionArea" | "positionTryFallbacks" | "positionTryOrder" | "positionVisibility" | "printColorAdjust" | "quotes" | "r" | "resize" | "right" | "rotate" | "rowGap" | "rubyAlign" | "rubyMerge" | "rubyOverhang" | "rubyPosition" | "rx" | "ry" | "scale" | "scrollBehavior" | "scrollInitialTarget" | "scrollMarginBlockEnd" | "scrollMarginBlockStart" | "scrollMarginBottom" | "scrollMarginInlineEnd" | "scrollMarginInlineStart" | "scrollMarginLeft" | "scrollMarginRight" | "scrollMarginTop" | "scrollPaddingBlockEnd" | "scrollPaddingBlockStart" | "scrollPaddingBottom" | "scrollPaddingInlineEnd" | "scrollPaddingInlineStart" | "scrollPaddingLeft" | "scrollPaddingRight" | "scrollPaddingTop" | "scrollSnapAlign" | "scrollSnapMarginBottom" | "scrollSnapMarginLeft" | "scrollSnapMarginRight" | "scrollSnapMarginTop" | "scrollSnapStop" | "scrollSnapType" | "scrollTimelineAxis" | "scrollTimelineName" | "scrollbarColor" | "scrollbarGutter" | "scrollbarWidth" | "shapeImageThreshold" | "shapeMargin" | "shapeOutside" | "shapeRendering" | "speakAs" | "stopColor" | "stopOpacity" | "stroke" | "strokeColor" | "strokeDasharray" | "strokeDashoffset" | "strokeLinecap" | "strokeLinejoin" | "strokeMiterlimit" | "strokeOpacity" | "strokeWidth" | "tabSize" | "tableLayout" | "textAlign" | "textAlignLast" | "textAnchor" | "textAutospace" | "textBox" | "textBoxEdge" | "textBoxTrim" | "textCombineUpright" | "textDecorationColor" | "textDecorationLine" | "textDecorationSkip" | "textDecorationSkipInk" | "textDecorationStyle" | "textDecorationThickness" | "textEmphasisColor" | "textEmphasisPosition" | "textEmphasisStyle" | "textIndent" | "textJustify" | "textOrientation" | "textOverflow" | "textRendering" | "textShadow" | "textSizeAdjust" | "textSpacingTrim" | "textTransform" | "textUnderlineOffset" | "textUnderlinePosition" | "textWrapMode" | "textWrapStyle" | "timelineScope" | "top" | "touchAction" | "transform" | "transformBox" | "transformOrigin" | "transformStyle" | "transitionBehavior" | "transitionDelay" | "transitionDuration" | "transitionProperty" | "transitionTimingFunction" | "translate" | "unicodeBidi" | "userSelect" | "vectorEffect" | "verticalAlign" | "viewTimelineAxis" | "viewTimelineInset" | "viewTimelineName" | "viewTransitionClass" | "viewTransitionName" | "visibility" | "whiteSpace" | "whiteSpaceCollapse" | "widows" | "willChange" | "wordBreak" | "wordSpacing" | "wordWrap" | "writingMode" | "x" | "y" | "zIndex" | "zoom" | "all" | "animation" | "animationRange" | "background" | "backgroundPosition" | "borderBlock" | "borderBlockColor" | "borderBlockEnd" | "borderBlockStart" | "borderBlockStyle" | "borderBlockWidth" | "borderBottom" | "borderColor" | "borderImage" | "borderInline" | "borderInlineColor" | "borderInlineEnd" | "borderInlineStart" | "borderInlineStyle" | "borderInlineWidth" | "borderLeft" | "borderRadius" | "borderRight" | "borderStyle" | "borderTop" | "borderWidth" | "caret" | "columnRule" | "columns" | "containIntrinsicSize" | "container" | "flex" | "flexFlow" | "grid" | "gridArea" | "gridColumn" | "gridRow" | "gridTemplate" | "inset" | "insetBlock" | "insetInline" | "lineClamp" | "listStyle" | "marginBlock" | "marginInline" | "mask" | "maskBorder" | "motion" | "offset" | "overflow" | "overscrollBehavior" | "paddingBlock" | "paddingInline" | "placeSelf" | "positionTry" | "scrollMargin" | "scrollMarginBlock" | "scrollMarginInline" | "scrollPadding" | "scrollPaddingBlock" | "scrollPaddingInline" | "scrollSnapMargin" | "scrollTimeline" | "textDecoration" | "textEmphasis" | "textWrap" | "viewTimeline" | "MozAnimationDelay" | "MozAnimationDirection" | "MozAnimationDuration" | "MozAnimationFillMode" | "MozAnimationIterationCount" | "MozAnimationName" | "MozAnimationPlayState" | "MozAnimationTimingFunction" | "MozAppearance" | "MozBackfaceVisibility" | "MozBinding" | "MozBorderBottomColors" | "MozBorderEndColor" | "MozBorderEndStyle" | "MozBorderEndWidth" | "MozBorderLeftColors" | "MozBorderRightColors" | "MozBorderStartColor" | "MozBorderStartStyle" | "MozBorderTopColors" | "MozBoxSizing" | "MozColumnRuleColor" | "MozColumnRuleStyle" | "MozColumnRuleWidth" | "MozColumnWidth" | "MozContextProperties" | "MozFontFeatureSettings" | "MozFontLanguageOverride" | "MozHyphens" | "MozMarginEnd" | "MozMarginStart" | "MozOrient" | "MozOsxFontSmoothing" | "MozOutlineRadiusBottomleft" | "MozOutlineRadiusBottomright" | "MozOutlineRadiusTopleft" | "MozOutlineRadiusTopright" | "MozPaddingEnd" | "MozPaddingStart" | "MozPerspective" | "MozPerspectiveOrigin" | "MozStackSizing" | "MozTabSize" | "MozTextBlink" | "MozTextSizeAdjust" | "MozTransform" | "MozTransformOrigin" | "MozTransformStyle" | "MozUserModify" | "MozUserSelect" | "MozWindowDragging" | "MozWindowShadow" | "msAccelerator" | "msBlockProgression" | "msContentZoomChaining" | "msContentZoomLimitMax" | "msContentZoomLimitMin" | "msContentZoomSnapPoints" | "msContentZoomSnapType" | "msContentZooming" | "msFilter" | "msFlexDirection" | "msFlexPositive" | "msFlowFrom" | "msFlowInto" | "msGridColumns" | "msGridRows" | "msHighContrastAdjust" | "msHyphenateLimitChars" | "msHyphenateLimitLines" | "msHyphenateLimitZone" | "msHyphens" | "msImeAlign" | "msLineBreak" | "msOrder" | "msOverflowStyle" | "msOverflowX" | "msOverflowY" | "msScrollChaining" | "msScrollLimitXMax" | "msScrollLimitXMin" | "msScrollLimitYMax" | "msScrollLimitYMin" | "msScrollRails" | "msScrollSnapPointsX" | "msScrollSnapPointsY" | "msScrollSnapType" | "msScrollTranslation" | "msScrollbar3dlightColor" | "msScrollbarArrowColor" | "msScrollbarBaseColor" | "msScrollbarDarkshadowColor" | "msScrollbarFaceColor" | "msScrollbarHighlightColor" | "msScrollbarShadowColor" | "msScrollbarTrackColor" | "msTextAutospace" | "msTextCombineHorizontal" | "msTextOverflow" | "msTouchAction" | "msTouchSelect" | "msTransform" | "msTransformOrigin" | "msTransitionDelay" | "msTransitionDuration" | "msTransitionProperty" | "msTransitionTimingFunction" | "msUserSelect" | "msWordBreak" | "msWrapFlow" | "msWrapMargin" | "msWrapThrough" | "msWritingMode" | "WebkitAlignContent" | "WebkitAlignItems" | "WebkitAlignSelf" | "WebkitAnimationDelay" | "WebkitAnimationDirection" | "WebkitAnimationDuration" | "WebkitAnimationFillMode" | "WebkitAnimationIterationCount" | "WebkitAnimationName" | "WebkitAnimationPlayState" | "WebkitAnimationTimingFunction" | "WebkitAppearance" | "WebkitBackdropFilter" | "WebkitBackfaceVisibility" | "WebkitBackgroundClip" | "WebkitBackgroundOrigin" | "WebkitBackgroundSize" | "WebkitBorderBeforeColor" | "WebkitBorderBeforeStyle" | "WebkitBorderBeforeWidth" | "WebkitBorderBottomLeftRadius" | "WebkitBorderBottomRightRadius" | "WebkitBorderImageSlice" | "WebkitBorderTopLeftRadius" | "WebkitBorderTopRightRadius" | "WebkitBoxDecorationBreak" | "WebkitBoxReflect" | "WebkitBoxShadow" | "WebkitBoxSizing" | "WebkitClipPath" | "WebkitColumnCount" | "WebkitColumnFill" | "WebkitColumnRuleColor" | "WebkitColumnRuleStyle" | "WebkitColumnRuleWidth" | "WebkitColumnSpan" | "WebkitColumnWidth" | "WebkitFilter" | "WebkitFlexBasis" | "WebkitFlexDirection" | "WebkitFlexGrow" | "WebkitFlexShrink" | "WebkitFlexWrap" | "WebkitFontFeatureSettings" | "WebkitFontKerning" | "WebkitFontSmoothing" | "WebkitFontVariantLigatures" | "WebkitHyphenateCharacter" | "WebkitHyphens" | "WebkitInitialLetter" | "WebkitJustifyContent" | "WebkitLineBreak" | "WebkitLineClamp" | "WebkitLogicalHeight" | "WebkitLogicalWidth" | "WebkitMarginEnd" | "WebkitMarginStart" | "WebkitMaskAttachment" | "WebkitMaskBoxImageOutset" | "WebkitMaskBoxImageRepeat" | "WebkitMaskBoxImageSlice" | "WebkitMaskBoxImageSource" | "WebkitMaskBoxImageWidth" | "WebkitMaskClip" | "WebkitMaskComposite" | "WebkitMaskImage" | "WebkitMaskOrigin" | "WebkitMaskPosition" | "WebkitMaskPositionX" | "WebkitMaskPositionY" | "WebkitMaskRepeat" | "WebkitMaskRepeatX" | "WebkitMaskRepeatY" | "WebkitMaskSize" | "WebkitMaxInlineSize" | "WebkitOrder" | "WebkitOverflowScrolling" | "WebkitPaddingEnd" | "WebkitPaddingStart" | "WebkitPerspective" | "WebkitPerspectiveOrigin" | "WebkitPrintColorAdjust" | "WebkitRubyPosition" | "WebkitScrollSnapType" | "WebkitShapeMargin" | "WebkitTapHighlightColor" | "WebkitTextCombine" | "WebkitTextDecorationColor" | "WebkitTextDecorationLine" | "WebkitTextDecorationSkip" | "WebkitTextDecorationStyle" | "WebkitTextEmphasisColor" | "WebkitTextEmphasisPosition" | "WebkitTextEmphasisStyle" | "WebkitTextFillColor" | "WebkitTextOrientation" | "WebkitTextSizeAdjust" | "WebkitTextStrokeColor" | "WebkitTextStrokeWidth" | "WebkitTextUnderlinePosition" | "WebkitTouchCallout" | "WebkitTransform" | "WebkitTransformOrigin" | "WebkitTransformStyle" | "WebkitTransitionDelay" | "WebkitTransitionDuration" | "WebkitTransitionProperty" | "WebkitTransitionTimingFunction" | "WebkitUserModify" | "WebkitUserSelect" | "WebkitWritingMode" | "MozAnimation" | "MozBorderImage" | "MozColumnRule" | "MozColumns" | "MozOutlineRadius" | "MozTransition" | "msContentZoomLimit" | "msContentZoomSnap" | "msFlex" | "msScrollLimit" | "msScrollSnapX" | "msScrollSnapY" | "msTransition" | "WebkitAnimation" | "WebkitBorderBefore" | "WebkitBorderImage" | "WebkitBorderRadius" | "WebkitColumnRule" | "WebkitColumns" | "WebkitFlex" | "WebkitFlexFlow" | "WebkitMask" | "WebkitMaskBoxImage" | "WebkitTextEmphasis" | "WebkitTextStroke" | "WebkitTransition" | "boxAlign" | "boxDirection" | "boxFlex" | "boxFlexGroup" | "boxLines" | "boxOrdinalGroup" | "boxOrient" | "boxPack" | "clip" | "fontStretch" | "gridColumnGap" | "gridGap" | "gridRowGap" | "imeMode" | "insetArea" | "offsetBlock" | "offsetBlockEnd" | "offsetBlockStart" | "offsetInline" | "offsetInlineEnd" | "offsetInlineStart" | "pageBreakAfter" | "pageBreakBefore" | "pageBreakInside" | "positionTryOptions" | "scrollSnapCoordinate" | "scrollSnapDestination" | "scrollSnapPointsX" | "scrollSnapPointsY" | "scrollSnapTypeX" | "scrollSnapTypeY" | "KhtmlBoxAlign" | "KhtmlBoxDirection" | "KhtmlBoxFlex" | "KhtmlBoxFlexGroup" | "KhtmlBoxLines" | "KhtmlBoxOrdinalGroup" | "KhtmlBoxOrient" | "KhtmlBoxPack" | "KhtmlLineBreak" | "KhtmlOpacity" | "KhtmlUserSelect" | "MozBackgroundClip" | "MozBackgroundOrigin" | "MozBackgroundSize" | "MozBorderRadius" | "MozBorderRadiusBottomleft" | "MozBorderRadiusBottomright" | "MozBorderRadiusTopleft" | "MozBorderRadiusTopright" | "MozBoxAlign" | "MozBoxDirection" | "MozBoxFlex" | "MozBoxOrdinalGroup" | "MozBoxOrient" | "MozBoxPack" | "MozBoxShadow" | "MozColumnCount" | "MozColumnFill" | "MozFloatEdge" | "MozForceBrokenImageIcon" | "MozOpacity" | "MozOutline" | "MozOutlineColor" | "MozOutlineStyle" | "MozOutlineWidth" | "MozTextAlignLast" | "MozTextDecorationColor" | "MozTextDecorationLine" | "MozTextDecorationStyle" | "MozTransitionDelay" | "MozTransitionDuration" | "MozTransitionProperty" | "MozTransitionTimingFunction" | "MozUserFocus" | "MozUserInput" | "msImeMode" | "OAnimation" | "OAnimationDelay" | "OAnimationDirection" | "OAnimationDuration" | "OAnimationFillMode" | "OAnimationIterationCount" | "OAnimationName" | "OAnimationPlayState" | "OAnimationTimingFunction" | "OBackgroundSize" | "OBorderImage" | "OObjectFit" | "OObjectPosition" | "OTabSize" | "OTextOverflow" | "OTransform" | "OTransformOrigin" | "OTransition" | "OTransitionDelay" | "OTransitionDuration" | "OTransitionProperty" | "OTransitionTimingFunction" | "WebkitBoxAlign" | "WebkitBoxDirection" | "WebkitBoxFlex" | "WebkitBoxFlexGroup" | "WebkitBoxLines" | "WebkitBoxOrdinalGroup" | "WebkitBoxOrient" | "WebkitBoxPack" | "colorInterpolation" | "colorRendering" | "glyphOrientationVertical" | "image" | "svgFill" | "fade" | "styledScrollbar" | "scrollbar" | "boldFontWeight" | "hide" | "shadow" | "radius" | "flow" | "gridAreas" | "gridColumns" | "gridRows" | "preset" | "align" | "justify" | "place" | "@keyframes" | "@properties" | "recipe" | "style" | "as" | "element" | "styles" | "breakpoints" | "block" | "inline" | "mods" | "isHidden" | "isDisabled" | "css" | "theme" | "tokens" | "ref"> & RefAttributes<unknown>> & SubElementComponents<Record<string, never>>;
980
+ } & BaseStyleProps & WithVariant<VariantMap> & Omit<Omit<AllHTMLAttributes<HTMLElement>, keyof react.ClassAttributes<HTMLDivElement> | keyof react.HTMLAttributes<HTMLDivElement>> & react.ClassAttributes<HTMLDivElement> & react.HTMLAttributes<HTMLDivElement>, "top" | "right" | "bottom" | "left" | "qa" | "qaVal" | "color" | "fill" | "font" | "outline" | "gap" | "padding" | "margin" | "width" | "height" | "border" | "transition" | "placeContent" | "placeItems" | "accentColor" | "alignContent" | "alignItems" | "alignSelf" | "alignTracks" | "alignmentBaseline" | "anchorName" | "anchorScope" | "animationComposition" | "animationDelay" | "animationDirection" | "animationDuration" | "animationFillMode" | "animationIterationCount" | "animationName" | "animationPlayState" | "animationRangeEnd" | "animationRangeStart" | "animationTimeline" | "animationTimingFunction" | "appearance" | "aspectRatio" | "backdropFilter" | "backfaceVisibility" | "backgroundAttachment" | "backgroundBlendMode" | "backgroundClip" | "backgroundColor" | "backgroundImage" | "backgroundOrigin" | "backgroundPositionX" | "backgroundPositionY" | "backgroundRepeat" | "backgroundSize" | "baselineShift" | "blockSize" | "borderBlockEndColor" | "borderBlockEndStyle" | "borderBlockEndWidth" | "borderBlockStartColor" | "borderBlockStartStyle" | "borderBlockStartWidth" | "borderBottomColor" | "borderBottomLeftRadius" | "borderBottomRightRadius" | "borderBottomStyle" | "borderBottomWidth" | "borderCollapse" | "borderEndEndRadius" | "borderEndStartRadius" | "borderImageOutset" | "borderImageRepeat" | "borderImageSlice" | "borderImageSource" | "borderImageWidth" | "borderInlineEndColor" | "borderInlineEndStyle" | "borderInlineEndWidth" | "borderInlineStartColor" | "borderInlineStartStyle" | "borderInlineStartWidth" | "borderLeftColor" | "borderLeftStyle" | "borderLeftWidth" | "borderRightColor" | "borderRightStyle" | "borderRightWidth" | "borderSpacing" | "borderStartEndRadius" | "borderStartStartRadius" | "borderTopColor" | "borderTopLeftRadius" | "borderTopRightRadius" | "borderTopStyle" | "borderTopWidth" | "boxDecorationBreak" | "boxShadow" | "boxSizing" | "breakAfter" | "breakBefore" | "breakInside" | "captionSide" | "caretColor" | "caretShape" | "clear" | "clipPath" | "clipRule" | "colorAdjust" | "colorInterpolationFilters" | "colorScheme" | "columnCount" | "columnFill" | "columnGap" | "columnRuleColor" | "columnRuleStyle" | "columnRuleWidth" | "columnSpan" | "columnWidth" | "contain" | "containIntrinsicBlockSize" | "containIntrinsicHeight" | "containIntrinsicInlineSize" | "containIntrinsicWidth" | "containerName" | "containerType" | "content" | "contentVisibility" | "counterIncrement" | "counterReset" | "counterSet" | "cursor" | "cx" | "cy" | "d" | "direction" | "display" | "dominantBaseline" | "emptyCells" | "fieldSizing" | "fillOpacity" | "fillRule" | "filter" | "flexBasis" | "flexDirection" | "flexGrow" | "flexShrink" | "flexWrap" | "float" | "floodColor" | "floodOpacity" | "fontFamily" | "fontFeatureSettings" | "fontKerning" | "fontLanguageOverride" | "fontOpticalSizing" | "fontPalette" | "fontSize" | "fontSizeAdjust" | "fontSmooth" | "fontStyle" | "fontSynthesis" | "fontSynthesisPosition" | "fontSynthesisSmallCaps" | "fontSynthesisStyle" | "fontSynthesisWeight" | "fontVariant" | "fontVariantAlternates" | "fontVariantCaps" | "fontVariantEastAsian" | "fontVariantEmoji" | "fontVariantLigatures" | "fontVariantNumeric" | "fontVariantPosition" | "fontVariationSettings" | "fontWeight" | "fontWidth" | "forcedColorAdjust" | "gridAutoColumns" | "gridAutoFlow" | "gridAutoRows" | "gridColumnEnd" | "gridColumnStart" | "gridRowEnd" | "gridRowStart" | "gridTemplateAreas" | "gridTemplateColumns" | "gridTemplateRows" | "hangingPunctuation" | "hyphenateCharacter" | "hyphenateLimitChars" | "hyphens" | "imageOrientation" | "imageRendering" | "imageResolution" | "initialLetter" | "initialLetterAlign" | "inlineSize" | "insetBlockEnd" | "insetBlockStart" | "insetInlineEnd" | "insetInlineStart" | "interpolateSize" | "isolation" | "justifyContent" | "justifyItems" | "justifySelf" | "justifyTracks" | "letterSpacing" | "lightingColor" | "lineBreak" | "lineHeight" | "lineHeightStep" | "listStyleImage" | "listStylePosition" | "listStyleType" | "marginBlockEnd" | "marginBlockStart" | "marginBottom" | "marginInlineEnd" | "marginInlineStart" | "marginLeft" | "marginRight" | "marginTop" | "marginTrim" | "marker" | "markerEnd" | "markerMid" | "markerStart" | "maskBorderMode" | "maskBorderOutset" | "maskBorderRepeat" | "maskBorderSlice" | "maskBorderSource" | "maskBorderWidth" | "maskClip" | "maskComposite" | "maskImage" | "maskMode" | "maskOrigin" | "maskPosition" | "maskRepeat" | "maskSize" | "maskType" | "masonryAutoFlow" | "mathDepth" | "mathShift" | "mathStyle" | "maxBlockSize" | "maxHeight" | "maxInlineSize" | "maxLines" | "maxWidth" | "minBlockSize" | "minHeight" | "minInlineSize" | "minWidth" | "mixBlendMode" | "motionDistance" | "motionPath" | "motionRotation" | "objectFit" | "objectPosition" | "objectViewBox" | "offsetAnchor" | "offsetDistance" | "offsetPath" | "offsetPosition" | "offsetRotate" | "offsetRotation" | "opacity" | "order" | "orphans" | "outlineColor" | "outlineOffset" | "outlineStyle" | "outlineWidth" | "overflowAnchor" | "overflowBlock" | "overflowClipBox" | "overflowClipMargin" | "overflowInline" | "overflowWrap" | "overflowX" | "overflowY" | "overlay" | "overscrollBehaviorBlock" | "overscrollBehaviorInline" | "overscrollBehaviorX" | "overscrollBehaviorY" | "paddingBlockEnd" | "paddingBlockStart" | "paddingBottom" | "paddingInlineEnd" | "paddingInlineStart" | "paddingLeft" | "paddingRight" | "paddingTop" | "page" | "paintOrder" | "perspective" | "perspectiveOrigin" | "pointerEvents" | "position" | "positionAnchor" | "positionArea" | "positionTryFallbacks" | "positionTryOrder" | "positionVisibility" | "printColorAdjust" | "quotes" | "r" | "resize" | "rotate" | "rowGap" | "rubyAlign" | "rubyMerge" | "rubyOverhang" | "rubyPosition" | "rx" | "ry" | "scale" | "scrollBehavior" | "scrollInitialTarget" | "scrollMarginBlockEnd" | "scrollMarginBlockStart" | "scrollMarginBottom" | "scrollMarginInlineEnd" | "scrollMarginInlineStart" | "scrollMarginLeft" | "scrollMarginRight" | "scrollMarginTop" | "scrollPaddingBlockEnd" | "scrollPaddingBlockStart" | "scrollPaddingBottom" | "scrollPaddingInlineEnd" | "scrollPaddingInlineStart" | "scrollPaddingLeft" | "scrollPaddingRight" | "scrollPaddingTop" | "scrollSnapAlign" | "scrollSnapMarginBottom" | "scrollSnapMarginLeft" | "scrollSnapMarginRight" | "scrollSnapMarginTop" | "scrollSnapStop" | "scrollSnapType" | "scrollTimelineAxis" | "scrollTimelineName" | "scrollbarColor" | "scrollbarGutter" | "scrollbarWidth" | "shapeImageThreshold" | "shapeMargin" | "shapeOutside" | "shapeRendering" | "speakAs" | "stopColor" | "stopOpacity" | "stroke" | "strokeColor" | "strokeDasharray" | "strokeDashoffset" | "strokeLinecap" | "strokeLinejoin" | "strokeMiterlimit" | "strokeOpacity" | "strokeWidth" | "tabSize" | "tableLayout" | "textAlign" | "textAlignLast" | "textAnchor" | "textAutospace" | "textBox" | "textBoxEdge" | "textBoxTrim" | "textCombineUpright" | "textDecorationColor" | "textDecorationLine" | "textDecorationSkip" | "textDecorationSkipInk" | "textDecorationStyle" | "textDecorationThickness" | "textEmphasisColor" | "textEmphasisPosition" | "textEmphasisStyle" | "textIndent" | "textJustify" | "textOrientation" | "textOverflow" | "textRendering" | "textShadow" | "textSizeAdjust" | "textSpacingTrim" | "textTransform" | "textUnderlineOffset" | "textUnderlinePosition" | "textWrapMode" | "textWrapStyle" | "timelineScope" | "touchAction" | "transform" | "transformBox" | "transformOrigin" | "transformStyle" | "transitionBehavior" | "transitionDelay" | "transitionDuration" | "transitionProperty" | "transitionTimingFunction" | "translate" | "unicodeBidi" | "userSelect" | "vectorEffect" | "verticalAlign" | "viewTimelineAxis" | "viewTimelineInset" | "viewTimelineName" | "viewTransitionClass" | "viewTransitionName" | "visibility" | "whiteSpace" | "whiteSpaceCollapse" | "widows" | "willChange" | "wordBreak" | "wordSpacing" | "wordWrap" | "writingMode" | "x" | "y" | "zIndex" | "zoom" | "all" | "animation" | "animationRange" | "background" | "backgroundPosition" | "borderBlock" | "borderBlockColor" | "borderBlockEnd" | "borderBlockStart" | "borderBlockStyle" | "borderBlockWidth" | "borderBottom" | "borderColor" | "borderImage" | "borderInline" | "borderInlineColor" | "borderInlineEnd" | "borderInlineStart" | "borderInlineStyle" | "borderInlineWidth" | "borderLeft" | "borderRadius" | "borderRight" | "borderStyle" | "borderTop" | "borderWidth" | "caret" | "columnRule" | "columns" | "containIntrinsicSize" | "container" | "flex" | "flexFlow" | "grid" | "gridArea" | "gridColumn" | "gridRow" | "gridTemplate" | "inset" | "insetBlock" | "insetInline" | "lineClamp" | "listStyle" | "marginBlock" | "marginInline" | "mask" | "maskBorder" | "motion" | "offset" | "overflow" | "overscrollBehavior" | "paddingBlock" | "paddingInline" | "placeSelf" | "positionTry" | "scrollMargin" | "scrollMarginBlock" | "scrollMarginInline" | "scrollPadding" | "scrollPaddingBlock" | "scrollPaddingInline" | "scrollSnapMargin" | "scrollTimeline" | "textDecoration" | "textEmphasis" | "textWrap" | "viewTimeline" | "MozAnimationDelay" | "MozAnimationDirection" | "MozAnimationDuration" | "MozAnimationFillMode" | "MozAnimationIterationCount" | "MozAnimationName" | "MozAnimationPlayState" | "MozAnimationTimingFunction" | "MozAppearance" | "MozBackfaceVisibility" | "MozBinding" | "MozBorderBottomColors" | "MozBorderEndColor" | "MozBorderEndStyle" | "MozBorderEndWidth" | "MozBorderLeftColors" | "MozBorderRightColors" | "MozBorderStartColor" | "MozBorderStartStyle" | "MozBorderTopColors" | "MozBoxSizing" | "MozColumnRuleColor" | "MozColumnRuleStyle" | "MozColumnRuleWidth" | "MozColumnWidth" | "MozContextProperties" | "MozFontFeatureSettings" | "MozFontLanguageOverride" | "MozHyphens" | "MozMarginEnd" | "MozMarginStart" | "MozOrient" | "MozOsxFontSmoothing" | "MozOutlineRadiusBottomleft" | "MozOutlineRadiusBottomright" | "MozOutlineRadiusTopleft" | "MozOutlineRadiusTopright" | "MozPaddingEnd" | "MozPaddingStart" | "MozPerspective" | "MozPerspectiveOrigin" | "MozStackSizing" | "MozTabSize" | "MozTextBlink" | "MozTextSizeAdjust" | "MozTransform" | "MozTransformOrigin" | "MozTransformStyle" | "MozUserModify" | "MozUserSelect" | "MozWindowDragging" | "MozWindowShadow" | "msAccelerator" | "msBlockProgression" | "msContentZoomChaining" | "msContentZoomLimitMax" | "msContentZoomLimitMin" | "msContentZoomSnapPoints" | "msContentZoomSnapType" | "msContentZooming" | "msFilter" | "msFlexDirection" | "msFlexPositive" | "msFlowFrom" | "msFlowInto" | "msGridColumns" | "msGridRows" | "msHighContrastAdjust" | "msHyphenateLimitChars" | "msHyphenateLimitLines" | "msHyphenateLimitZone" | "msHyphens" | "msImeAlign" | "msLineBreak" | "msOrder" | "msOverflowStyle" | "msOverflowX" | "msOverflowY" | "msScrollChaining" | "msScrollLimitXMax" | "msScrollLimitXMin" | "msScrollLimitYMax" | "msScrollLimitYMin" | "msScrollRails" | "msScrollSnapPointsX" | "msScrollSnapPointsY" | "msScrollSnapType" | "msScrollTranslation" | "msScrollbar3dlightColor" | "msScrollbarArrowColor" | "msScrollbarBaseColor" | "msScrollbarDarkshadowColor" | "msScrollbarFaceColor" | "msScrollbarHighlightColor" | "msScrollbarShadowColor" | "msScrollbarTrackColor" | "msTextAutospace" | "msTextCombineHorizontal" | "msTextOverflow" | "msTouchAction" | "msTouchSelect" | "msTransform" | "msTransformOrigin" | "msTransitionDelay" | "msTransitionDuration" | "msTransitionProperty" | "msTransitionTimingFunction" | "msUserSelect" | "msWordBreak" | "msWrapFlow" | "msWrapMargin" | "msWrapThrough" | "msWritingMode" | "WebkitAlignContent" | "WebkitAlignItems" | "WebkitAlignSelf" | "WebkitAnimationDelay" | "WebkitAnimationDirection" | "WebkitAnimationDuration" | "WebkitAnimationFillMode" | "WebkitAnimationIterationCount" | "WebkitAnimationName" | "WebkitAnimationPlayState" | "WebkitAnimationTimingFunction" | "WebkitAppearance" | "WebkitBackdropFilter" | "WebkitBackfaceVisibility" | "WebkitBackgroundClip" | "WebkitBackgroundOrigin" | "WebkitBackgroundSize" | "WebkitBorderBeforeColor" | "WebkitBorderBeforeStyle" | "WebkitBorderBeforeWidth" | "WebkitBorderBottomLeftRadius" | "WebkitBorderBottomRightRadius" | "WebkitBorderImageSlice" | "WebkitBorderTopLeftRadius" | "WebkitBorderTopRightRadius" | "WebkitBoxDecorationBreak" | "WebkitBoxReflect" | "WebkitBoxShadow" | "WebkitBoxSizing" | "WebkitClipPath" | "WebkitColumnCount" | "WebkitColumnFill" | "WebkitColumnRuleColor" | "WebkitColumnRuleStyle" | "WebkitColumnRuleWidth" | "WebkitColumnSpan" | "WebkitColumnWidth" | "WebkitFilter" | "WebkitFlexBasis" | "WebkitFlexDirection" | "WebkitFlexGrow" | "WebkitFlexShrink" | "WebkitFlexWrap" | "WebkitFontFeatureSettings" | "WebkitFontKerning" | "WebkitFontSmoothing" | "WebkitFontVariantLigatures" | "WebkitHyphenateCharacter" | "WebkitHyphens" | "WebkitInitialLetter" | "WebkitJustifyContent" | "WebkitLineBreak" | "WebkitLineClamp" | "WebkitLogicalHeight" | "WebkitLogicalWidth" | "WebkitMarginEnd" | "WebkitMarginStart" | "WebkitMaskAttachment" | "WebkitMaskBoxImageOutset" | "WebkitMaskBoxImageRepeat" | "WebkitMaskBoxImageSlice" | "WebkitMaskBoxImageSource" | "WebkitMaskBoxImageWidth" | "WebkitMaskClip" | "WebkitMaskComposite" | "WebkitMaskImage" | "WebkitMaskOrigin" | "WebkitMaskPosition" | "WebkitMaskPositionX" | "WebkitMaskPositionY" | "WebkitMaskRepeat" | "WebkitMaskRepeatX" | "WebkitMaskRepeatY" | "WebkitMaskSize" | "WebkitMaxInlineSize" | "WebkitOrder" | "WebkitOverflowScrolling" | "WebkitPaddingEnd" | "WebkitPaddingStart" | "WebkitPerspective" | "WebkitPerspectiveOrigin" | "WebkitPrintColorAdjust" | "WebkitRubyPosition" | "WebkitScrollSnapType" | "WebkitShapeMargin" | "WebkitTapHighlightColor" | "WebkitTextCombine" | "WebkitTextDecorationColor" | "WebkitTextDecorationLine" | "WebkitTextDecorationSkip" | "WebkitTextDecorationStyle" | "WebkitTextEmphasisColor" | "WebkitTextEmphasisPosition" | "WebkitTextEmphasisStyle" | "WebkitTextFillColor" | "WebkitTextOrientation" | "WebkitTextSizeAdjust" | "WebkitTextStrokeColor" | "WebkitTextStrokeWidth" | "WebkitTextUnderlinePosition" | "WebkitTouchCallout" | "WebkitTransform" | "WebkitTransformOrigin" | "WebkitTransformStyle" | "WebkitTransitionDelay" | "WebkitTransitionDuration" | "WebkitTransitionProperty" | "WebkitTransitionTimingFunction" | "WebkitUserModify" | "WebkitUserSelect" | "WebkitWritingMode" | "MozAnimation" | "MozBorderImage" | "MozColumnRule" | "MozColumns" | "MozOutlineRadius" | "MozTransition" | "msContentZoomLimit" | "msContentZoomSnap" | "msFlex" | "msScrollLimit" | "msScrollSnapX" | "msScrollSnapY" | "msTransition" | "WebkitAnimation" | "WebkitBorderBefore" | "WebkitBorderImage" | "WebkitBorderRadius" | "WebkitColumnRule" | "WebkitColumns" | "WebkitFlex" | "WebkitFlexFlow" | "WebkitMask" | "WebkitMaskBoxImage" | "WebkitTextEmphasis" | "WebkitTextStroke" | "WebkitTransition" | "boxAlign" | "boxDirection" | "boxFlex" | "boxFlexGroup" | "boxLines" | "boxOrdinalGroup" | "boxOrient" | "boxPack" | "clip" | "fontStretch" | "gridColumnGap" | "gridGap" | "gridRowGap" | "imeMode" | "insetArea" | "offsetBlock" | "offsetBlockEnd" | "offsetBlockStart" | "offsetInline" | "offsetInlineEnd" | "offsetInlineStart" | "pageBreakAfter" | "pageBreakBefore" | "pageBreakInside" | "positionTryOptions" | "scrollSnapCoordinate" | "scrollSnapDestination" | "scrollSnapPointsX" | "scrollSnapPointsY" | "scrollSnapTypeX" | "scrollSnapTypeY" | "KhtmlBoxAlign" | "KhtmlBoxDirection" | "KhtmlBoxFlex" | "KhtmlBoxFlexGroup" | "KhtmlBoxLines" | "KhtmlBoxOrdinalGroup" | "KhtmlBoxOrient" | "KhtmlBoxPack" | "KhtmlLineBreak" | "KhtmlOpacity" | "KhtmlUserSelect" | "MozBackgroundClip" | "MozBackgroundOrigin" | "MozBackgroundSize" | "MozBorderRadius" | "MozBorderRadiusBottomleft" | "MozBorderRadiusBottomright" | "MozBorderRadiusTopleft" | "MozBorderRadiusTopright" | "MozBoxAlign" | "MozBoxDirection" | "MozBoxFlex" | "MozBoxOrdinalGroup" | "MozBoxOrient" | "MozBoxPack" | "MozBoxShadow" | "MozColumnCount" | "MozColumnFill" | "MozFloatEdge" | "MozForceBrokenImageIcon" | "MozOpacity" | "MozOutline" | "MozOutlineColor" | "MozOutlineStyle" | "MozOutlineWidth" | "MozTextAlignLast" | "MozTextDecorationColor" | "MozTextDecorationLine" | "MozTextDecorationStyle" | "MozTransitionDelay" | "MozTransitionDuration" | "MozTransitionProperty" | "MozTransitionTimingFunction" | "MozUserFocus" | "MozUserInput" | "msImeMode" | "OAnimation" | "OAnimationDelay" | "OAnimationDirection" | "OAnimationDuration" | "OAnimationFillMode" | "OAnimationIterationCount" | "OAnimationName" | "OAnimationPlayState" | "OAnimationTimingFunction" | "OBackgroundSize" | "OBorderImage" | "OObjectFit" | "OObjectPosition" | "OTabSize" | "OTextOverflow" | "OTransform" | "OTransformOrigin" | "OTransition" | "OTransitionDelay" | "OTransitionDuration" | "OTransitionProperty" | "OTransitionTimingFunction" | "WebkitBoxAlign" | "WebkitBoxDirection" | "WebkitBoxFlex" | "WebkitBoxFlexGroup" | "WebkitBoxLines" | "WebkitBoxOrdinalGroup" | "WebkitBoxOrient" | "WebkitBoxPack" | "colorInterpolation" | "colorRendering" | "glyphOrientationVertical" | "image" | "svgFill" | "fade" | "styledScrollbar" | "scrollbar" | "boldFontWeight" | "hide" | "shadow" | "radius" | "flow" | "gridAreas" | "gridColumns" | "gridRows" | "preset" | "align" | "justify" | "place" | "@keyframes" | "@properties" | "recipe" | "style" | "as" | "element" | "styles" | "breakpoints" | "block" | "inline" | "mods" | "isHidden" | "isDisabled" | "css" | "theme" | "tokens" | "ref"> & RefAttributes<unknown>> & SubElementComponents<Record<string, never>>;
981
981
  //#endregion
982
982
  export { AllBasePropsWithMods, Element, ElementsDefinition, SubElementDefinition, SubElementProps, TastyElementOptions, TastyElementProps, TastyProps, tasty };
983
983
  //# sourceMappingURL=tasty.d.ts.map
@@ -13,7 +13,7 @@ import { mergeStyles } from "./merge-styles.js";
13
13
  * Resolution order per level (top-level and each sub-element independently):
14
14
  * base_recipe_1 base_recipe_2 → component styles → post_recipe_1 post_recipe_2
15
15
  *
16
- * The `|` separator splits base recipes (before component styles)
16
+ * The `/` separator splits base recipes (before component styles)
17
17
  * from post recipes (after component styles). All merges use mergeStyles
18
18
  * semantics: primitives and state maps with '' key fully replace;
19
19
  * state maps without '' key extend the existing value.
@@ -24,10 +24,11 @@ const devMode = isDevEnv();
24
24
  /**
25
25
  * Parse a recipe string into base and post recipe name groups.
26
26
  *
27
- * Syntax: `'base1 base2 | post1 post2'`
27
+ * Syntax: `'base1 base2 / post1 post2'`
28
28
  * - Names are space-separated within each group
29
- * - `|` separates base (before component) from post (after component) groups
30
- * - `|` is optional; if absent, all names are base
29
+ * - `/` separates base (before component) from post (after component) groups
30
+ * - `/` is optional; if absent, all names are base
31
+ * - `none` as the sole base value means "no base recipes"
31
32
  *
32
33
  * Returns `{ base: null, post: null }` if the string is empty or invalid.
33
34
  */
@@ -39,15 +40,18 @@ function parseRecipeNames(value) {
39
40
  if (typeof value !== "string") return empty;
40
41
  const trimmed = value.trim();
41
42
  if (trimmed === "") return empty;
42
- const pipeIndex = trimmed.indexOf("|");
43
- if (pipeIndex === -1) return {
44
- base: splitNames(trimmed),
45
- post: null
46
- };
47
- const basePart = trimmed.slice(0, pipeIndex);
48
- const postPart = trimmed.slice(pipeIndex + 1);
43
+ const slashIndex = trimmed.indexOf("/");
44
+ if (slashIndex === -1) {
45
+ if (trimmed === "none") return empty;
46
+ return {
47
+ base: splitNames(trimmed),
48
+ post: null
49
+ };
50
+ }
51
+ const basePart = trimmed.slice(0, slashIndex);
52
+ const postPart = trimmed.slice(slashIndex + 1);
49
53
  return {
50
- base: splitNames(basePart),
54
+ base: basePart.trim() === "none" ? null : splitNames(basePart),
51
55
  post: splitNames(postPart)
52
56
  };
53
57
  }
@@ -1 +1 @@
1
- {"version":3,"file":"resolve-recipes.js","names":[],"sources":["../../src/utils/resolve-recipes.ts"],"sourcesContent":["/**\n * Recipe resolution utility.\n *\n * Resolves `recipe` style properties by looking up predefined recipe styles\n * from global configuration and merging them with the component's own styles.\n *\n * Resolution order per level (top-level and each sub-element independently):\n * base_recipe_1 base_recipe_2 → component styles → post_recipe_1 post_recipe_2\n *\n * The `|` separator splits base recipes (before component styles)\n * from post recipes (after component styles). All merges use mergeStyles\n * semantics: primitives and state maps with '' key fully replace;\n * state maps without '' key extend the existing value.\n *\n * Returns the same object reference if no recipes are present (zero overhead).\n */\n\nimport { getGlobalRecipes } from '../config';\nimport { isSelector } from '../pipeline';\nimport type { RecipeStyles, Styles } from '../styles/types';\n\nimport { isDevEnv } from './is-dev-env';\nimport { mergeStyles } from './merge-styles';\n\nconst devMode = isDevEnv();\n\ninterface ParsedRecipeGroups {\n base: string[] | null;\n post: string[] | null;\n}\n\n/**\n * Parse a recipe string into base and post recipe name groups.\n *\n * Syntax: `'base1 base2 | post1 post2'`\n * - Names are space-separated within each group\n * - `|` separates base (before component) from post (after component) groups\n * - `|` is optional; if absent, all names are base\n *\n * Returns `{ base: null, post: null }` if the string is empty or invalid.\n */\nfunction parseRecipeNames(value: unknown): ParsedRecipeGroups {\n const empty: ParsedRecipeGroups = { base: null, post: null };\n\n if (typeof value !== 'string') return empty;\n const trimmed = value.trim();\n if (trimmed === '') return empty;\n\n const pipeIndex = trimmed.indexOf('|');\n\n if (pipeIndex === -1) {\n const names = splitNames(trimmed);\n return { base: names, post: null };\n }\n\n const basePart = trimmed.slice(0, pipeIndex);\n const postPart = trimmed.slice(pipeIndex + 1);\n\n return {\n base: splitNames(basePart),\n post: splitNames(postPart),\n };\n}\n\nfunction splitNames(s: string): string[] | null {\n const names = s.split(/\\s+/).filter(Boolean);\n return names.length > 0 ? names : null;\n}\n\n/**\n * Collect merged styles for a list of recipe names.\n * Each recipe is flat-spread on top of the previous.\n */\nfunction collectRecipeStyles(\n names: string[],\n recipes: Record<string, RecipeStyles>,\n): Record<string, unknown> {\n let merged: Record<string, unknown> = {};\n\n for (const name of names) {\n const recipeStyles = recipes[name];\n\n if (!recipeStyles) {\n if (devMode) {\n console.warn(\n `[Tasty] Recipe \"${name}\" not found. ` +\n `Make sure it is defined in configure({ recipes: { ... } }).`,\n );\n }\n continue;\n }\n\n merged = { ...merged, ...(recipeStyles as Record<string, unknown>) };\n }\n\n return merged;\n}\n\n/**\n * Resolve recipe references in a flat styles object (no sub-elements).\n * Returns null if no `recipe` key is present.\n *\n * Resolution: base recipes → component styles → post recipes (all via mergeStyles)\n */\nfunction resolveRecipesForLevel(\n styles: Record<string, unknown>,\n recipes: Record<string, RecipeStyles>,\n): Record<string, unknown> | null {\n if (!('recipe' in styles)) return null;\n\n const { base, post } = parseRecipeNames(styles.recipe);\n\n // Separate selector keys (sub-elements) from flat style properties.\n // mergeStyles handles selectors with its own semantics (e.g. false = delete),\n // but at this level we only want recipe merging on flat properties.\n\n const { recipe: _recipe, ...allRest } = styles;\n const flatStyles: Record<string, unknown> = {};\n const selectorStyles: Record<string, unknown> = {};\n\n for (const key of Object.keys(allRest)) {\n if (isSelector(key)) {\n selectorStyles[key] = allRest[key];\n } else {\n flatStyles[key] = allRest[key];\n }\n }\n\n if (!base && !post) {\n return allRest;\n }\n\n // 1. Merge base recipes, then component styles on top (via mergeStyles)\n let result: Record<string, unknown>;\n\n if (base) {\n const baseStyles = collectRecipeStyles(base, recipes);\n result = mergeStyles(baseStyles as Styles, flatStyles as Styles) as Record<\n string,\n unknown\n >;\n } else {\n result = { ...flatStyles };\n }\n\n // 2. Apply post recipes via mergeStyles (state map extend semantics)\n if (post) {\n const postStyles = collectRecipeStyles(post, recipes);\n result = mergeStyles(result as Styles, postStyles as Styles) as Record<\n string,\n unknown\n >;\n }\n\n // Re-attach selector keys unchanged\n for (const key of Object.keys(selectorStyles)) {\n result[key] = selectorStyles[key];\n }\n\n return result;\n}\n\n/**\n * Resolve all `recipe` style properties in a styles object.\n *\n * Handles both top-level and sub-element recipe references.\n * Returns the same object reference if no recipes are present anywhere\n * (zero overhead for the common case).\n *\n * @param styles - The styles object potentially containing `recipe` keys\n * @returns Resolved styles with recipe values merged in, or the original object if unchanged\n */\nexport function resolveRecipes(styles: Styles): Styles {\n const recipes = getGlobalRecipes();\n\n // Fast path: no recipes configured globally\n if (!recipes) return styles;\n\n let changed = false;\n\n // Resolve top-level recipe\n const topResolved = resolveRecipesForLevel(\n styles as Record<string, unknown>,\n recipes,\n );\n\n let result: Record<string, unknown>;\n\n if (topResolved) {\n changed = true;\n result = topResolved;\n } else {\n // Keep reference; a shallow copy is deferred until a sub-element actually changes\n result = styles as Record<string, unknown>;\n }\n\n // Resolve sub-element recipes\n const keys = Object.keys(result);\n\n for (const key of keys) {\n if (!isSelector(key)) continue;\n\n const subStyles = result[key];\n\n if (\n !subStyles ||\n typeof subStyles !== 'object' ||\n Array.isArray(subStyles)\n ) {\n continue;\n }\n\n const subRecord = subStyles as Record<string, unknown>;\n\n if (!('recipe' in subRecord)) continue;\n\n const subResolved = resolveRecipesForLevel(subRecord, recipes);\n\n if (subResolved) {\n if (!changed) {\n // First change in sub-elements -- need to shallow-copy the top level\n changed = true;\n result = { ...(styles as Record<string, unknown>) };\n }\n result[key] = subResolved;\n }\n }\n\n return changed ? (result as Styles) : styles;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAwBA,MAAM,UAAU,UAAU;;;;;;;;;;;AAiB1B,SAAS,iBAAiB,OAAoC;CAC5D,MAAM,QAA4B;EAAE,MAAM;EAAM,MAAM;EAAM;AAE5D,KAAI,OAAO,UAAU,SAAU,QAAO;CACtC,MAAM,UAAU,MAAM,MAAM;AAC5B,KAAI,YAAY,GAAI,QAAO;CAE3B,MAAM,YAAY,QAAQ,QAAQ,IAAI;AAEtC,KAAI,cAAc,GAEhB,QAAO;EAAE,MADK,WAAW,QAAQ;EACX,MAAM;EAAM;CAGpC,MAAM,WAAW,QAAQ,MAAM,GAAG,UAAU;CAC5C,MAAM,WAAW,QAAQ,MAAM,YAAY,EAAE;AAE7C,QAAO;EACL,MAAM,WAAW,SAAS;EAC1B,MAAM,WAAW,SAAS;EAC3B;;AAGH,SAAS,WAAW,GAA4B;CAC9C,MAAM,QAAQ,EAAE,MAAM,MAAM,CAAC,OAAO,QAAQ;AAC5C,QAAO,MAAM,SAAS,IAAI,QAAQ;;;;;;AAOpC,SAAS,oBACP,OACA,SACyB;CACzB,IAAI,SAAkC,EAAE;AAExC,MAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,eAAe,QAAQ;AAE7B,MAAI,CAAC,cAAc;AACjB,OAAI,QACF,SAAQ,KACN,mBAAmB,KAAK,0EAEzB;AAEH;;AAGF,WAAS;GAAE,GAAG;GAAQ,GAAI;GAA0C;;AAGtE,QAAO;;;;;;;;AAST,SAAS,uBACP,QACA,SACgC;AAChC,KAAI,EAAE,YAAY,QAAS,QAAO;CAElC,MAAM,EAAE,MAAM,SAAS,iBAAiB,OAAO,OAAO;CAMtD,MAAM,EAAE,QAAQ,SAAS,GAAG,YAAY;CACxC,MAAM,aAAsC,EAAE;CAC9C,MAAM,iBAA0C,EAAE;AAElD,MAAK,MAAM,OAAO,OAAO,KAAK,QAAQ,CACpC,KAAI,WAAW,IAAI,CACjB,gBAAe,OAAO,QAAQ;KAE9B,YAAW,OAAO,QAAQ;AAI9B,KAAI,CAAC,QAAQ,CAAC,KACZ,QAAO;CAIT,IAAI;AAEJ,KAAI,KAEF,UAAS,YADU,oBAAoB,MAAM,QAAQ,EACV,WAAqB;KAKhE,UAAS,EAAE,GAAG,YAAY;AAI5B,KAAI,MAAM;EACR,MAAM,aAAa,oBAAoB,MAAM,QAAQ;AACrD,WAAS,YAAY,QAAkB,WAAqB;;AAO9D,MAAK,MAAM,OAAO,OAAO,KAAK,eAAe,CAC3C,QAAO,OAAO,eAAe;AAG/B,QAAO;;;;;;;;;;;;AAaT,SAAgB,eAAe,QAAwB;CACrD,MAAM,UAAU,kBAAkB;AAGlC,KAAI,CAAC,QAAS,QAAO;CAErB,IAAI,UAAU;CAGd,MAAM,cAAc,uBAClB,QACA,QACD;CAED,IAAI;AAEJ,KAAI,aAAa;AACf,YAAU;AACV,WAAS;OAGT,UAAS;CAIX,MAAM,OAAO,OAAO,KAAK,OAAO;AAEhC,MAAK,MAAM,OAAO,MAAM;AACtB,MAAI,CAAC,WAAW,IAAI,CAAE;EAEtB,MAAM,YAAY,OAAO;AAEzB,MACE,CAAC,aACD,OAAO,cAAc,YACrB,MAAM,QAAQ,UAAU,CAExB;EAGF,MAAM,YAAY;AAElB,MAAI,EAAE,YAAY,WAAY;EAE9B,MAAM,cAAc,uBAAuB,WAAW,QAAQ;AAE9D,MAAI,aAAa;AACf,OAAI,CAAC,SAAS;AAEZ,cAAU;AACV,aAAS,EAAE,GAAI,QAAoC;;AAErD,UAAO,OAAO;;;AAIlB,QAAO,UAAW,SAAoB"}
1
+ {"version":3,"file":"resolve-recipes.js","names":[],"sources":["../../src/utils/resolve-recipes.ts"],"sourcesContent":["/**\n * Recipe resolution utility.\n *\n * Resolves `recipe` style properties by looking up predefined recipe styles\n * from global configuration and merging them with the component's own styles.\n *\n * Resolution order per level (top-level and each sub-element independently):\n * base_recipe_1 base_recipe_2 → component styles → post_recipe_1 post_recipe_2\n *\n * The `/` separator splits base recipes (before component styles)\n * from post recipes (after component styles). All merges use mergeStyles\n * semantics: primitives and state maps with '' key fully replace;\n * state maps without '' key extend the existing value.\n *\n * Returns the same object reference if no recipes are present (zero overhead).\n */\n\nimport { getGlobalRecipes } from '../config';\nimport { isSelector } from '../pipeline';\nimport type { RecipeStyles, Styles } from '../styles/types';\n\nimport { isDevEnv } from './is-dev-env';\nimport { mergeStyles } from './merge-styles';\n\nconst devMode = isDevEnv();\n\ninterface ParsedRecipeGroups {\n base: string[] | null;\n post: string[] | null;\n}\n\n/**\n * Parse a recipe string into base and post recipe name groups.\n *\n * Syntax: `'base1 base2 / post1 post2'`\n * - Names are space-separated within each group\n * - `/` separates base (before component) from post (after component) groups\n * - `/` is optional; if absent, all names are base\n * - `none` as the sole base value means \"no base recipes\"\n *\n * Returns `{ base: null, post: null }` if the string is empty or invalid.\n */\nfunction parseRecipeNames(value: unknown): ParsedRecipeGroups {\n const empty: ParsedRecipeGroups = { base: null, post: null };\n\n if (typeof value !== 'string') return empty;\n const trimmed = value.trim();\n if (trimmed === '') return empty;\n\n const slashIndex = trimmed.indexOf('/');\n\n if (slashIndex === -1) {\n if (trimmed === 'none') return empty;\n const names = splitNames(trimmed);\n return { base: names, post: null };\n }\n\n const basePart = trimmed.slice(0, slashIndex);\n const postPart = trimmed.slice(slashIndex + 1);\n\n return {\n base: basePart.trim() === 'none' ? null : splitNames(basePart),\n post: splitNames(postPart),\n };\n}\n\nfunction splitNames(s: string): string[] | null {\n const names = s.split(/\\s+/).filter(Boolean);\n return names.length > 0 ? names : null;\n}\n\n/**\n * Collect merged styles for a list of recipe names.\n * Each recipe is flat-spread on top of the previous.\n */\nfunction collectRecipeStyles(\n names: string[],\n recipes: Record<string, RecipeStyles>,\n): Record<string, unknown> {\n let merged: Record<string, unknown> = {};\n\n for (const name of names) {\n const recipeStyles = recipes[name];\n\n if (!recipeStyles) {\n if (devMode) {\n console.warn(\n `[Tasty] Recipe \"${name}\" not found. ` +\n `Make sure it is defined in configure({ recipes: { ... } }).`,\n );\n }\n continue;\n }\n\n merged = { ...merged, ...(recipeStyles as Record<string, unknown>) };\n }\n\n return merged;\n}\n\n/**\n * Resolve recipe references in a flat styles object (no sub-elements).\n * Returns null if no `recipe` key is present.\n *\n * Resolution: base recipes → component styles → post recipes (all via mergeStyles)\n */\nfunction resolveRecipesForLevel(\n styles: Record<string, unknown>,\n recipes: Record<string, RecipeStyles>,\n): Record<string, unknown> | null {\n if (!('recipe' in styles)) return null;\n\n const { base, post } = parseRecipeNames(styles.recipe);\n\n // Separate selector keys (sub-elements) from flat style properties.\n // mergeStyles handles selectors with its own semantics (e.g. false = delete),\n // but at this level we only want recipe merging on flat properties.\n\n const { recipe: _recipe, ...allRest } = styles;\n const flatStyles: Record<string, unknown> = {};\n const selectorStyles: Record<string, unknown> = {};\n\n for (const key of Object.keys(allRest)) {\n if (isSelector(key)) {\n selectorStyles[key] = allRest[key];\n } else {\n flatStyles[key] = allRest[key];\n }\n }\n\n if (!base && !post) {\n return allRest;\n }\n\n // 1. Merge base recipes, then component styles on top (via mergeStyles)\n let result: Record<string, unknown>;\n\n if (base) {\n const baseStyles = collectRecipeStyles(base, recipes);\n result = mergeStyles(baseStyles as Styles, flatStyles as Styles) as Record<\n string,\n unknown\n >;\n } else {\n result = { ...flatStyles };\n }\n\n // 2. Apply post recipes via mergeStyles (state map extend semantics)\n if (post) {\n const postStyles = collectRecipeStyles(post, recipes);\n result = mergeStyles(result as Styles, postStyles as Styles) as Record<\n string,\n unknown\n >;\n }\n\n // Re-attach selector keys unchanged\n for (const key of Object.keys(selectorStyles)) {\n result[key] = selectorStyles[key];\n }\n\n return result;\n}\n\n/**\n * Resolve all `recipe` style properties in a styles object.\n *\n * Handles both top-level and sub-element recipe references.\n * Returns the same object reference if no recipes are present anywhere\n * (zero overhead for the common case).\n *\n * @param styles - The styles object potentially containing `recipe` keys\n * @returns Resolved styles with recipe values merged in, or the original object if unchanged\n */\nexport function resolveRecipes(styles: Styles): Styles {\n const recipes = getGlobalRecipes();\n\n // Fast path: no recipes configured globally\n if (!recipes) return styles;\n\n let changed = false;\n\n // Resolve top-level recipe\n const topResolved = resolveRecipesForLevel(\n styles as Record<string, unknown>,\n recipes,\n );\n\n let result: Record<string, unknown>;\n\n if (topResolved) {\n changed = true;\n result = topResolved;\n } else {\n // Keep reference; a shallow copy is deferred until a sub-element actually changes\n result = styles as Record<string, unknown>;\n }\n\n // Resolve sub-element recipes\n const keys = Object.keys(result);\n\n for (const key of keys) {\n if (!isSelector(key)) continue;\n\n const subStyles = result[key];\n\n if (\n !subStyles ||\n typeof subStyles !== 'object' ||\n Array.isArray(subStyles)\n ) {\n continue;\n }\n\n const subRecord = subStyles as Record<string, unknown>;\n\n if (!('recipe' in subRecord)) continue;\n\n const subResolved = resolveRecipesForLevel(subRecord, recipes);\n\n if (subResolved) {\n if (!changed) {\n // First change in sub-elements -- need to shallow-copy the top level\n changed = true;\n result = { ...(styles as Record<string, unknown>) };\n }\n result[key] = subResolved;\n }\n }\n\n return changed ? (result as Styles) : styles;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAwBA,MAAM,UAAU,UAAU;;;;;;;;;;;;AAkB1B,SAAS,iBAAiB,OAAoC;CAC5D,MAAM,QAA4B;EAAE,MAAM;EAAM,MAAM;EAAM;AAE5D,KAAI,OAAO,UAAU,SAAU,QAAO;CACtC,MAAM,UAAU,MAAM,MAAM;AAC5B,KAAI,YAAY,GAAI,QAAO;CAE3B,MAAM,aAAa,QAAQ,QAAQ,IAAI;AAEvC,KAAI,eAAe,IAAI;AACrB,MAAI,YAAY,OAAQ,QAAO;AAE/B,SAAO;GAAE,MADK,WAAW,QAAQ;GACX,MAAM;GAAM;;CAGpC,MAAM,WAAW,QAAQ,MAAM,GAAG,WAAW;CAC7C,MAAM,WAAW,QAAQ,MAAM,aAAa,EAAE;AAE9C,QAAO;EACL,MAAM,SAAS,MAAM,KAAK,SAAS,OAAO,WAAW,SAAS;EAC9D,MAAM,WAAW,SAAS;EAC3B;;AAGH,SAAS,WAAW,GAA4B;CAC9C,MAAM,QAAQ,EAAE,MAAM,MAAM,CAAC,OAAO,QAAQ;AAC5C,QAAO,MAAM,SAAS,IAAI,QAAQ;;;;;;AAOpC,SAAS,oBACP,OACA,SACyB;CACzB,IAAI,SAAkC,EAAE;AAExC,MAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,eAAe,QAAQ;AAE7B,MAAI,CAAC,cAAc;AACjB,OAAI,QACF,SAAQ,KACN,mBAAmB,KAAK,0EAEzB;AAEH;;AAGF,WAAS;GAAE,GAAG;GAAQ,GAAI;GAA0C;;AAGtE,QAAO;;;;;;;;AAST,SAAS,uBACP,QACA,SACgC;AAChC,KAAI,EAAE,YAAY,QAAS,QAAO;CAElC,MAAM,EAAE,MAAM,SAAS,iBAAiB,OAAO,OAAO;CAMtD,MAAM,EAAE,QAAQ,SAAS,GAAG,YAAY;CACxC,MAAM,aAAsC,EAAE;CAC9C,MAAM,iBAA0C,EAAE;AAElD,MAAK,MAAM,OAAO,OAAO,KAAK,QAAQ,CACpC,KAAI,WAAW,IAAI,CACjB,gBAAe,OAAO,QAAQ;KAE9B,YAAW,OAAO,QAAQ;AAI9B,KAAI,CAAC,QAAQ,CAAC,KACZ,QAAO;CAIT,IAAI;AAEJ,KAAI,KAEF,UAAS,YADU,oBAAoB,MAAM,QAAQ,EACV,WAAqB;KAKhE,UAAS,EAAE,GAAG,YAAY;AAI5B,KAAI,MAAM;EACR,MAAM,aAAa,oBAAoB,MAAM,QAAQ;AACrD,WAAS,YAAY,QAAkB,WAAqB;;AAO9D,MAAK,MAAM,OAAO,OAAO,KAAK,eAAe,CAC3C,QAAO,OAAO,eAAe;AAG/B,QAAO;;;;;;;;;;;;AAaT,SAAgB,eAAe,QAAwB;CACrD,MAAM,UAAU,kBAAkB;AAGlC,KAAI,CAAC,QAAS,QAAO;CAErB,IAAI,UAAU;CAGd,MAAM,cAAc,uBAClB,QACA,QACD;CAED,IAAI;AAEJ,KAAI,aAAa;AACf,YAAU;AACV,WAAS;OAGT,UAAS;CAIX,MAAM,OAAO,OAAO,KAAK,OAAO;AAEhC,MAAK,MAAM,OAAO,MAAM;AACtB,MAAI,CAAC,WAAW,IAAI,CAAE;EAEtB,MAAM,YAAY,OAAO;AAEzB,MACE,CAAC,aACD,OAAO,cAAc,YACrB,MAAM,QAAQ,UAAU,CAExB;EAGF,MAAM,YAAY;AAElB,MAAI,EAAE,YAAY,WAAY;EAE9B,MAAM,cAAc,uBAAuB,WAAW,QAAQ;AAE9D,MAAI,aAAa;AACf,OAAI,CAAC,SAAS;AAEZ,cAAU;AACV,aAAS,EAAE,GAAI,QAAoC;;AAErD,UAAO,OAAO;;;AAIlB,QAAO,UAAW,SAAoB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tenphi/tasty",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "A design-system-integrated styling system and DSL for concise, state-aware UI styling",
5
5
  "type": "module",
6
6
  "exports": {