0
# Runtime Sprinkles
1
2
Lightweight runtime version of sprinkles creation for dynamic styling without build-time constraints. Enables the use of sprinkles in environments where vanilla-extract's file scope is not available.
3
4
## Capabilities
5
6
### createSprinkles (Runtime)
7
8
Runtime-only version of createSprinkles that works without vanilla-extract's file scope requirements. Perfect for testing environments, server-side rendering, or dynamic style generation.
9
10
```typescript { .api }
11
/**
12
* Creates a sprinkles function for runtime usage without file scope constraints
13
* @param args - Variable number of property configuration objects from defineProperties
14
* @returns SprinklesFn for runtime composition of CSS class names
15
*/
16
function createSprinkles<Args extends ReadonlyArray<SprinklesProperties>>(
17
...args: Args
18
): SprinklesFn<Args>;
19
20
/**
21
* Same SprinklesFn type as build-time version but without CSS composition
22
*/
23
type SprinklesFn<Args extends ReadonlyArray<SprinklesProperties>> = ((
24
props: SprinkleProps<Args>
25
) => string) & { properties: Set<keyof SprinkleProps<Args>> };
26
```
27
28
## Key Differences from Build-time Version
29
30
1. **No CSS Generation**: Runtime sprinkles only look up pre-existing class names; they don't generate CSS
31
2. **No File Scope Requirement**: Can be used outside of vanilla-extract's build-time file scope
32
3. **Performance Optimized**: Lightweight runtime with minimal overhead (<0.5KB gzipped)
33
4. **Class Name Composition**: Returns space-separated class name strings without CSS composition
34
35
## Usage Examples
36
37
**Basic runtime setup:**
38
39
```typescript
40
// This import works at runtime without build constraints
41
import { createSprinkles } from "@vanilla-extract/sprinkles/createRuntimeSprinkles";
42
43
// Use the same property definitions as build-time
44
const responsiveProperties = defineProperties({
45
conditions: {
46
mobile: {},
47
tablet: { '@media': 'screen and (min-width: 768px)' },
48
desktop: { '@media': 'screen and (min-width: 1024px)' }
49
},
50
defaultCondition: 'mobile',
51
properties: {
52
display: ['none', 'flex', 'block'],
53
flexDirection: ['row', 'column'],
54
padding: {
55
small: '8px',
56
medium: '16px',
57
large: '24px'
58
}
59
}
60
});
61
62
// Create runtime sprinkles function
63
const runtimeSprinkles = createSprinkles(responsiveProperties);
64
```
65
66
**Dynamic runtime styling:**
67
68
```typescript
69
import { createSprinkles } from "@vanilla-extract/sprinkles/createRuntimeSprinkles";
70
71
// Runtime sprinkles for dynamic styling
72
function createDynamicComponent(props: any) {
73
const className = runtimeSprinkles({
74
display: 'flex',
75
flexDirection: props.isColumn ? 'column' : 'row',
76
padding: props.size === 'large' ? 'large' : 'medium'
77
});
78
79
return `<div class="${className}">${props.children}</div>`;
80
}
81
82
// Usage with dynamic values
83
const component1 = createDynamicComponent({
84
isColumn: true,
85
size: 'large',
86
children: 'Content'
87
});
88
89
const component2 = createDynamicComponent({
90
isColumn: false,
91
size: 'medium',
92
children: 'Other content'
93
});
94
```
95
96
**Testing environments:**
97
98
```typescript
99
// jest.config.js or test setup
100
import { createSprinkles } from "@vanilla-extract/sprinkles/createRuntimeSprinkles";
101
102
// Mock sprinkles for testing without build-time constraints
103
const mockSprinkles = createSprinkles(/* property definitions */);
104
105
// Test component styling
106
describe('Component styles', () => {
107
it('should generate correct class names', () => {
108
const className = mockSprinkles({
109
display: 'flex',
110
padding: 'medium'
111
});
112
113
expect(className).toContain('display_flex');
114
expect(className).toContain('paddingTop_medium');
115
});
116
117
it('should handle conditional values', () => {
118
const className = mockSprinkles({
119
flexDirection: {
120
mobile: 'column',
121
desktop: 'row'
122
}
123
});
124
125
expect(className).toContain('flexDirection_column_mobile');
126
expect(className).toContain('flexDirection_row_desktop');
127
});
128
});
129
```
130
131
**Server-side rendering:**
132
133
```typescript
134
import { createSprinkles } from "@vanilla-extract/sprinkles/createRuntimeSprinkles";
135
136
// Server-side component with runtime sprinkles
137
function ServerComponent({ layout, spacing, theme }) {
138
const className = runtimeSprinkles({
139
display: 'flex',
140
flexDirection: layout === 'vertical' ? 'column' : 'row',
141
padding: spacing,
142
background: theme === 'dark' ? 'elevated' : 'surface'
143
});
144
145
return {
146
html: `<div class="${className}">Server-rendered content</div>`,
147
className: className
148
};
149
}
150
151
// Usage in server context
152
const rendered = ServerComponent({
153
layout: 'vertical',
154
spacing: 'large',
155
theme: 'dark'
156
});
157
```
158
159
**Conditional runtime composition:**
160
161
```typescript
162
import { createSprinkles } from "@vanilla-extract/sprinkles/createRuntimeSprinkles";
163
164
// Runtime composition with conditional logic
165
function createResponsiveLayout(config: {
166
isMobile: boolean;
167
isTablet: boolean;
168
isDesktop: boolean;
169
alignment: 'left' | 'center' | 'right';
170
}) {
171
let flexDirection: 'row' | 'column' = 'row';
172
let justifyContent: string = 'flex-start';
173
174
if (config.isMobile) {
175
flexDirection = 'column';
176
}
177
178
switch (config.alignment) {
179
case 'center':
180
justifyContent = 'center';
181
break;
182
case 'right':
183
justifyContent = 'flex-end';
184
break;
185
}
186
187
return runtimeSprinkles({
188
display: 'flex',
189
flexDirection,
190
justifyContent
191
});
192
}
193
194
// Usage
195
const mobileLayout = createResponsiveLayout({
196
isMobile: true,
197
isTablet: false,
198
isDesktop: false,
199
alignment: 'center'
200
});
201
```
202
203
**Properties introspection at runtime:**
204
205
```typescript
206
import { createSprinkles } from "@vanilla-extract/sprinkles/createRuntimeSprinkles";
207
208
const runtimeSprinkles = createSprinkles(/* properties */);
209
210
// Same introspection API as build-time version
211
console.log(runtimeSprinkles.properties.has('display')); // true
212
console.log(runtimeSprinkles.properties.has('fontSize')); // false
213
214
// Filter runtime props
215
function filterSprinkleProps(props: Record<string, any>) {
216
const sprinkleProps: Record<string, any> = {};
217
const otherProps: Record<string, any> = {};
218
219
for (const [key, value] of Object.entries(props)) {
220
if (runtimeSprinkles.properties.has(key)) {
221
sprinkleProps[key] = value;
222
} else {
223
otherProps[key] = value;
224
}
225
}
226
227
return { sprinkleProps, otherProps };
228
}
229
230
// Usage
231
const { sprinkleProps, otherProps } = filterSprinkleProps({
232
display: 'flex',
233
padding: 'medium',
234
onClick: () => {},
235
'data-testid': 'component'
236
});
237
238
const className = runtimeSprinkles(sprinkleProps);
239
```
240
241
**Error handling at runtime:**
242
243
```typescript
244
import { createSprinkles } from "@vanilla-extract/sprinkles/createRuntimeSprinkles";
245
246
const runtimeSprinkles = createSprinkles(/* properties */);
247
248
// Runtime error handling (development mode)
249
try {
250
const className = runtimeSprinkles({
251
display: 'invalid-value' // Will throw SprinklesError in development
252
});
253
} catch (error) {
254
if (error.name === 'SprinklesError') {
255
console.error('Sprinkles validation error:', error.message);
256
// Handle gracefully in production
257
}
258
}
259
260
// Safe runtime usage with fallbacks
261
function safeRuntimeSprinkles(props: any, fallback = '') {
262
try {
263
return runtimeSprinkles(props);
264
} catch (error) {
265
if (process.env.NODE_ENV === 'development') {
266
console.warn('Sprinkles error:', error.message);
267
}
268
return fallback;
269
}
270
}
271
```
272
273
## Performance Considerations
274
275
- **Lightweight**: Runtime sprinkles adds minimal JavaScript overhead (<0.5KB gzipped)
276
- **Pre-generated CSS**: All CSS classes are generated at build time; runtime only does lookups
277
- **Caching**: Class name combinations are efficiently cached using LRU cache
278
- **Tree Shaking**: Unused property configurations can be tree-shaken
279
- **No Style Injection**: No runtime style injection or DOM manipulation
280
281
### Build-time vs Runtime Performance
282
283
**Build-time sprinkles:**
284
- Zero runtime cost for style composition
285
- CSS classes are composed at build time
286
- Uses vanilla-extract's `composeStyles` for optimal CSS merging
287
- File scope validation ensures correct usage
288
289
**Runtime sprinkles:**
290
- Minimal JavaScript overhead for class name concatenation
291
- Uses simple string composition (`mockComposeStyles`)
292
- No CSS composition - returns space-separated class names
293
- Always validates class name lookup for safety
294
295
### Memory Usage
296
297
```typescript
298
// Runtime sprinkles memory profile
299
const runtimeSprinkles = createSprinkles(properties);
300
301
// Memory used:
302
// - Property definitions: ~1-5KB depending on configuration size
303
// - Function closure: ~200 bytes
304
// - Properties Set: ~100 bytes per property
305
// - Total: Usually <10KB for typical configurations
306
```
307
308
### Performance Benchmarks
309
310
Typical performance characteristics:
311
312
- **Class name lookup**: ~0.01ms per property
313
- **Conditional value resolution**: ~0.05ms per conditional property
314
- **Responsive array processing**: ~0.1ms per array
315
- **String concatenation**: ~0.001ms per class name
316
317
### Optimization Tips
318
319
```typescript
320
// ✓ Good: Reuse sprinkles functions
321
const sprinkles = createSprinkles(properties);
322
const class1 = sprinkles({ display: 'flex' });
323
const class2 = sprinkles({ display: 'block' });
324
325
// ✗ Avoid: Creating new sprinkles functions repeatedly
326
function BadComponent() {
327
const sprinkles = createSprinkles(properties); // Recreated each render
328
return sprinkles({ display: 'flex' });
329
}
330
331
// ✓ Good: Memoize complex conditional values
332
const memoizedStyle = useMemo(() => sprinkles({
333
display: complexCondition ? 'flex' : 'block',
334
padding: calculatePadding()
335
}), [complexCondition, calculatePadding]);
336
337
// ✓ Good: Batch class name generation
338
const classes = {
339
container: sprinkles({ display: 'flex', padding: 'medium' }),
340
item: sprinkles({ flex: '1', margin: 'small' }),
341
button: sprinkles({ padding: 'small', border: 'none' })
342
};
343
```
344
345
## Import Paths
346
347
```typescript
348
// Runtime sprinkles (no build-time CSS generation)
349
import { createSprinkles } from "@vanilla-extract/sprinkles/createRuntimeSprinkles";
350
351
// Build-time sprinkles (requires vanilla-extract file scope)
352
import { createSprinkles } from "@vanilla-extract/sprinkles";
353
```