0
# Conditional Value Utilities
1
2
Helper functions for working with conditional values in responsive and theme-based styling scenarios. These utilities provide functions for mapping and normalizing conditional values.
3
4
## Capabilities
5
6
### createMapValueFn
7
8
Creates a function for mapping over conditional values. This is useful for converting high-level prop values to low-level sprinkles, e.g. converting left/right to flex-start/end.
9
10
```typescript { .api }
11
/**
12
* Creates a function for mapping over conditional values
13
* @param properties - Sprinkles properties configuration with conditions
14
* @returns Map value function that can transform conditional values
15
*/
16
function createMapValueFn<SprinklesProperties extends Conditions<string>>(
17
properties: SprinklesProperties
18
): <
19
OutputValue extends string | number | boolean | null | undefined,
20
Value extends ConditionalValue<SprinklesProperties, string | number | boolean>
21
>(
22
value: Value,
23
fn: (
24
inputValue: ExtractValue<Value>,
25
key: ExtractConditionNames<SprinklesProperties>
26
) => OutputValue
27
) => Value extends string | number | boolean
28
? OutputValue
29
: Partial<Record<ExtractConditionNames<SprinklesProperties>, OutputValue>>;
30
31
/**
32
* Extract condition names from sprinkles properties
33
*/
34
type ExtractConditionNames<SprinklesProperties extends Conditions<string>> =
35
SprinklesProperties['conditions']['conditionNames'][number];
36
37
/**
38
* Extract value type from conditional value
39
*/
40
type ExtractValue<Value> = Value extends ResponsiveArrayByMaxLength<number, infer T>
41
? NonNullable<T>
42
: Value extends Partial<Record<string, infer T>>
43
? NonNullable<T>
44
: Value;
45
```
46
47
### createNormalizeValueFn
48
49
Creates a function for normalizing conditional values into a consistent object structure. Any primitive values or responsive arrays will be converted to conditional objects.
50
51
```typescript { .api }
52
/**
53
* Creates a function for normalizing conditional values into consistent object structure
54
* @param properties - Sprinkles properties configuration with conditions
55
* @returns Normalize function that converts values to conditional objects
56
*/
57
function createNormalizeValueFn<SprinklesProperties extends Conditions<string>>(
58
properties: SprinklesProperties
59
): <Value extends string | number | boolean>(
60
value: ConditionalValue<SprinklesProperties, Value>
61
) => Partial<Record<ExtractConditionNames<SprinklesProperties>, Value>>;
62
63
/**
64
* Conditions interface for type constraints
65
*/
66
type Conditions<ConditionName extends string> = {
67
conditions: {
68
defaultCondition: ConditionName | false;
69
conditionNames: Array<ConditionName>;
70
responsiveArray?: Array<ConditionName>;
71
};
72
};
73
74
/**
75
* Required conditional object type for strict conditional values
76
*/
77
type RequiredConditionalObject<
78
RequiredConditionName extends string,
79
OptionalConditionNames extends string,
80
Value extends string | number | boolean
81
> = Record<RequiredConditionName, Value> &
82
Partial<Record<OptionalConditionNames, Value>>;
83
```
84
85
## Usage Examples
86
87
**Setting up utility functions:**
88
89
```typescript
90
import {
91
defineProperties,
92
createSprinkles,
93
createMapValueFn,
94
createNormalizeValueFn
95
} from "@vanilla-extract/sprinkles";
96
97
const responsiveProperties = defineProperties({
98
conditions: {
99
mobile: {},
100
tablet: { '@media': 'screen and (min-width: 768px)' },
101
desktop: { '@media': 'screen and (min-width: 1024px)' }
102
},
103
defaultCondition: 'mobile',
104
responsiveArray: ['mobile', 'tablet', 'desktop'],
105
properties: {
106
display: ['flex', 'block', 'none'],
107
alignItems: ['flex-start', 'center', 'flex-end', 'stretch']
108
}
109
});
110
111
export const sprinkles = createSprinkles(responsiveProperties);
112
export const mapResponsiveValue = createMapValueFn(responsiveProperties);
113
export const normalizeResponsiveValue = createNormalizeValueFn(responsiveProperties);
114
```
115
116
**Mapping values with createMapValueFn:**
117
118
```typescript
119
import { mapResponsiveValue } from './sprinkles.css';
120
121
// Define mapping for semantic alignment values
122
const alignToFlexAlign = {
123
left: 'flex-start',
124
center: 'center',
125
right: 'flex-end',
126
stretch: 'stretch'
127
} as const;
128
129
// Map primitive value
130
const mapped = mapResponsiveValue(
131
'left',
132
(value) => alignToFlexAlign[value]
133
);
134
// Result: 'flex-start'
135
136
// Map conditional object
137
const mappedConditional = mapResponsiveValue(
138
{
139
mobile: 'center',
140
desktop: 'left'
141
} as const,
142
(value) => alignToFlexAlign[value]
143
);
144
// Result: { mobile: 'center', desktop: 'flex-start' }
145
146
// Map responsive array
147
const mappedArray = mapResponsiveValue(
148
['center', null, 'left'] as const,
149
(value) => alignToFlexAlign[value]
150
);
151
// Result: { mobile: 'center', desktop: 'flex-start' }
152
153
// Access both value and condition in mapper
154
const mappedWithCondition = mapResponsiveValue(
155
{ mobile: 'small', desktop: 'large' },
156
(value, condition) => `${condition}-${value}`
157
);
158
// Result: { mobile: 'mobile-small', desktop: 'desktop-large' }
159
```
160
161
**Normalizing values with createNormalizeValueFn:**
162
163
```typescript
164
import { normalizeResponsiveValue } from './sprinkles.css';
165
166
// Normalize primitive value
167
const normalized = normalizeResponsiveValue('block');
168
// Result: { mobile: 'block' }
169
170
// Normalize responsive array
171
const normalizedArray = normalizeResponsiveValue(['none', null, 'block']);
172
// Result: { mobile: 'none', desktop: 'block' }
173
174
// Normalize conditional object (already normalized)
175
const normalizedObject = normalizeResponsiveValue({
176
mobile: 'none',
177
desktop: 'block'
178
});
179
// Result: { mobile: 'none', desktop: 'block' }
180
```
181
182
**Building higher-level component APIs:**
183
184
```typescript
185
import { mapResponsiveValue, sprinkles } from './sprinkles.css';
186
187
// Define semantic alignment API
188
type Alignment = 'left' | 'center' | 'right' | 'stretch';
189
type ResponsiveAlignment = Alignment | {
190
mobile?: Alignment;
191
tablet?: Alignment;
192
desktop?: Alignment;
193
} | [Alignment?, Alignment?, Alignment?];
194
195
function createAlignmentStyles(alignment: ResponsiveAlignment) {
196
const alignToFlex = {
197
left: 'flex-start',
198
center: 'center',
199
right: 'flex-end',
200
stretch: 'stretch'
201
} as const;
202
203
const flexAlignment = mapResponsiveValue(
204
alignment,
205
(value) => alignToFlex[value]
206
);
207
208
return sprinkles({
209
display: 'flex',
210
alignItems: flexAlignment
211
});
212
}
213
214
// Usage
215
const leftAligned = createAlignmentStyles('left');
216
const responsiveAligned = createAlignmentStyles({
217
mobile: 'center',
218
desktop: 'left'
219
});
220
const arrayAligned = createAlignmentStyles(['center', 'left', 'right']);
221
```
222
223
**Complex value transformations:**
224
225
```typescript
226
import { mapResponsiveValue } from './sprinkles.css';
227
228
// Transform spacing values
229
const spaceScale = {
230
xs: '4px',
231
sm: '8px',
232
md: '16px',
233
lg: '24px',
234
xl: '32px'
235
} as const;
236
237
function transformSpacing(space: any) {
238
return mapResponsiveValue(space, (value) => {
239
if (typeof value === 'number') {
240
return `${value}px`;
241
}
242
return spaceScale[value] || value;
243
});
244
}
245
246
// Usage
247
const spacing = transformSpacing({
248
mobile: 'sm',
249
tablet: 16,
250
desktop: 'lg'
251
});
252
// Result: { mobile: '8px', tablet: '16px', desktop: '24px' }
253
```
254
255
**Conditional theme mapping:**
256
257
```typescript
258
import { mapResponsiveValue } from './sprinkles.css';
259
260
// Theme-aware color mapping
261
const colorThemes = {
262
light: {
263
primary: '#007bff',
264
secondary: '#6c757d',
265
background: '#ffffff'
266
},
267
dark: {
268
primary: '#66b3ff',
269
secondary: '#9ca3af',
270
background: '#1a1a1a'
271
}
272
} as const;
273
274
function mapThemeColors(colorValue: any, theme: 'light' | 'dark') {
275
return mapResponsiveValue(colorValue, (value, condition) => {
276
// You could use condition to determine theme per breakpoint
277
return colorThemes[theme][value] || value;
278
});
279
}
280
281
// Usage
282
const themedColor = mapThemeColors(
283
{ mobile: 'primary', desktop: 'secondary' },
284
'dark'
285
);
286
// Result: { mobile: '#66b3ff', desktop: '#9ca3af' }
287
```
288
289
**Type-safe custom conditional values:**
290
291
```typescript
292
import { ConditionalValue } from "@vanilla-extract/sprinkles";
293
294
// Create custom conditional value types
295
export type ResponsiveValue<Value extends string | number> =
296
ConditionalValue<typeof responsiveProperties, Value>;
297
298
// Usage in component props
299
interface BoxProps {
300
align?: ResponsiveValue<'left' | 'center' | 'right'>;
301
spacing?: ResponsiveValue<'sm' | 'md' | 'lg'>;
302
}
303
304
function Box({ align = 'left', spacing = 'md' }: BoxProps) {
305
const alignmentClass = createAlignmentStyles(align);
306
const spacingValue = transformSpacing(spacing);
307
308
return sprinkles({
309
className: alignmentClass,
310
padding: spacingValue
311
});
312
}
313
```