Zero-runtime atomic CSS framework for vanilla-extract that generates static utility classes with type-safe composition
—
Core functionality for defining atomic CSS properties with conditions, shorthands, and responsive configurations. This is the foundation for creating custom utility class systems.
Defines a collection of utility classes with properties, conditions and shorthands. Supports multiple overloads for different configuration combinations.
/**
* Define properties with conditions, shorthands, and responsive arrays
*/
function defineProperties<
Properties extends AtomicProperties,
ResponsiveLength extends number,
Conditions extends BaseConditions,
Shorthands extends { [shorthandName: string]: Array<keyof Properties> },
DefaultCondition extends keyof Conditions | Array<keyof Conditions> | false
>(
options: ConditionalAtomicOptions<Properties, Conditions, DefaultCondition> &
ShorthandOptions<Properties, Shorthands> &
ResponsiveArrayOptions<Conditions, ResponsiveLength>
): ConditionalWithResponsiveArrayAtomicStyles<
Properties,
Conditions,
ResponsiveLength,
DefaultCondition
> & ShorthandAtomicStyles<Shorthands>;
/**
* Define properties with conditions and shorthands
*/
function defineProperties<
Properties extends AtomicProperties,
Conditions extends BaseConditions,
Shorthands extends { [shorthandName: string]: Array<keyof Properties> },
DefaultCondition extends keyof Conditions | Array<keyof Conditions> | false
>(
options: ConditionalAtomicOptions<Properties, Conditions, DefaultCondition> &
ShorthandOptions<Properties, Shorthands>
): ConditionalAtomicStyles<Properties, Conditions, DefaultCondition> &
ShorthandAtomicStyles<Shorthands>;
/**
* Define properties with conditions and responsive arrays
*/
function defineProperties<
Properties extends AtomicProperties,
Conditions extends BaseConditions,
ResponsiveLength extends number,
DefaultCondition extends keyof Conditions | Array<keyof Conditions> | false
>(
options: ConditionalAtomicOptions<Properties, Conditions, DefaultCondition> &
ResponsiveArrayOptions<Conditions, ResponsiveLength>
): ConditionalWithResponsiveArrayAtomicStyles<
Properties,
Conditions,
ResponsiveLength,
DefaultCondition
>;
/**
* Define properties with conditions only
*/
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>;
/**
* Define unconditional properties with shorthands
*/
function defineProperties<
Properties extends AtomicProperties,
Shorthands extends { [shorthandName: string]: Array<keyof Properties> }
>(
options: UnconditionalAtomicOptions<Properties> &
ShorthandOptions<Properties, Shorthands>
): UnconditionalAtomicStyles<Properties> & ShorthandAtomicStyles<Shorthands>;
/**
* Define unconditional properties only
*/
function defineProperties<Properties extends AtomicProperties>(
options: UnconditionalAtomicOptions<Properties>
): UnconditionalAtomicStyles<Properties>;The defineProperties function accepts various configuration options:
Define which CSS properties and values should be available. Properties can be defined as arrays for simple mappings or objects for semantic mappings.
// Array format for valid CSS values
properties: {
display: ['none', 'block', 'flex'],
flexDirection: ['row', 'column']
}
// Object format for semantic mappings
properties: {
padding: {
small: '4px',
medium: '8px',
large: '16px'
},
color: {
primary: '#007bff',
secondary: '#6c757d'
}
}
// Style objects for complex scenarios
properties: {
background: {
red: {
vars: { [alpha]: '1' },
background: `rgba(255, 0, 0, ${alpha})`
}
}
}Define a set of media/feature queries, selectors, or other conditions for the provided properties.
conditions: {
mobile: {},
tablet: { '@media': 'screen and (min-width: 768px)' },
desktop: { '@media': 'screen and (min-width: 1024px)' },
darkMode: { '@media': '(prefers-color-scheme: dark)' },
hover: { selector: '&:hover' },
focus: { selector: '&:focus' },
supportsGrid: { '@supports': 'display: grid' },
largeContainer: { '@container': '(min-width: 800px)' }
}Defines which condition(s) should be used when a non-conditional value is requested.
// Single default condition
defaultCondition: 'mobile'
// Multiple default conditions for mutually exclusive scenarios
defaultCondition: ['lightMode', 'darkMode']
// Force explicit condition usage
defaultCondition: falseMaps custom shorthand properties to multiple underlying CSS properties.
shorthands: {
padding: ['paddingTop', 'paddingBottom', 'paddingLeft', 'paddingRight'],
paddingX: ['paddingLeft', 'paddingRight'],
paddingY: ['paddingTop', 'paddingBottom'],
placeItems: ['justifyContent', 'alignItems']
}Providing an array of condition names enables responsive array notation by defining the order of conditions.
responsiveArray: ['mobile', 'tablet', 'desktop']Optional CSS layer for generated styles. This wraps all generated CSS classes in the specified layer, useful for managing cascade order in complex stylesheets.
// Add all generated styles to the 'utilities' layer
'@layer': 'utilities'
// Example with CSS layer
const layeredProperties = defineProperties({
'@layer': 'components',
properties: {
padding: {
small: '8px',
medium: '16px',
large: '24px'
},
margin: {
none: '0',
auto: 'auto'
}
}
});
// Generated CSS will be wrapped in @layer components { ... }CSS Output:
@layer components {
.padding_small_mobile {
padding: 8px;
}
.margin_auto_mobile {
margin: auto;
}
}Basic responsive properties:
import { defineProperties } from "@vanilla-extract/sprinkles";
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'],
flexDirection: ['row', 'column'],
paddingTop: {
small: '10px',
medium: '20px',
large: '30px'
}
}
});Color properties with theme conditions:
const colorProperties = defineProperties({
conditions: {
lightMode: {},
darkMode: { '@media': '(prefers-color-scheme: dark)' }
},
defaultCondition: 'lightMode',
properties: {
color: {
primary: '#007bff',
secondary: '#6c757d'
},
background: {
surface: '#ffffff',
elevated: '#f8f9fa'
}
}
});Properties with shorthands and responsive arrays:
const spacingProperties = defineProperties({
conditions: {
mobile: {},
tablet: { '@media': 'screen and (min-width: 768px)' },
desktop: { '@media': 'screen and (min-width: 1024px)' }
},
defaultCondition: 'mobile',
responsiveArray: ['mobile', 'tablet', 'desktop'],
properties: {
paddingTop: { small: '4px', medium: '8px', large: '16px' },
paddingBottom: { small: '4px', medium: '8px', large: '16px' },
paddingLeft: { small: '4px', medium: '8px', large: '16px' },
paddingRight: { small: '4px', medium: '8px', large: '16px' }
},
shorthands: {
padding: ['paddingTop', 'paddingBottom', 'paddingLeft', 'paddingRight'],
paddingX: ['paddingLeft', 'paddingRight'],
paddingY: ['paddingTop', 'paddingBottom']
}
});Unconditional properties:
const unconditionalProperties = defineProperties({
properties: {
fontFamily: {
heading: 'Georgia, serif',
body: 'Arial, sans-serif',
mono: 'Consolas, monospace'
},
borderRadius: {
small: '2px',
medium: '4px',
large: '8px'
}
}
});CSS layer integration:
const layeredUtilities = defineProperties({
'@layer': 'utilities',
conditions: {
mobile: {},
tablet: { '@media': 'screen and (min-width: 768px)' },
desktop: { '@media': 'screen and (min-width: 1024px)' }
},
defaultCondition: 'mobile',
properties: {
display: ['none', 'block', 'flex'],
position: ['static', 'relative', 'absolute', 'fixed'],
zIndex: {
hide: -1,
base: 0,
elevated: 10,
modal: 100
}
},
shorthands: {
hide: ['display'],
show: ['display']
}
});
// Generated CSS will be organized in the utilities layer
// @layer utilities {
// .display_none_mobile { display: none; }
// .zIndex_modal_mobile { z-index: 100; }
// /* etc... */
// }Install with Tessl CLI
npx tessl i tessl/npm-vanilla-extract--sprinkles