@pyreon/rocketstyle 0.21.0 → 0.23.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 +117 -199
- package/package.json +9 -9
package/README.md
CHANGED
|
@@ -1,36 +1,22 @@
|
|
|
1
1
|
# @pyreon/rocketstyle
|
|
2
2
|
|
|
3
|
-
Multi-dimensional styling
|
|
3
|
+
Multi-dimensional component styling — states, sizes, variants, themes, light/dark, all cached.
|
|
4
4
|
|
|
5
|
-
Organize
|
|
5
|
+
`@pyreon/rocketstyle` is the styling layer Pyreon's UI system builds on. Organize styles by named DIMENSIONS — `state` (`primary` / `danger` / `success`), `size` (`sm` / `md` / `lg`), `variant`, plus any custom dimension you declare — instead of flat boolean props. Each dimension is a chainable method (`.states({...})`, `.sizes({...})`); per-dimension values are themed via `.theme()` callbacks that receive `(theme, mode, css)`, with light/dark mode threaded through and pseudo-states (`hover` / `focus` / `pressed` / `active` / `disabled`) auto-detected. Built on `@pyreon/attrs` + `@pyreon/styler`. Per-definition WeakMap caches make per-mount cost near zero for same-definition components — verified 73% reduction in `styler.resolve` calls on real-app benchmarks.
|
|
6
6
|
|
|
7
|
-
##
|
|
8
|
-
|
|
9
|
-
- **Dimension-based theming** — define style variations as named dimensions (states, sizes, variants)
|
|
10
|
-
- **Immutable chaining** — `.attrs()`, `.theme()`, `.states()`, `.sizes()`, `.styles()` and more
|
|
11
|
-
- **Boolean shorthand** — `Button({ primary: true, lg: true })` instead of `Button({ state: 'primary', size: 'lg' })`
|
|
12
|
-
- **Pseudo-state detection** — hover, focus, pressed tracked via signals and context
|
|
13
|
-
- **Light/dark mode** — theme callbacks receive a mode parameter
|
|
14
|
-
- **Provider/Consumer** — propagate parent state to children through context
|
|
15
|
-
- **Multi-tier WeakMap caching** — dimension maps, reserved keys, omit Sets, and theme results cached per component definition (shared across all instances). Per-mount allocations near zero for same-definition components
|
|
16
|
-
- **TypeScript inference** — dimension values and prop types inferred through the chain
|
|
17
|
-
|
|
18
|
-
## Installation
|
|
7
|
+
## Install
|
|
19
8
|
|
|
20
9
|
```bash
|
|
21
|
-
bun add @pyreon/rocketstyle
|
|
10
|
+
bun add @pyreon/rocketstyle @pyreon/core @pyreon/reactivity @pyreon/ui-core @pyreon/styler
|
|
22
11
|
```
|
|
23
12
|
|
|
24
|
-
## Quick
|
|
13
|
+
## Quick start
|
|
25
14
|
|
|
26
|
-
```
|
|
15
|
+
```tsx
|
|
27
16
|
import rocketstyle from '@pyreon/rocketstyle'
|
|
28
17
|
import { Element } from '@pyreon/elements'
|
|
29
18
|
|
|
30
|
-
const Button = rocketstyle()({
|
|
31
|
-
name: 'Button',
|
|
32
|
-
component: Element,
|
|
33
|
-
})
|
|
19
|
+
const Button = rocketstyle()({ name: 'Button', component: Element })
|
|
34
20
|
.attrs({ tag: 'button' })
|
|
35
21
|
.theme({
|
|
36
22
|
fontSize: 16,
|
|
@@ -39,238 +25,178 @@ const Button = rocketstyle()({
|
|
|
39
25
|
borderRadius: 4,
|
|
40
26
|
color: '#fff',
|
|
41
27
|
backgroundColor: '#0d6efd',
|
|
42
|
-
hover: {
|
|
43
|
-
backgroundColor: '#0b5ed7',
|
|
44
|
-
},
|
|
28
|
+
hover: { backgroundColor: '#0b5ed7' },
|
|
45
29
|
})
|
|
46
30
|
.states({
|
|
47
|
-
primary: {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
},
|
|
51
|
-
danger: {
|
|
52
|
-
backgroundColor: '#dc3545',
|
|
53
|
-
hover: { backgroundColor: '#bb2d3b' },
|
|
54
|
-
},
|
|
55
|
-
success: {
|
|
56
|
-
backgroundColor: '#198754',
|
|
57
|
-
hover: { backgroundColor: '#157347' },
|
|
58
|
-
},
|
|
31
|
+
primary: { backgroundColor: '#0d6efd', hover: { backgroundColor: '#0b5ed7' } },
|
|
32
|
+
danger: { backgroundColor: '#dc3545', hover: { backgroundColor: '#bb2d3b' } },
|
|
33
|
+
success: { backgroundColor: '#198754', hover: { backgroundColor: '#157347' } },
|
|
59
34
|
})
|
|
60
35
|
.sizes({
|
|
61
36
|
sm: { fontSize: 14, paddingX: 12, paddingY: 6 },
|
|
62
37
|
md: { fontSize: 16, paddingX: 16, paddingY: 8 },
|
|
63
38
|
lg: { fontSize: 18, paddingX: 20, paddingY: 10 },
|
|
64
39
|
})
|
|
65
|
-
```
|
|
66
40
|
|
|
67
|
-
|
|
68
|
-
// Named props
|
|
69
|
-
Button({ state: 'danger', size: 'lg', label: 'Delete' })
|
|
70
|
-
|
|
71
|
-
// Boolean shorthand (when useBooleans is enabled)
|
|
72
|
-
Button({ danger: true, lg: true, label: 'Delete' })
|
|
41
|
+
<Button state="danger" size="lg">Delete</Button>
|
|
73
42
|
```
|
|
74
43
|
|
|
75
|
-
## Core
|
|
44
|
+
## Core concepts
|
|
76
45
|
|
|
77
46
|
### Dimensions
|
|
78
47
|
|
|
79
|
-
A dimension is a named axis of style variation.
|
|
48
|
+
A dimension is a named axis of style variation. Defaults ship four:
|
|
80
49
|
|
|
81
|
-
| Dimension | Prop name | Multi | Example |
|
|
82
|
-
| ---------- | --------- |
|
|
83
|
-
| `states` | `state` | no
|
|
84
|
-
| `sizes` | `size` | no
|
|
85
|
-
| `variants` | `variant` | no
|
|
86
|
-
| `multiple` | — | yes
|
|
50
|
+
| Dimension | Prop name | Multi? | Example |
|
|
51
|
+
| ---------- | --------- | ------ | ------------------------------ |
|
|
52
|
+
| `states` | `state` | no | `primary`, `danger`, `success` |
|
|
53
|
+
| `sizes` | `size` | no | `sm`, `md`, `lg` |
|
|
54
|
+
| `variants` | `variant` | no | `outlined`, `filled` |
|
|
55
|
+
| `multiple` | — | yes | `rounded`, `shadow` |
|
|
87
56
|
|
|
88
|
-
Each dimension creates a chain method
|
|
57
|
+
Each declared dimension creates a chain method AND a corresponding prop on the component. Multi-dimensions accept multiple active values at once.
|
|
89
58
|
|
|
90
|
-
|
|
59
|
+
### Default: `useBooleans: false` (string prop values)
|
|
91
60
|
|
|
92
|
-
|
|
61
|
+
```tsx
|
|
62
|
+
<Button state="primary" size="lg">Save</Button>
|
|
63
|
+
```
|
|
93
64
|
|
|
94
|
-
|
|
65
|
+
Boolean shorthand (`<Button primary lg>Save</Button>`) is opt-in via `rocketstyle({ useBooleans: true })`. **Important**: before April 2026 the type default was `true` but the runtime was `false` — boolean props typechecked but were silently dropped at runtime. Fixed in `rocketstyle/init.ts`; new code should not rely on the historical behaviour.
|
|
95
66
|
|
|
96
|
-
|
|
67
|
+
### Theme + pseudo-states
|
|
97
68
|
|
|
98
69
|
```ts
|
|
99
70
|
.theme({
|
|
100
71
|
color: '#333',
|
|
101
72
|
fontSize: 16,
|
|
102
|
-
hover:
|
|
103
|
-
focus:
|
|
104
|
-
active:
|
|
73
|
+
hover: { color: '#000' },
|
|
74
|
+
focus: { outline: '2px solid blue' },
|
|
75
|
+
active: { transform: 'scale(0.98)' },
|
|
76
|
+
disabled: { opacity: 0.5 },
|
|
105
77
|
})
|
|
106
78
|
```
|
|
107
79
|
|
|
108
|
-
|
|
80
|
+
Pseudo-state keys nest directly. Bases (`@pyreon/elements`) generate `:hover` / `:focus-visible` / `:active` / `:disabled` CSS from the nested objects. `:hover` is unconditional — applied to EVERY component with hover theme; only `cursor: pointer` is gated on `onClick` / `href`.
|
|
109
81
|
|
|
110
|
-
|
|
82
|
+
### Styles callback
|
|
111
83
|
|
|
112
84
|
```ts
|
|
113
85
|
.styles((css) => css`
|
|
86
|
+
cursor: pointer;
|
|
87
|
+
border: none;
|
|
88
|
+
transition: all 0.2s;
|
|
89
|
+
|
|
114
90
|
${({ $rocketstyle, $rocketstate }) => {
|
|
115
|
-
// $rocketstyle — computed theme
|
|
91
|
+
// $rocketstyle — computed theme (base + active dimension values merged)
|
|
116
92
|
// $rocketstate — { hover, focus, pressed, active, disabled, pseudo }
|
|
117
|
-
return css
|
|
93
|
+
return /* css string */
|
|
118
94
|
}}
|
|
119
95
|
`)
|
|
120
96
|
```
|
|
121
97
|
|
|
98
|
+
`$rocketstyle` is identity-cached — same dimension-prop combo produces the same object identity, which lets the styler's `classCache` skip resolve work entirely on cache hits.
|
|
99
|
+
|
|
122
100
|
## API
|
|
123
101
|
|
|
124
|
-
### rocketstyle(options?)
|
|
102
|
+
### `rocketstyle(options?)({ name, component })`
|
|
125
103
|
|
|
126
104
|
Factory initializer. Returns a function that accepts component configuration.
|
|
127
105
|
|
|
128
106
|
```ts
|
|
129
107
|
const factory = rocketstyle({
|
|
130
|
-
dimensions: {
|
|
131
|
-
/* custom dimensions */
|
|
132
|
-
},
|
|
108
|
+
dimensions: { /* custom dimensions */ },
|
|
133
109
|
useBooleans: true,
|
|
134
110
|
})
|
|
135
111
|
|
|
136
|
-
const
|
|
137
|
-
name: 'ComponentName',
|
|
138
|
-
component: BaseComponent,
|
|
139
|
-
})
|
|
112
|
+
const Button = factory({ name: 'Button', component: Element })
|
|
140
113
|
```
|
|
141
114
|
|
|
142
|
-
###
|
|
143
|
-
|
|
144
|
-
Same API as `@pyreon/attrs`. Define default props with optional priority and filter.
|
|
115
|
+
### `.attrs(props | callback, options?)`
|
|
145
116
|
|
|
146
|
-
|
|
147
|
-
Button.attrs({ tag: 'button', role: 'button' })
|
|
148
|
-
Button.attrs((props) => ({ 'aria-label': props.label }))
|
|
149
|
-
```
|
|
117
|
+
Same as `@pyreon/attrs` — accumulate defaults, supports callback / priority / filter.
|
|
150
118
|
|
|
151
|
-
###
|
|
119
|
+
### `.theme(values | callback)`
|
|
152
120
|
|
|
153
|
-
Base theme
|
|
121
|
+
Base theme applied to every instance.
|
|
154
122
|
|
|
155
123
|
```ts
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
fontSize: 16,
|
|
159
|
-
color: '#fff',
|
|
160
|
-
hover: { opacity: 0.9 },
|
|
161
|
-
})
|
|
162
|
-
|
|
163
|
-
// Callback form — receives the theme context and mode
|
|
164
|
-
Button.theme((theme, mode, css) => ({
|
|
124
|
+
.theme({ fontSize: 16, color: '#fff', hover: { opacity: 0.9 } })
|
|
125
|
+
.theme((theme, mode, css) => ({
|
|
165
126
|
fontSize: 16,
|
|
166
127
|
color: mode === 'dark' ? '#fff' : '#333',
|
|
167
128
|
}))
|
|
168
129
|
```
|
|
169
130
|
|
|
170
|
-
###
|
|
131
|
+
### `.states()` / `.sizes()` / `.variants()` / `.multiple()`
|
|
171
132
|
|
|
172
|
-
Define
|
|
133
|
+
Define per-dimension values.
|
|
173
134
|
|
|
174
135
|
```ts
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
})
|
|
179
|
-
|
|
180
|
-
Button.sizes({
|
|
181
|
-
sm: { fontSize: 14, paddingX: 8 },
|
|
182
|
-
lg: { fontSize: 18, paddingX: 20 },
|
|
183
|
-
})
|
|
136
|
+
.states({ primary: { backgroundColor: '#0d6efd' }, danger: { backgroundColor: '#dc3545' } })
|
|
137
|
+
.sizes({ sm: { fontSize: 14, paddingX: 8 }, lg: { fontSize: 18, paddingX: 20 } })
|
|
138
|
+
.multiple({ rounded: { borderRadius: 999 }, shadow: { boxShadow: '0 2px 8px rgba(0,0,0,0.15)' } })
|
|
184
139
|
|
|
185
|
-
//
|
|
186
|
-
|
|
187
|
-
rounded: { borderRadius: 999 },
|
|
188
|
-
shadow: { boxShadow: '0 2px 8px rgba(0,0,0,0.15)' },
|
|
189
|
-
})
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
Dimension methods also accept callbacks:
|
|
193
|
-
|
|
194
|
-
```ts
|
|
195
|
-
Button.states((theme, mode, css) => ({
|
|
140
|
+
// Callback form — receives (theme, mode, css)
|
|
141
|
+
.states((theme) => ({
|
|
196
142
|
primary: { backgroundColor: theme.colors?.primary ?? '#0d6efd' },
|
|
197
143
|
}))
|
|
198
144
|
```
|
|
199
145
|
|
|
200
|
-
###
|
|
146
|
+
### `.styles(callback)`
|
|
201
147
|
|
|
202
|
-
|
|
148
|
+
CSS template using `@pyreon/styler`'s `css` tagged template.
|
|
203
149
|
|
|
204
|
-
|
|
205
|
-
Button.styles(
|
|
206
|
-
(css) => css`
|
|
207
|
-
cursor: pointer;
|
|
208
|
-
border: none;
|
|
209
|
-
transition: all 0.2s;
|
|
210
|
-
|
|
211
|
-
${({ $rocketstyle }) => makeItResponsive({ theme: $rocketstyle, styles, css })}
|
|
212
|
-
`,
|
|
213
|
-
)
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
### .config(options)
|
|
217
|
-
|
|
218
|
-
Reconfigure the component.
|
|
150
|
+
### `.config(options)`
|
|
219
151
|
|
|
220
152
|
```ts
|
|
221
153
|
Button.config({
|
|
222
|
-
name: 'PrimaryButton',
|
|
223
|
-
component: NewBase,
|
|
224
|
-
provider: true,
|
|
225
|
-
consumer: (ctx) =>
|
|
226
|
-
inversed: true,
|
|
227
|
-
DEBUG: true,
|
|
154
|
+
name: 'PrimaryButton',
|
|
155
|
+
component: NewBase, // swap base — resets prop chains
|
|
156
|
+
provider: true, // make this a context provider for children
|
|
157
|
+
consumer: (ctx) => …, // consume parent component context
|
|
158
|
+
inversed: true, // invert theme mode for subtree
|
|
159
|
+
DEBUG: true,
|
|
228
160
|
})
|
|
229
161
|
```
|
|
230
162
|
|
|
231
|
-
###
|
|
232
|
-
|
|
233
|
-
Same API as `@pyreon/attrs`:
|
|
234
|
-
|
|
235
|
-
```ts
|
|
236
|
-
Button.compose({ withTracking: trackingHoc })
|
|
237
|
-
Button.statics({ category: 'action' })
|
|
163
|
+
### `.compose(hocs)` / `.statics(metadata)`
|
|
238
164
|
|
|
239
|
-
|
|
240
|
-
```
|
|
165
|
+
Same API as `@pyreon/attrs`.
|
|
241
166
|
|
|
242
|
-
### isRocketComponent(value)
|
|
167
|
+
### `isRocketComponent(value)` / `resolveTheme(value)`
|
|
243
168
|
|
|
244
|
-
Runtime
|
|
169
|
+
Runtime guard and theme accessor for use inside styled-component interpolations:
|
|
245
170
|
|
|
246
171
|
```ts
|
|
247
|
-
import { isRocketComponent } from '@pyreon/rocketstyle'
|
|
172
|
+
import { isRocketComponent, resolveTheme } from '@pyreon/rocketstyle'
|
|
173
|
+
|
|
174
|
+
isRocketComponent(Button) // true
|
|
248
175
|
|
|
249
|
-
|
|
176
|
+
styled(Component)`
|
|
177
|
+
color: ${(props) => resolveTheme(props.$rocketstyle).color};
|
|
178
|
+
`
|
|
250
179
|
```
|
|
251
180
|
|
|
252
|
-
|
|
181
|
+
`resolveTheme` handles both function-accessor (reactive) and plain-object `$rocketstyle` shapes.
|
|
253
182
|
|
|
254
|
-
|
|
183
|
+
## Custom dimensions
|
|
255
184
|
|
|
256
185
|
```ts
|
|
257
186
|
const rocketButton = rocketstyle({
|
|
258
187
|
dimensions: {
|
|
259
|
-
intent: 'intent',
|
|
260
|
-
size:
|
|
261
|
-
appearance: {
|
|
262
|
-
propName: 'appearance',
|
|
263
|
-
multi: true, // allows multiple values
|
|
264
|
-
},
|
|
188
|
+
intent: 'intent', // prop: intent="primary"
|
|
189
|
+
size: 'size',
|
|
190
|
+
appearance: { propName: 'appearance', multi: true },
|
|
265
191
|
},
|
|
266
192
|
})
|
|
267
193
|
```
|
|
268
194
|
|
|
269
|
-
|
|
195
|
+
Creates `.intent()`, `.size()`, `.appearance()` chain methods.
|
|
270
196
|
|
|
271
|
-
|
|
197
|
+
### Transform dimensions
|
|
272
198
|
|
|
273
|
-
Mark
|
|
199
|
+
Mark `transform: true` to make a dimension receive the accumulated theme from all prior dimensions — ideal for modifiers like `outlined` that derive from the active state.
|
|
274
200
|
|
|
275
201
|
```ts
|
|
276
202
|
const rocketButton = rocketstyle({
|
|
@@ -282,51 +208,44 @@ const rocketButton = rocketstyle({
|
|
|
282
208
|
|
|
283
209
|
const Button = rocketButton({ name: 'Button', component: Element })
|
|
284
210
|
.theme({ backgroundColor: '#0d6efd', color: '#fff' })
|
|
285
|
-
.states({
|
|
286
|
-
danger: { backgroundColor: '#dc3545', color: '#fff' },
|
|
287
|
-
})
|
|
211
|
+
.states({ danger: { backgroundColor: '#dc3545', color: '#fff' } })
|
|
288
212
|
.modifiers({
|
|
289
213
|
outlined: (theme) => ({
|
|
290
|
-
color: theme.backgroundColor,
|
|
214
|
+
color: theme.backgroundColor, // receives merged theme from prior dimensions
|
|
291
215
|
backgroundColor: 'transparent',
|
|
292
216
|
}),
|
|
293
217
|
})
|
|
294
218
|
|
|
295
|
-
|
|
296
|
-
Button({ state: 'danger', modifier: 'outlined' })
|
|
219
|
+
<Button state="danger" modifier="outlined" /> // outlined sees danger's red, becomes red-on-transparent
|
|
297
220
|
```
|
|
298
221
|
|
|
299
|
-
## Provider / Consumer
|
|
300
|
-
|
|
301
|
-
Propagate parent component state to children through Pyreon's context system.
|
|
222
|
+
## Provider / Consumer — parent-child state propagation
|
|
302
223
|
|
|
303
224
|
```ts
|
|
304
225
|
// Parent provides its state
|
|
305
226
|
const ButtonGroup = Button.config({ provider: true })
|
|
306
227
|
|
|
307
228
|
// Child consumes parent state
|
|
308
|
-
const ButtonIcon = rocketstyle()({
|
|
309
|
-
name: 'ButtonIcon',
|
|
310
|
-
component: Element,
|
|
311
|
-
})
|
|
229
|
+
const ButtonIcon = rocketstyle()({ name: 'ButtonIcon', component: Element })
|
|
312
230
|
.config({
|
|
313
|
-
consumer: (ctx) =>
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
})),
|
|
231
|
+
consumer: (ctx) => ctx(({ pseudo }) => ({
|
|
232
|
+
state: pseudo.hover ? 'active' : 'default',
|
|
233
|
+
})),
|
|
317
234
|
})
|
|
318
235
|
.states({
|
|
319
236
|
default: { color: '#666' },
|
|
320
|
-
active:
|
|
237
|
+
active: { color: '#fff' },
|
|
321
238
|
})
|
|
322
239
|
|
|
323
|
-
|
|
324
|
-
|
|
240
|
+
<ButtonGroup state="primary">
|
|
241
|
+
<ButtonIcon />
|
|
242
|
+
Label
|
|
243
|
+
</ButtonGroup>
|
|
325
244
|
```
|
|
326
245
|
|
|
327
|
-
## Light /
|
|
246
|
+
## Light / dark mode
|
|
328
247
|
|
|
329
|
-
Theme callbacks receive
|
|
248
|
+
Theme + dimension callbacks receive `(theme, mode, css)`. `mode === 'light' | 'dark'`. Use `inversed: true` on `.config()` to flip the mode for a subtree.
|
|
330
249
|
|
|
331
250
|
```ts
|
|
332
251
|
Button.theme((theme, mode) => ({
|
|
@@ -335,32 +254,31 @@ Button.theme((theme, mode) => ({
|
|
|
335
254
|
}))
|
|
336
255
|
```
|
|
337
256
|
|
|
338
|
-
Use `inversed: true` in `.config()` to flip the mode for a component subtree.
|
|
339
|
-
|
|
340
257
|
## Performance
|
|
341
258
|
|
|
342
|
-
|
|
259
|
+
Per-definition WeakMap caches keep per-mount cost flat as instance count grows:
|
|
260
|
+
|
|
261
|
+
- **`_dimensionsCache`** — `getDimensionsMap` result keyed on dimension-themes identity
|
|
262
|
+
- **`_reservedKeysCache`** — `Object.keys(reservedPropNames)` keyed on keywords identity
|
|
263
|
+
- **`_omitSetCache`** — pre-built `Set<string>` for `omit()` (avoids per-mount Set allocation)
|
|
264
|
+
- **`LocalThemeManager`** — WeakMap tiers for baseTheme, dimensionThemes, and per-mode resolved themes
|
|
265
|
+
- **`_rsMemo`** — dimension-prop memo keyed by `mode|dimensionPropTuple|pseudoState`, LRU-bounded at 32 entries per theme. Hit returns identity-stable `{ rocketstyle, rocketstate }` so the downstream styler `classCache` skips resolve entirely. Real-app E2 benchmark: 200 Buttons × 5 runs, baseline dropped from 8.80ms to 4.80ms (-45%); per-Button `styler.resolve` from 22 to 6 (-73%).
|
|
343
266
|
|
|
344
|
-
|
|
345
|
-
- `_dimensionsCache` — `getDimensionsMap` result keyed on dimension-themes identity
|
|
346
|
-
- `_reservedKeysCache` — `Object.keys(reservedPropNames)` keyed on keywords identity
|
|
347
|
-
- `_omitSetCache` — pre-built `Set<string>` for `omit()` (avoids per-mount Set construction)
|
|
348
|
-
- `ALL_PSEUDO_KEYS` / `STATIC_OMIT_KEYS` — merged key arrays computed once
|
|
349
|
-
- **Theme cache** (`LocalThemeManager`): `WeakMap` tiers for baseTheme, dimensionThemes, and per-mode resolved themes
|
|
350
|
-
- **getTheme in-place merge**: dimension slices merged directly onto `finalTheme` instead of allocating a new target per `merge()` call
|
|
351
|
-
- **Frozen `EMPTY_PSEUDO`**: shared frozen `{}` for pseudo-state defaults instead of 6 allocations per call
|
|
352
|
-
- **Dev guard**: uses `__DEV__` (`import.meta.env.DEV`) — tree-shaken to zero bytes in production
|
|
267
|
+
Real apps MUST mount one shared `<PyreonUI>` provider for the memo to span instances — each provider mount creates a fresh `enrichedTheme` via `computed()`, which produces a different WeakMap key.
|
|
353
268
|
|
|
354
269
|
For a 150-component page with 8 dimensions each: ~1,350 Set allocations, ~300 array spreads, and ~150 map rebuilds eliminated vs naive implementation.
|
|
355
270
|
|
|
356
|
-
##
|
|
271
|
+
## Gotchas
|
|
272
|
+
|
|
273
|
+
- **`.config({ component: NewBase })` resets `attrs` / `priorityAttrs` / `filterAttrs` / `compose` chains** — they were tailored to the previous component's prop shape. `theme` / `styles` / dimension chains are preserved. Re-chain shared attrs explicitly if you swap the base.
|
|
274
|
+
- **`useBooleans: false` is the default** (since April 2026 alignment fix). String props are the idiomatic surface.
|
|
275
|
+
- **Cache keys are downstream of normalization.** Under `useBooleans: true`, the memo correctly keys by the resolved dimension (not the raw boolean prop) — otherwise every boolean variant would collide on the first cached entry.
|
|
276
|
+
- **Dimension props don't accept function accessors directly.** `state={() => signal()}` is wrong — write `state={signal()}` and let the compiler emit reactive `_rp()` wrapping. Caught by the Reactivity Lens.
|
|
277
|
+
- **`provider: true` + `consumer:` on the same component** is a legal but rare shape. Most apps separate the two for clarity.
|
|
278
|
+
|
|
279
|
+
## Documentation
|
|
357
280
|
|
|
358
|
-
|
|
359
|
-
| ------------------ | ------- |
|
|
360
|
-
| @pyreon/core | \* |
|
|
361
|
-
| @pyreon/reactivity | \* |
|
|
362
|
-
| @pyreon/ui-core | \* |
|
|
363
|
-
| @pyreon/styler | \* |
|
|
281
|
+
Full docs: [docs.pyreon.dev/docs/rocketstyle](https://docs.pyreon.dev/docs/rocketstyle) (or `docs/docs/rocketstyle.md` in this repo).
|
|
364
282
|
|
|
365
283
|
## License
|
|
366
284
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pyreon/rocketstyle",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"description": "Multi-dimensional style composition for Pyreon components",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -42,19 +42,19 @@
|
|
|
42
42
|
"typecheck": "tsc --noEmit"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@pyreon/test-utils": "^0.13.
|
|
46
|
-
"@pyreon/typescript": "^0.
|
|
47
|
-
"@pyreon/ui-core": "^0.
|
|
45
|
+
"@pyreon/test-utils": "^0.13.10",
|
|
46
|
+
"@pyreon/typescript": "^0.23.0",
|
|
47
|
+
"@pyreon/ui-core": "^0.23.0",
|
|
48
48
|
"@vitest/browser-playwright": "^4.1.4",
|
|
49
|
-
"@vitus-labs/tools-rolldown": "^2.
|
|
49
|
+
"@vitus-labs/tools-rolldown": "^2.4.0"
|
|
50
50
|
},
|
|
51
51
|
"engines": {
|
|
52
52
|
"node": ">= 22"
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@pyreon/core": "^0.
|
|
56
|
-
"@pyreon/reactivity": "^0.
|
|
57
|
-
"@pyreon/styler": "^0.
|
|
58
|
-
"@pyreon/ui-core": "^0.
|
|
55
|
+
"@pyreon/core": "^0.23.0",
|
|
56
|
+
"@pyreon/reactivity": "^0.23.0",
|
|
57
|
+
"@pyreon/styler": "^0.23.0",
|
|
58
|
+
"@pyreon/ui-core": "^0.23.0"
|
|
59
59
|
}
|
|
60
60
|
}
|