Zero-runtime atomic CSS framework for vanilla-extract that generates static utility classes with type-safe composition
npx @tessl/cli install tessl/npm-vanilla-extract--sprinkles@1.6.0Vanilla Extract Sprinkles is a zero-runtime atomic CSS framework that generates static utility classes for vanilla-extract. It enables developers to create type-safe, custom atomic CSS systems by defining properties, conditions (media queries, selectors), and shorthands through a declarative configuration API. All CSS is generated at build time with optional lightweight runtime composition for dynamic styling.
npm install @vanilla-extract/sprinklesyarn add @vanilla-extract/sprinklespnpm add @vanilla-extract/sprinklesimport { defineProperties, createSprinkles } from "@vanilla-extract/sprinkles";For utilities:
import { createMapValueFn, createNormalizeValueFn } from "@vanilla-extract/sprinkles";For runtime usage:
import { createSprinkles } from "@vanilla-extract/sprinkles/createRuntimeSprinkles";For standalone utilities:
import { createMapValueFn, createNormalizeValueFn } from "@vanilla-extract/sprinkles/createUtils";import { defineProperties, createSprinkles } from "@vanilla-extract/sprinkles";
// Define properties with conditions and values
const responsiveProperties = defineProperties({
conditions: {
mobile: {},
tablet: { '@media': 'screen and (min-width: 768px)' },
desktop: { '@media': 'screen and (min-width: 1024px)' }
},
defaultCondition: 'mobile',
properties: {
display: ['none', 'flex', 'block', 'inline'],
flexDirection: ['row', 'column'],
paddingTop: {
small: '4px',
medium: '8px',
large: '16px'
}
},
shorthands: {
padding: ['paddingTop', 'paddingBottom', 'paddingLeft', 'paddingRight'],
paddingX: ['paddingLeft', 'paddingRight'],
paddingY: ['paddingTop', 'paddingBottom']
}
});
// Create sprinkles function
export const sprinkles = createSprinkles(responsiveProperties);
// Use in .css.ts files (build-time)
export const container = sprinkles({
display: 'flex',
paddingX: 'small',
flexDirection: {
mobile: 'column',
desktop: 'row'
}
});
// Use at runtime
const dynamicClass = sprinkles({
display: 'flex',
paddingTop: ['small', 'medium', 'large']
});Vanilla Extract Sprinkles is built around several key concepts:
defineProperties creates atomic CSS class configurations with support for conditions, shorthands, and responsive arrayscreateSprinkles generates a type-safe function that composes utility classesCore functionality for defining atomic CSS properties with conditions, shorthands, and responsive configurations. This is the foundation for creating custom utility class systems.
function defineProperties<
Properties extends AtomicProperties,
Conditions extends BaseConditions,
DefaultCondition extends keyof Conditions | Array<keyof Conditions> | false
>(
options: ConditionalAtomicOptions<Properties, Conditions, DefaultCondition>
): ConditionalAtomicStyles<Properties, Conditions, DefaultCondition>;Transform property definitions into type-safe utility functions that generate CSS class names. Supports both build-time and runtime usage patterns.
function createSprinkles<Args extends ReadonlyArray<SprinklesProperties>>(
...config: Args
): SprinklesFn<Args>;
type SprinklesFn<Args extends ReadonlyArray<SprinklesProperties>> = ((
props: SprinkleProps<Args>
) => string) & { properties: Set<keyof SprinkleProps<Args>> };Helper functions for working with conditional values in responsive and theme-based styling scenarios.
function createMapValueFn<SprinklesProperties extends Conditions<string>>(
properties: SprinklesProperties
): <OutputValue, Value>(
value: Value,
fn: (inputValue: ExtractValue<Value>, key: string) => OutputValue
) => OutputValue | Partial<Record<string, OutputValue>>;
function createNormalizeValueFn<SprinklesProperties extends Conditions<string>>(
properties: SprinklesProperties
): <Value>(
value: ConditionalValue<SprinklesProperties, Value>
) => Partial<Record<string, Value>>;Lightweight runtime version of sprinkles creation for dynamic styling without build-time constraints.
function createSprinkles<Args extends ReadonlyArray<SprinklesProperties>>(
...args: Args
): SprinklesFn<Args>;type ConditionalValue<
SprinklesProperties extends Conditions<string>,
Value extends string | number | boolean
> =
| Value
| Partial<Record<string, Value>>
| ResponsiveArrayByMaxLength<number, Value>;
type RequiredConditionalValue<
SprinklesProperties extends Conditions<string>,
Value extends string | number | boolean
> = Value | RequiredConditionalObject<string, string, Value> | ResponsiveArray<number, Value>;
interface ResponsiveArray<Length extends number, Value> extends ReadonlyArray<Value> {
0: Value;
length: Length;
}
interface SprinklesProperties {
styles: {
[property: string]:
| ConditionalWithResponsiveArrayProperty
| ConditionalProperty
| ShorthandProperty
| UnconditionalProperty;
};
}
interface ConditionalPropertyValue {
defaultClass: string | undefined;
conditions: {
[conditionName: string]: string;
};
}
interface ConditionalProperty {
values: {
[valueName: string]: ConditionalPropertyValue;
};
}
interface UnconditionalProperty {
values: {
[valueName: string]: {
defaultClass: string;
};
};
}
interface ShorthandProperty {
mappings: Array<string>;
}
interface ConditionalWithResponsiveArrayProperty {
responsiveArray: Array<string>;
values: {
[valueName: string]: ConditionalPropertyValue;
};
}
type AtomicProperties = {
[Property in keyof CSSProperties]?:
| Record<string, CSSProperties[Property] | Omit<StyleRule, ConditionKey>>
| ReadonlyArray<CSSProperties[Property]>;
} | Record<string, Record<string | number, Omit<StyleRule, ConditionKey>>>;
type BaseConditions = { [conditionName: string]: Partial<Record<ConditionKey, string>> };
type ConditionKey = '@media' | '@supports' | '@container' | 'selector';
type AtomicCSSProperties = {
[Property in keyof CSSProperties]?:
| Record<string, CSSProperties[Property] | Omit<StyleRule, ConditionKey>>
| ReadonlyArray<CSSProperties[Property]>;
};
type AtomicCustomProperties = Record<
string,
Record<string | number, Omit<StyleRule, ConditionKey>>
>;
type ResponsiveArrayByMaxLength<MaxLength extends number, Value> =
MaxLength extends 1 ? ResponsiveArray<1, Value | null> :
MaxLength extends 2 ? ResponsiveArray<1 | 2, Value | null> :
MaxLength extends 3 ? ResponsiveArray<1 | 2 | 3, Value | null> :
MaxLength extends 4 ? ResponsiveArray<1 | 2 | 3 | 4, Value | null> :
MaxLength extends 5 ? ResponsiveArray<1 | 2 | 3 | 4 | 5, Value | null> :
MaxLength extends 6 ? ResponsiveArray<1 | 2 | 3 | 4 | 5 | 6, Value | null> :
MaxLength extends 7 ? ResponsiveArray<1 | 2 | 3 | 4 | 5 | 6 | 7, Value | null> :
MaxLength extends 8 ? ResponsiveArray<1 | 2 | 3 | 4 | 5 | 6 | 7 | 8, Value | null> :
never;
type ResponsiveArrayConfig<Value> = ResponsiveArray<2 | 3 | 4 | 5 | 6 | 7 | 8, Value>;
type ConditionalAtomicOptions<
Properties extends AtomicProperties,
Conditions extends BaseConditions,
DefaultCondition extends keyof Conditions | Array<keyof Conditions> | false
> = {
'@layer'?: string;
properties: Properties;
conditions: Conditions;
defaultCondition: DefaultCondition;
};
type UnconditionalAtomicOptions<Properties extends AtomicProperties> = {
'@layer'?: string;
properties: Properties;
};
type ShorthandOptions<
Properties extends AtomicProperties,
Shorthands extends { [shorthandName: string]: Array<keyof Properties> }
> = {
shorthands: Shorthands;
};
type ResponsiveArrayOptions<
Conditions extends BaseConditions,
ResponsiveLength extends number
> = {
responsiveArray: ResponsiveArrayConfig<keyof Conditions> & {
length: ResponsiveLength;
};
};The following exports are deprecated but still available for backward compatibility:
/**
* @deprecated Use `defineProperties` instead
*/
const createAtomicStyles = defineProperties;
/**
* @deprecated Use `createSprinkles` instead
*/
const createAtomsFn = createSprinkles;/**
* @deprecated Use `createSprinkles` instead
*/
const createAtomsFn = createSprinkles;From createAtomicStyles to defineProperties:
// Old (deprecated)
import { createAtomicStyles } from "@vanilla-extract/sprinkles";
const styles = createAtomicStyles({ /* config */ });
// New (recommended)
import { defineProperties } from "@vanilla-extract/sprinkles";
const styles = defineProperties({ /* config */ });From createAtomsFn to createSprinkles:
// Old (deprecated) - Build-time
import { createAtomsFn } from "@vanilla-extract/sprinkles";
const atoms = createAtomsFn(properties);
// New (recommended) - Build-time
import { createSprinkles } from "@vanilla-extract/sprinkles";
const sprinkles = createSprinkles(properties);
// Old (deprecated) - Runtime
import { createAtomsFn } from "@vanilla-extract/sprinkles/createRuntimeSprinkles";
const atoms = createAtomsFn(properties);
// New (recommended) - Runtime
import { createSprinkles } from "@vanilla-extract/sprinkles/createRuntimeSprinkles";
const sprinkles = createSprinkles(properties);