# Vanilla Extract
> ✋ This is an advanced feature recommended for library authors only.
---
# Vanilla Extract Documentation
# Source: https://raw.githubusercontent.com/vanilla-extract-css/vanilla-extract/master/site/docs/api/add-function-serializer.md
# Path: site/docs/api/add-function-serializer.md
---
title: addFunctionSerializer
parent: api
---
# addFunctionSerializer
> ✋ This is an advanced feature recommended for library authors only.
Typically, if you try to export a function from one of your stylesheets, you'll get an error that you can only export plain objects, arrays, strings, numbers and `null`/`undefined`.
If you're wanting to create higher level abstractions like [Sprinkles](/documentation/packages/sprinkles) or [Recipes](/documentation/packages/recipes), this is a big problem!
To address this limitation, the `addFunctionSerializer` utility allows you to annotate your functions with instructions on how to serialize them.
As a basic example, let's say we want to create a library called `my-awesome-styled-div` that lets you create a React component that renders a `
` with static CSS bound to it, and we want our consumers to use it in their `.css.ts` files like this:
```ts
// MyComponent.css.ts
import { styledDiv } from 'my-awesome-styled-div';
export const MyComponent = styledDiv({
background: 'blue',
padding: 12
});
```
Normally if you tried to do this, since `MyComponent` is a function, it would trigger an error during the build since it can't be serialized.
Luckily `addFunctionSerializer` can help us!
To understand how we could make this API work, we'll first look at what you might expect the implementation of `styledDiv` to look like.
```tsx
// index.tsx
import React from 'react';
import { style } from '@vanilla-extract/css';
export function styledDiv(styles) {
const className = style(styles);
return function Component(
props: React.ComponentProps<'div'>
) {
return (
);
};
}
```
This is a pretty simple implementation, but it has a couple of major problems that prevent it from working. Firstly, there's no way to convert this function to a string so it can be added to your runtime JavaScript bundle. Secondly, even if it could be serialized somehow, it relies on `@vanilla-extract/css` which can't generate static CSS files at runtime.
To fix this, we need to start by splitting the runtime code out into its own module and avoid using `@vanilla-extract/css`. In this case we'll create a `runtime.tsx` file. It must be available as a separate entry point from the library so that it can be imported within runtime code, for example:
```tsx
import { runtimeStyledDiv } from 'my-awesome-styled-div/runtime';
```
The runtime implementation would look like this:
```tsx
// runtime.tsx
import React from 'react';
export function runtimeStyledDiv(className) {
return function Component(
props: React.ComponentProps<'div'>
) {
return (
);
};
}
```
To make this work at runtime, we've had to alter the API a bit. Instead of accepting an object of styles, we now accept the generated class name. That's because this code will run in the end-user's browser where we no longer have the ability to generate static CSS.
We then need to annotate the generated component within `styledDiv` using `addFunctionSerializer`, providing the path to the runtime module, the name of the imported function, and the arguments that should be passed to it.
> ✋ All arguments passed to the runtime function must be serializable!
```ts
// index.ts
import { addFunctionSerializer } from '@vanilla-extract/css/functionSerializer';
import { style } from '@vanilla-extract/css';
import { runtimeStyledDiv } from './runtime';
export function styledDiv(styles) {
const className = style(styles);
const args = [className];
// First we call our runtime function at build time
const Component = runtimeStyledDiv(...args);
// Then we tell vanilla-extract how to serialize the previous
// function call by annotating its return value
addFunctionSerializer(Component, {
importPath: 'my-awesome-styled-div/runtime',
importName: 'runtimeStyledDiv',
args
});
// Return the result of calling the runtime function
return Component;
}
```
It takes a little bit of wiring to get this working, but thankfully our consumers don't need to know about any of this! By making use of `addFunctionSerializer`, we can now break free of the usual constraints around exports and provide much more expressive APIs to our library consumers.
---
# Vanilla Extract Documentation
# Source: https://raw.githubusercontent.com/vanilla-extract-css/vanilla-extract/master/site/docs/api/assign-vars.md
# Path: site/docs/api/assign-vars.md
---
title: assignVars
parent: api
---
# assignVars
Allows you to populate the values of a theme contract and assign them to a style.
While similar to [createTheme](/documentation/api/create-theme), this API provides more fine-grained control of how you populate the variables.
For example, this is useful for creating responsive themes since it can be used within `@media` blocks:
```ts compiled
// theme.css.ts
import {
createThemeContract,
style,
assignVars
} from '@vanilla-extract/css';
export const vars = createThemeContract({
space: {
small: null,
large: null
}
});
export const responsiveTheme = style({
vars: assignVars(vars, {
space: {
small: '4px',
large: '16px'
}
}),
'@media': {
'screen and (min-width: 1024px)': {
vars: assignVars(vars, {
space: {
small: '8px',
large: '32px'
}
})
}
}
});
```
## Partial theme contracts
As a convenience, the `assignVars` function can also handle populating sections of the theme contract.
For example, if the theme contract above also included a colour palette, we could choose to only implement the space scale responsively:
```ts compiled
// styles.css.ts
import {
createThemeContract,
style,
assignVars
} from '@vanilla-extract/css';
export const vars = createThemeContract({
color: {
brand: null,
accent: null
},
space: {
small: null,
large: null
}
});
export const responsiveTheme = style({
vars: assignVars(vars, {
color: {
brand: 'pink',
accent: 'aquamarine'
},
space: {
small: '4px',
large: '16px'
}
}),
'@media': {
'screen and (min-width: 1024px)': {
vars: assignVars(vars.space, {
small: '8px',
large: '32px'
})
}
}
});
```
---
# Vanilla Extract Documentation
# Source: https://raw.githubusercontent.com/vanilla-extract-css/vanilla-extract/master/site/docs/api/create-container.md
# Path: site/docs/api/create-container.md
---
title: createContainer
parent: api
---
# createContainer
Creates a single scoped container name for use with [CSS Container Queries]. This avoids potential naming collisions with other containers.
> 🚧 Ensure your target browsers [support container queries].
> Vanilla-extract supports the [container query syntax][css container queries] but does not polyfill the feature in unsupported browsers.
```ts compiled
// sidebar.css.ts
import {
style,
createContainer
} from '@vanilla-extract/css';
export const sidebarContainer = createContainer();
export const sidebar = style({
containerName: sidebarContainer,
containerType: 'inline-size'
});
// navigation.css.ts
import {
style,
createContainer
} from '@vanilla-extract/css';
import { sidebarContainer } from './sidebar.css.ts';
export const navigation = style({
'@container': {
[`${sidebarContainer} (min-width: 400px)`]: {
display: 'flex'
}
}
});
```
[css container queries]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Container_Queries
[support container queries]: https://caniuse.com/css-container-queries
---
# Vanilla Extract Documentation
# Source: https://raw.githubusercontent.com/vanilla-extract-css/vanilla-extract/master/site/docs/api/create-theme-contract.md
# Path: site/docs/api/create-theme-contract.md
---
title: createThemeContract
parent: api
---
# createThemeContract
Creates a contract of locally scoped variable names for themes to implement.
This is useful if you want to split your themes into different bundles. In this case, your themes would be defined in separate files, but we'll keep this example simple.
> 🎨 New to theming in vanilla-extract? Make sure you’ve read the [theming overview](/documentation/theming) first.
```ts compiled
// themes.css.ts
import {
createThemeContract,
createTheme,
style
} from '@vanilla-extract/css';
export const vars = createThemeContract({
color: {
brand: null
},
font: {
body: null
}
});
export const themeA = createTheme(vars, {
color: {
brand: 'blue'
},
font: {
body: 'arial'
}
});
export const themeB = createTheme(vars, {
color: {
brand: 'pink'
},
font: {
body: 'comic sans ms'
}
});
export const brandText = style({
color: vars.color.brand,
fontFamily: vars.font.body
});
```
---
# Vanilla Extract Documentation
# Source: https://raw.githubusercontent.com/vanilla-extract-css/vanilla-extract/master/site/docs/api/create-theme.md
# Path: site/docs/api/create-theme.md
---
title: createTheme
parent: api
---
# createTheme
Creates a locally scoped theme class and a theme contract which can be consumed within your styles.
> 🎨 New to theming in vanilla-extract? Make sure you’ve read the [theming overview](/documentation/theming) first.
```ts compiled
// theme.css.ts
import { createTheme, style } from '@vanilla-extract/css';
export const [themeClass, vars] = createTheme({
color: {
brand: 'blue'
},
font: {
body: 'arial'
}
});
export const brandText = style({
color: vars.color.brand,
fontFamily: vars.font.body
});
```
## Creating theme variants
Theme variants can be created by passing a theme contract as the first argument to `createTheme`.
All theme values must be provided or it’s a type error.
```ts compiled
// themes.css.ts
import { createTheme, style } from '@vanilla-extract/css';
export const [themeA, vars] = createTheme({
color: {
brand: 'blue'
},
font: {
body: 'arial'
}
});
export const themeB = createTheme(vars, {
color: {
brand: 'pink'
},
font: {
body: 'comic sans ms'
}
});
export const brandText = style({
color: vars.color.brand,
fontFamily: vars.font.body
});
```
## @layer
Themes can be assigned to a layer by name using the `@layer` key at the top-level of the theme definition.
> 🚧 Ensure your target browsers [support layers].
> Vanilla Extract supports the [layers syntax][layer] but does not polyfill the feature in unsupported browsers.
```ts compiled
// themes.css.ts
import { createTheme, layer } from '@vanilla-extract/css';
export const themeLayer = layer();
export const [themeA, vars] = createTheme({
'@layer': themeLayer,
color: {
brand: 'blue'
},
font: {
body: 'arial'
}
});
```
[support layers]: https://caniuse.com/css-cascade-layers
[layer]: https://developer.mozilla.org/en-US/docs/Web/CSS/@layer
---
# Vanilla Extract Documentation
# Source: https://raw.githubusercontent.com/vanilla-extract-css/vanilla-extract/master/site/docs/api/create-var.md
# Path: site/docs/api/create-var.md
---
title: createVar
parent: api
---
# createVar
Creates a single scoped CSS Variable reference.
```ts compiled
// accent.css.ts
import { createVar, style } from '@vanilla-extract/css';
export const accentVar = createVar();
```
As you can see, no CSS is generated when you create a variable, it is only a reference that can be set later on.
## Setting the variable
The variable reference created above can be set using the [“vars” key](/documentation/styling/#css-variables).
```ts compiled
// accent.css.ts
import { createVar, style } from '@vanilla-extract/css';
export const accentVar = createVar();
export const blue = style({
vars: {
[accentVar]: 'blue'
}
});
export const pink = style({
vars: {
[accentVar]: 'pink'
}
});
```
Keep in mind the value of the variable can be changed in another class or even in a media query. For example, let’s change the value when the user prefers a dark color-scheme:
```ts compiled
// accent.css.ts
import { createVar, style } from '@vanilla-extract/css';
export const accentVar = createVar();
export const blue = style({
vars: {
[accentVar]: 'blue'
},
'@media': {
'(prefers-color-scheme: dark)': {
vars: {
[accentVar]: 'lightblue'
}
}
}
});
export const pink = style({
vars: {
[accentVar]: 'pink'
},
'@media': {
'(prefers-color-scheme: dark)': {
vars: {
[accentVar]: 'lightpink'
}
}
}
});
```
## Using the variable
The variable reference can then be passed as the value for any CSS property.
```ts compiled
// style.css.ts
import { createVar, style } from '@vanilla-extract/css';
import { accentVar } from './accent.css.ts';
export const accentText = style({
color: accentVar
});
// accent.css.ts
import { createVar, style } from '@vanilla-extract/css';
export const accentVar = createVar();
export const blue = style({
vars: {
[accentVar]: 'blue'
}
});
export const pink = style({
vars: {
[accentVar]: 'pink'
}
});
```
## Assigning variables dynamically
CSS variables can also be assigned dynamically using APIs in [the `@vanilla-extract/dynamic` package](/documentation/packages/dynamic).
## @property rules
[@property] rules can also be created using `createVar`.
CSS variables with @property rules are used in the same way as regular CSS variables:
```ts compiled
// accent.css.ts
import { createVar, style } from '@vanilla-extract/css';
export const accentVar = createVar({
syntax: '
',
inherits: false,
initialValue: 'blue'
});
export const pink = style({
vars: {
[accentVar]: 'pink'
}
});
```
[@property]: https://developer.mozilla.org/en-US/docs/Web/CSS/@property
---
# Vanilla Extract Documentation
# Source: https://raw.githubusercontent.com/vanilla-extract-css/vanilla-extract/master/site/docs/api/create-view-transition.md
# Path: site/docs/api/create-view-transition.md
---
title: createViewTransition
parent: api
---
# createViewTransition
Creates a single scoped view transition name for use with [CSS View Transitions].
This avoids potential naming collisions with other view transitions.
> 🚧 Ensure your target browsers [support view transitions].
> Vanilla-extract supports the [view transition syntax][css view transitions] but does not polyfill the feature in unsupported browsers.
```ts compiled
// itemPage.css.ts
import {
style,
createViewTransition
} from '@vanilla-extract/css';
export const titleViewTransition = createViewTransition();
export const pageTitle = style({
viewTransitionName: titleViewTransition
});
// navigation.css.ts
import { style } from '@vanilla-extract/css';
import { titleViewTransition } from './itemPage.css.ts';
export const navigationLinkTitle = style({
viewTransitionName: titleViewTransition
});
```
[css view transitions]: https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API#css_additions
[support view transitions]: https://caniuse.com/view-transitions
---
# Vanilla Extract Documentation
# Source: https://raw.githubusercontent.com/vanilla-extract-css/vanilla-extract/master/site/docs/api/fallback-var.md
# Path: site/docs/api/fallback-var.md
---
title: fallbackVar
parent: api
---
# fallbackVar
Provides fallback values for variables that have been created using vanilla-extract APIs, e.g. `createVar`, `createTheme`, etc.
As these APIs produce variable references that contain the CSS var function, e.g. `var(--colorVar__qzfheg0)`, it is necessary to handle the wrapping function when providing a fallback.
```ts compiled
// style.css.ts
import {
createVar,
fallbackVar,
style
} from '@vanilla-extract/css';
export const colorVar = createVar();
export const color = style({
color: fallbackVar(colorVar, 'blue')
});
```
## Multiple fallback values
The `fallbackVar` function handles falling back across multiple variables by providing multiple parameters.
```ts compiled
// style.css.ts
import {
createVar,
fallbackVar,
style
} from '@vanilla-extract/css';
export const primaryVar = createVar();
export const secondaryVar = createVar();
export const color = style({
color: fallbackVar(primaryVar, secondaryVar, 'blue')
});
```
---
# Vanilla Extract Documentation
# Source: https://raw.githubusercontent.com/vanilla-extract-css/vanilla-extract/master/site/docs/api/font-face.md
# Path: site/docs/api/font-face.md
---
title: fontFace
parent: api
---
# fontFace
Creates a locally scoped [font-family](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-family) for the defined [@font-face](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face).
```ts compiled
// text.css.ts
import { fontFace, style } from '@vanilla-extract/css';
const comicSans = fontFace({
src: 'local("Comic Sans MS")'
});
export const font = style({
fontFamily: comicSans
});
```
## Multiple Fonts with Single Family
The `fontFace` function allows you to pass an array of font-face rules that may contain different rules but treat them as if they are from one font family.
```ts compiled
// text.css.ts
import { fontFace, style } from '@vanilla-extract/css';
const gentium = fontFace([
{
src: 'local("Gentium")',
fontWeight: 'normal'
},
{
src: 'local("Gentium Bold")',
fontWeight: 'bold'
}
]);
export const font = style({
fontFamily: gentium
});
```
---
# Vanilla Extract Documentation
# Source: https://raw.githubusercontent.com/vanilla-extract-css/vanilla-extract/master/site/docs/api/keyframes.md
# Path: site/docs/api/keyframes.md
---
title: keyframes
parent: api
---
# keyframes
Creates a locally scoped [animation name](https://developer.mozilla.org/en-US/docs/Web/CSS/animation-name) for the defined [@keyframes](https://developer.mozilla.org/en-US/docs/Web/CSS/@keyframes).
```ts compiled
// animation.css.ts
import { keyframes, style } from '@vanilla-extract/css';
const rotate = keyframes({
'0%': { transform: 'rotate(0deg)' },
'100%': { transform: 'rotate(360deg)' }
});
export const spin = style({
animationName: rotate,
animationDuration: '3s'
});
// or interpolate as a shorthand:
export const spinAgain = style({
animation: `${rotate} 3s`
});
```
## Animating CSS variables
CSS variables can be animated by setting their values within the `vars` property of a keyframe step:
```ts compiled
// animation.css.ts
import {
createVar,
fallbackVar,
keyframes,
style
} from '@vanilla-extract/css';
const angle = createVar({
syntax: '',
inherits: false,
initialValue: '0deg'
});
const angleKeyframes = keyframes({
'0%': {
vars: {
[angle]: '0deg'
}
},
'100%': {
vars: {
[angle]: '360deg'
}
}
});
export const root = style({
backgroundImage: `linear-gradient(${angle}, rgba(153, 70, 198, 0.35) 0%, rgba(28, 56, 240, 0.46) 100%)`,
animation: `${angleKeyframes} 7s infinite ease-in-out both`,
vars: {
// This will fallback to 180deg if @property is not supported by the browser
[angle]: fallbackVar(angle, '180deg')
}
});
```
---
# Vanilla Extract Documentation
# Source: https://raw.githubusercontent.com/vanilla-extract-css/vanilla-extract/master/site/docs/api/layer.md
# Path: site/docs/api/layer.md
---
title: layer
parent: api
---
# layer
Creates a single scoped [layer]. This avoids potential naming collisions with other layers.
> 🚧 Ensure your target browsers [support layers].
> Vanilla Extract supports the [layers syntax][layer] but does not polyfill the feature in unsupported browsers.
```ts compiled
// layers.css.ts
import { layer } from '@vanilla-extract/css';
export const reset = layer('reset');
export const framework = layer('framework');
export const app = layer('app');
```
## Nesting layers
To facilitate organisation of styles, [layer nesting] is supported by providing a `parent` layer reference via the options object.
This will generate the shorthand syntax, i.e. `parent.child`, while also making the relationship between layers explicit.
```ts compiled
// layers.css.ts
import { layer } from '@vanilla-extract/css';
export const reset = layer('reset');
export const framework = layer('framework');
export const typography = layer(
{ parent: framework },
'typography'
);
```
## Assigning styles
Styles can be assigned to a layer by name, using the `@layer` key in the style object.
In this example, we first import the `layers.css.ts` stylesheet, setting up the order of the layers, then create a style within the `reset` layer.
```ts compiled
// reset.css.ts
import { style } from '@vanilla-extract/css';
import { reset } from './layers.css.ts';
export const noMargin = style({
'@layer': {
[reset]: {
margin: 0
}
}
});
// layers.css.ts
import { layer } from '@vanilla-extract/css';
export const reset = layer('reset');
export const framework = layer('framework');
export const typography = layer(
{ parent: framework },
'typography'
);
```
## Layer merging
In order to generate the smallest possible CSS output, Vanilla Extract will merge styles that are assigned to the same layer within the same file, if it can be done without impacting the precedence of the rules.
Notice in this example, while the `themedHeading` style is created before the the `heading` style, it appears later in the stylesheet. This is due to it being assigned to the `theme` layer — which is declared after the `base` layer.
```ts compiled
// typography.css.ts
import { style, layer } from '@vanilla-extract/css';
const base = layer();
const theme = layer();
const text = style({
'@layer': {
[base]: {
fontSize: '1rem'
}
}
});
const themedHeading = style({
'@layer': {
[theme]: {
color: 'rebeccapurple'
}
}
});
const heading = style({
'@layer': {
[base]: {
fontSize: '2.4rem'
}
}
});
```
[layer]: https://developer.mozilla.org/en-US/docs/Web/CSS/@layer
[layer nesting]: https://developer.mozilla.org/en-US/docs/Web/CSS/@layer#nesting_layers
[support layers]: https://caniuse.com/css-cascade-layers
---
# Vanilla Extract Documentation
# Source: https://raw.githubusercontent.com/vanilla-extract-css/vanilla-extract/master/site/docs/api/style-variants.md
# Path: site/docs/api/style-variants.md
---
title: styleVariants
parent: api
---
# styleVariants
Creates a collection of named style rules.
This is useful for mapping component props to styles, for example: ``
```ts compiled
// styles.css.ts
import { styleVariants } from '@vanilla-extract/css';
export const background = styleVariants({
primary: { background: 'blue' },
secondary: { background: 'aqua' }
});
// app.tsx
import { background } from './styles.css.ts';
interface SectionProps {
variant: keyof typeof background;
}
const Section = ({ variant }: SectionProps) => (
);
```
## Style composition
Variant styles can also be composed into a single rule by providing an array of styles.
> ✨ Curious about style composition? Make sure you’ve read the [style composition overview](/documentation/style-composition) first.
```ts compiled
// styles.css.ts
import { style, styleVariants } from '@vanilla-extract/css';
const base = style({ padding: 12 });
export const variant = styleVariants({
primary: [base, { background: 'blue' }],
secondary: [base, { background: 'aqua' }]
});
```
## Mapping variants
To make generating sets of style variants easier, a mapping function can be provided as the second argument.
For example, we can iterate over the `palette` below, without having to define the style rule explicitly for each entry.
```ts compiled
// styles.css.ts
import { style, styleVariants } from '@vanilla-extract/css';
const base = style({ padding: 12 });
const palette = {
primary: 'blue',
secondary: 'aqua'
};
export const variant = styleVariants(
palette,
(paletteColor) => [base, { background: paletteColor }]
);
```
---
# Vanilla Extract Documentation
# Source: https://raw.githubusercontent.com/vanilla-extract-css/vanilla-extract/master/site/docs/api/style.md
# Path: site/docs/api/style.md
---
title: style
parent: api
---
# style
Creates a style rule with a locally scoped class name.
> 🖌 New to styling with vanilla-extract? Make sure you’ve read the [styling overview](/documentation/styling) first.
This class can then be imported directly into your consuming application code, creating a first-class contract between your CSS and JavaScript.
```ts compiled
// styles.css.ts
import { style } from '@vanilla-extract/css';
export const flexContainer = style({
display: 'flex'
});
// app.ts
import { flexContainer } from './styles.css.ts';
document.write(`
...
`);
```
CSS Variables, simple pseudos, selectors and media/feature queries are all supported.
```ts compiled
// styles.css.ts
import { style, createVar } from '@vanilla-extract/css';
const scopedVar = createVar();
export const className = style({
display: 'flex',
vars: {
[scopedVar]: 'green',
'--global-variable': 'purple'
},
':hover': {
color: 'red'
},
selectors: {
'&:nth-child(2n)': {
background: '#fafafa'
}
},
'@media': {
'screen and (min-width: 768px)': {
padding: 10
}
},
'@supports': {
'(display: grid)': {
display: 'grid'
}
}
});
```
## Style composition
The `style` function allows you to pass an array of class names and/or style objects but continue to treat them as if they are a single class name.
> ✨ Curious about style composition? Make sure you’ve read the [style composition overview](/documentation/style-composition) first.
```ts compiled
// styles.css.ts
import { style } from '@vanilla-extract/css';
const base = style({ padding: 12 });
const primary = style([base, { background: 'blue' }]);
```
## Style merging
When passing an array of style objects they will be deep merged into a single class. This is useful when creating utilities to help construct consistent styles.
```ts compiled
// styles.css.ts
import { style } from '@vanilla-extract/css';
const responsiveStyle = ({ tablet, desktop }) => ({
'@media': {
'screen and (min-width: 768px)': tablet,
'screen and (min-width: 1024px)': desktop
}
});
const container = style([
{
display: 'flex',
flexDirection: 'column'
},
responsiveStyle({
tablet: { flex: 1, content: 'I will be overridden' },
desktop: { flexDirection: 'row' }
}),
{
'@media': {
'screen and (min-width: 768px)': {
content: 'I win!'
}
}
}
]);
```
---
# Vanilla Extract Documentation
# Source: https://raw.githubusercontent.com/vanilla-extract-css/vanilla-extract/master/site/docs/global-api/create-global-theme-contract.md
# Path: site/docs/global-api/create-global-theme-contract.md
---
title: createGlobalThemeContract
parent: global-api
---
# createGlobalThemeContract
Creates a contract of globally scoped variable names for themes to implement.
This is useful if you want to make your theme contract available to non-JavaScript environments.
> 🎨 New to theming in vanilla-extract? Make sure you’ve read the [theming overview](/documentation/theming) first.
```ts compiled
// themes.css.ts
import {
createGlobalThemeContract,
createGlobalTheme,
style
} from '@vanilla-extract/css';
export const vars = createGlobalThemeContract({
color: {
brand: 'color-brand'
},
font: {
body: 'font-body'
}
});
createGlobalTheme(':root', vars, {
color: {
brand: 'blue'
},
font: {
body: 'arial'
}
});
export const brandText = style({
color: vars.color.brand,
fontFamily: vars.font.body
});
```
## Formatting the variable names
A map function can be provided as the second argument which has access to the value and the object path.
For example, you can automatically prefix all variable names:
```ts compiled
// themes.css.ts
import {
createGlobalThemeContract,
createGlobalTheme
} from '@vanilla-extract/css';
export const vars = createGlobalThemeContract(
{
color: {
brand: 'color-brand'
},
font: {
body: 'font-body'
}
},
(value) => `prefix-${value}`
);
createGlobalTheme(':root', vars, {
color: {
brand: 'blue'
},
font: {
body: 'arial'
}
});
```
Or, automatically generate names from the object path.
For example, converting to title case:
```ts compiled
// themes.css.ts
import {
createGlobalThemeContract,
createGlobalTheme
} from '@vanilla-extract/css';
const toTitleCase = (s) =>
`${s.charAt(0).toUpperCase()}${s.slice(1)}`;
export const vars = createGlobalThemeContract(
{
color: {
brand: null
},
font: {
body: null
}
},
(_value, path) => `${path.map(toTitleCase).join('')}`
);
createGlobalTheme(':root', vars, {
color: {
brand: 'blue'
},
font: {
body: 'arial'
}
});
```
---
# Vanilla Extract Documentation
# Source: https://raw.githubusercontent.com/vanilla-extract-css/vanilla-extract/master/site/docs/global-api/create-global-theme.md
# Path: site/docs/global-api/create-global-theme.md
---
title: createGlobalTheme
parent: global-api
---
# createGlobalTheme
Creates a theme attached to a global selector, but with locally scoped variable names.
> 🎨 New to theming in vanilla-extract? Make sure you’ve read the [theming overview](/documentation/theming) first.
```ts compiled
// theme.css.ts
import { createGlobalTheme } from '@vanilla-extract/css';
export const vars = createGlobalTheme(':root', {
color: {
brand: 'blue'
},
font: {
body: 'arial'
}
});
```
All theme values must be provided or it’s a type error.
Importing this stylesheet as a side effect to include the styles in your CSS bundle.
```ts
// app.ts
import './theme.css.ts';
```
## Implementing a Theme Contract
An existing theme contract can be implemented by passing it as the second argument.
```ts compiled
// theme.css.ts
import {
createThemeContract,
createGlobalTheme
} from '@vanilla-extract/css';
export const vars = createThemeContract({
color: {
brand: null
},
font: {
body: null
}
});
createGlobalTheme(':root', vars, {
color: {
brand: 'blue'
},
font: {
body: 'arial'
}
});
```
## @layer
Global themes can be assigned to a layer by name using the `@layer` key at the top-level of the theme definition.
> 🚧 Ensure your target browsers [support layers].
> Vanilla Extract supports the [layers syntax][layer] but does not polyfill the feature in unsupported browsers.
```ts compiled
// theme.css.ts
import {
createGlobalTheme,
layer
} from '@vanilla-extract/css';
export const themeLayer = layer();
export const vars = createGlobalTheme(':root', {
'@layer': themeLayer,
color: {
brand: 'blue'
},
font: {
body: 'arial'
}
});
```
[support layers]: https://caniuse.com/css-cascade-layers
[layer]: https://developer.mozilla.org/en-US/docs/Web/CSS/@layer
---
# Vanilla Extract Documentation
# Source: https://raw.githubusercontent.com/vanilla-extract-css/vanilla-extract/master/site/docs/global-api/create-global-var.md
# Path: site/docs/global-api/create-global-var.md
---
title: createGlobalVar
parent: global-api
---
# createGlobalVar
Similar to [`createVar`], `createGlobalVar` creates a global CSS variable reference:
```ts compiled
// vars.css.ts
import {
createGlobalVar,
style
} from '@vanilla-extract/css';
const opacityVar = createGlobalVar('opacity');
export const content = style({
opacity: opacityVar
});
```
[@property] rules can also be created using `createGlobalVar`:
```ts compiled
// vars.css.ts
import {
createGlobalVar,
style
} from '@vanilla-extract/css';
const opacityVar = createGlobalVar('opacity', {
syntax: '',
inherits: false,
initialValue: '0.5'
});
export const content = style({
opacity: opacityVar
});
```
[`createVar`]: /documentation/api/create-var
[@property]: https://developer.mozilla.org/en-US/docs/Web/CSS/@property
---
# Vanilla Extract Documentation
# Source: https://raw.githubusercontent.com/vanilla-extract-css/vanilla-extract/master/site/docs/global-api/global-font-face.md
# Path: site/docs/global-api/global-font-face.md
---
title: globalFontFace
parent: global-api
---
# globalFontFace
Creates a globally scoped custom font.
```ts compiled
// text.css.ts
import {
globalFontFace,
style
} from '@vanilla-extract/css';
const comicSans = 'GlobalComicSans';
globalFontFace(comicSans, {
src: 'local("Comic Sans MS")'
});
export const font = style({
fontFamily: comicSans
});
```
## Multiple Global Fonts with Single Family
The `globalFontFace` function allows you to pass an array of font-face rules that may contain different rules but treat them as if they are from one font family.
```ts compiled
// text.css.ts
import {
globalFontFace,
style
} from '@vanilla-extract/css';
const gentium = 'GlobalGentium';
globalFontFace(gentium, [
{
src: 'local("Gentium")',
fontWeight: 'normal'
},
{
src: 'local("Gentium Bold")',
fontWeight: 'bold'
}
]);
export const font = style({
fontFamily: gentium
});
```
---
# Vanilla Extract Documentation
# Source: https://raw.githubusercontent.com/vanilla-extract-css/vanilla-extract/master/site/docs/global-api/global-keyframes.md
# Path: site/docs/global-api/global-keyframes.md
---
title: globalKeyframes
parent: global-api
---
# globalKeyframes
Creates a globally scoped set of keyframes.
```ts compiled
// animation.css.ts
import {
globalKeyframes,
style
} from '@vanilla-extract/css';
const rotate = 'globalRotate';
globalKeyframes(rotate, {
'0%': { transform: 'rotate(0deg)' },
'100%': { transform: 'rotate(360deg)' }
});
export const spin = style({
animation: `3s infinite ${rotate}`
});
```
---
# Vanilla Extract Documentation
# Source: https://raw.githubusercontent.com/vanilla-extract-css/vanilla-extract/master/site/docs/global-api/global-layer.md
# Path: site/docs/global-api/global-layer.md
---
title: globalLayer
parent: global-api
---
# globalLayer
Creates a globally scoped [layer].
> 🚧 Ensure your target browsers [support layers].
> Vanilla Extract supports the [layers syntax][layer] but does not polyfill the feature in unsupported browsers.
```ts compiled
// layers.css.ts
import { globalLayer } from '@vanilla-extract/css';
globalLayer('reset');
```
Useful for orchestrating the order of layers within the stylesheet, for example:
```ts compiled
// layers.css.ts
import { globalLayer } from '@vanilla-extract/css';
globalLayer('reset');
globalLayer('framework');
globalLayer('app');
```
## Nesting layers
To facilitate organisation of styles, [layer nesting] is supported by providing a `parent` layer name via the options object.
This will generate the shorthand syntax, i.e. `parent.child`, while also making the relationship between layers explicit.
```ts compiled
// layers.css.ts
import { globalLayer } from '@vanilla-extract/css';
globalLayer('reset');
globalLayer('framework');
globalLayer({ parent: 'framework' }, 'typography');
```
## Assigning styles
Styles can be assigned to a layer by name, using the `@layer` key in the style object.
In this example, we first import the `layers.css.ts` stylesheet, setting up the order of the layers, then create a style within the `reset` layer.
```ts compiled
// reset.css.ts
import { style } from '@vanilla-extract/css';
import './layers.css.ts';
export const noMargin = style({
'@layer': {
reset: {
margin: 0
}
}
});
// layers.css.ts
import { globalLayer } from '@vanilla-extract/css';
globalLayer('reset');
globalLayer('framework');
globalLayer({ parent: 'framework' }, 'typography');
```
Best practice would be to expose the layer references from the `layers.css.ts` stylesheet, and use those when creating styles.
```ts compiled
// reset.css.ts
import { style } from '@vanilla-extract/css';
import { reset } from './layers.css.ts';
export const noMargin = style({
'@layer': {
[reset]: {
margin: 0
}
}
});
// layers.css.ts
import { globalLayer } from '@vanilla-extract/css';
export const reset = globalLayer('reset');
export const framework = globalLayer('framework');
export const typography = globalLayer(
{ parent: framework },
'typography'
);
```
This is particularly useful when using the nested layers feature, as the parent and child names are computed.
In our example, the name of the typography layer becomes `framework.typography`:
```ts compiled
// text.css.ts
import { style } from '@vanilla-extract/css';
import { typography } from './layers.css.ts';
export const standard = style({
'@layer': {
[typography]: {
fontSize: '1rem'
}
}
});
// layers.css.ts
import { globalLayer } from '@vanilla-extract/css';
export const reset = globalLayer('reset');
export const framework = globalLayer('framework');
export const typography = globalLayer(
{ parent: framework },
'typography'
);
```
## Layer merging
In order to generate the smallest possible CSS output, Vanilla Extract will merge styles that are assigned to the same layer within the same file, if it can be done without impacting the precedence of the rules.
Notice in this example, while the `themedHeading` style is created before the the `heading` style, it appears later in the stylesheet. This is due to it being assigned to the `theme` layer — which is declared after the `base` layer.
```ts compiled
// typography.css.ts
import { style, globalLayer } from '@vanilla-extract/css';
const base = globalLayer('base');
const theme = globalLayer('theme');
const text = style({
'@layer': {
[base]: {
fontSize: '1rem'
}
}
});
const themedHeading = style({
'@layer': {
[theme]: {
color: 'rebeccapurple'
}
}
});
const heading = style({
'@layer': {
[base]: {
fontSize: '2.4rem'
}
}
});
```
[layer]: https://developer.mozilla.org/en-US/docs/Web/CSS/@layer
[layer nesting]: https://developer.mozilla.org/en-US/docs/Web/CSS/@layer#nesting_layers
[support layers]: https://caniuse.com/css-cascade-layers
---
# Vanilla Extract Documentation
# Source: https://raw.githubusercontent.com/vanilla-extract-css/vanilla-extract/master/site/docs/global-api/global-style.md
# Path: site/docs/global-api/global-style.md
---
title: globalStyle
parent: global-api
---
# globalStyle
Creates styles attached to a global selector.
Requires a selector string as the first parameter, followed by a style object.
```ts compiled
// app.css.ts
import { globalStyle } from '@vanilla-extract/css';
globalStyle('html, body', {
margin: 0
});
```
> 🧠 The global style object cannot use [simple pseudo](/documentation/styling#simple-pseudo-selectors) or [complex selectors](/documentation/styling/#complex-selectors).
> This avoids unexpected results when merging with the global selector, e.g. `ul li:first-child, a > span`.
Global selectors can also contain references to other scoped class names.
```ts compiled
// app.css.ts
import { style, globalStyle } from '@vanilla-extract/css';
export const parentClass = style({});
globalStyle(`${parentClass} > a`, {
color: 'pink'
});
```
---
# Vanilla Extract Documentation
# Source: https://raw.githubusercontent.com/vanilla-extract-css/vanilla-extract/master/site/docs/integrations/astro.md
# Path: site/docs/integrations/astro.md
---
title: Astro
parent: integrations
---
# Astro
Integrating Vanilla Extract with [Astro](https://astro.build) is done with the help of the [Vite plugin][vite integration].
## Installation
```bash
npm install @vanilla-extract/css
npm install @vanilla-extract/vite-plugin --save-dev
```
## Setup
Add Vanilla Extract Vite plugin to the Astro configuration:
```js
import { defineConfig } from 'astro/config';
import { vanillaExtractPlugin } from '@vanilla-extract/vite-plugin';
// https://astro.build/config
export default defineConfig({
vite: {
plugins: [vanillaExtractPlugin()]
}
});
```
You'll then be able to use `style` and other APIs in `.css.ts` files.
```ts
// button.css.ts
import { style } from '@vanilla-extract/css';
export const button = style({
padding: '0.5rem 1rem',
border: 'none',
borderRadius: '0.25rem',
color: 'white',
background: '#333'
});
```
And now you can reference styles in your Astro component:
```tsx
// Button.astro
---
import { button } from './button.css'
---
Click Me!
```
## Configuration
See the [Vite integration page][vite integration] for documentation on the Vite plugin.
[vite integration]: /documentation/integrations/vite
---
# Vanilla Extract Documentation
# Source: https://raw.githubusercontent.com/vanilla-extract-css/vanilla-extract/master/site/docs/integrations/esbuild.md
# Path: site/docs/integrations/esbuild.md
---
title: esbuild
parent: integrations
---
# esbuild
A plugin for integrating vanilla-extract with [esbuild](https://esbuild.github.io/).
## Installation
```bash
npm install --save-dev @vanilla-extract/esbuild-plugin
```
## Setup
Add the plugin to your build script, along with any desired [plugin configuration](#configuration).
```js
// bundle.js
const {
vanillaExtractPlugin
} = require('@vanilla-extract/esbuild-plugin');
require('esbuild')
.build({
entryPoints: ['app.ts'],
bundle: true,
plugins: [vanillaExtractPlugin()],
outfile: 'out.js'
})
.catch(() => process.exit(1));
```
## Configuration
```js
// bundle.js
const {
vanillaExtractPlugin
} = require('@vanilla-extract/esbuild-plugin');
require('esbuild').build({
plugins: [
vanillaExtractPlugin({
// configuration
})
]
});
```
The plugin accepts the following as optional configuration:
### processCss
As esbuild currently doesn't have a way to process the CSS generated by vanilla-extract, you can optionally use the `processCss` option.
For example, to run autoprefixer over the generated CSS.
```js
// bundle.js
const {
vanillaExtractPlugin
} = require('@vanilla-extract/esbuild-plugin');
const postcss = require('postcss');
const autoprefixer = require('autoprefixer');
async function processCss(css) {
const result = await postcss([autoprefixer]).process(
css,
{
from: undefined /* suppress source map warning */
}
);
return result.css;
}
require('esbuild')
.build({
entryPoints: ['app.ts'],
bundle: true,
plugins: [
vanillaExtractPlugin({
processCss
})
],
outfile: 'out.js'
})
.catch(() => process.exit(1));
```
### identifiers
Different formatting of identifiers (e.g. class names, keyframes, CSS Vars, etc) can be configured by selecting from the following options:
- `short` identifiers are a 7+ character hash. e.g. `hnw5tz3`
- `debug` identifiers contain human readable prefixes representing the owning filename and a potential rule level debug name. e.g. `myfile_mystyle_hnw5tz3`
- A custom identifier function takes an object parameter with properties `hash`, `filePath`, `debugId`, and `packageName`, and returns a customized identifier. e.g.
```ts
vanillaExtractPlugin({
identifiers: ({ hash }) => `prefix_${hash}`
});
```
Each integration will set a default value based on the configuration options passed to the bundler.
### esbuildOptions
esbuild is used internally to compile `.css.ts` files before evaluating them to extract styles. You can pass additional options here to customize that process.
Accepts a subset of esbuild build options (`plugins`, `external`, `define`, `loader`, `tsconfig` and `conditions`). See the [build API](https://esbuild.github.io/api/#build-api) documentation.
---
# Vanilla Extract Documentation
# Source: https://raw.githubusercontent.com/vanilla-extract-css/vanilla-extract/master/site/docs/integrations/gatsby.md
# Path: site/docs/integrations/gatsby.md
---
title: Gatsby
parent: integrations
---
# Gatsby
A plugin for integrating vanilla-extract with [Gatsby](https://www.gatsbyjs.com).
## Installation
```bash
npm install @vanilla-extract/css @vanilla-extract/webpack-plugin gatsby-plugin-vanilla-extract
```
## Setup
Add the plugin to your Gatsby configuration.
```js
// gatsby-config.js
module.exports = {
plugins: [`gatsby-plugin-vanilla-extract`]
};
```
## Configuration
See [plugin documentation](https://github.com/gatsby-uc/plugins/tree/main/packages/gatsby-plugin-vanilla-extract).
---
# Vanilla Extract Documentation
# Source: https://raw.githubusercontent.com/vanilla-extract-css/vanilla-extract/master/site/docs/integrations/next.md
# Path: site/docs/integrations/next.md
---
title: Next.js
parent: integrations
---
# Next.js
A plugin for integrating vanilla-extract with [Next.js](https://nextjs.org).
## Installation
```bash
npm install --save-dev @vanilla-extract/next-plugin
```
## Setup
If you don't have a `next.config.js` file in the root of your project, create one. Add the plugin to your `next.config.js` file.
```js
const {
createVanillaExtractPlugin
} = require('@vanilla-extract/next-plugin');
const withVanillaExtract = createVanillaExtractPlugin();
/** @type {import('next').NextConfig} */
const nextConfig = {};
module.exports = withVanillaExtract(nextConfig);
```
If required, this plugin can be composed with other plugins.
```js
const {
createVanillaExtractPlugin
} = require('@vanilla-extract/next-plugin');
const withVanillaExtract = createVanillaExtractPlugin();
const withMDX = require('@next/mdx')({
extension: /\.mdx$/
});
/** @type {import('next').NextConfig} */
const nextConfig = {};
module.exports = withVanillaExtract(withMDX(nextConfig));
```
## Configuration
The plugin accepts the following as optional configuration:
### identifiers
Different formatting of identifiers (e.g. class names, keyframes, CSS Vars, etc) can be configured by selecting from the following options:
- `short` identifiers are a 7+ character hash. e.g. `hnw5tz3`
- `debug` identifiers contain human readable prefixes representing the owning filename and a potential rule level debug name. e.g. `myfile_mystyle_hnw5tz3`
- A custom identifier function takes an object parameter with properties `hash`, `filePath`, `debugId`, and `packageName`, and returns a customized identifier. e.g.
```ts
const withVanillaExtract = createVanillaExtractPlugin({
identifiers: ({ hash }) => `prefix_${hash}`
});
```
Each integration will set a default value based on the configuration options passed to the bundler.
## Transpiling Vanilla Extract-dependent Libraries
By default, Next.js does not allow importing of TypeScript files outside of the app root.
If your application depends on a TypeScript library, whether that be a local package within your app's monorepo, or a dependency inside `node_modules`, and that library styles its components with Vanilla Extract, but does _not_ compile its styles, then that library needs to be added to [`transpilePackages`] in your Next.js config:
```tsx
// App.tsx
import { Button } from '@company/design-system';
export default function App() {
// This is unstyled and/or throws errors about Vanilla Extract being used in runtime
return Hello, World! ;
}
```
```js
// next.config.js
const {
createVanillaExtractPlugin
} = require('@vanilla-extract/next-plugin');
const withVanillaExtract = createVanillaExtractPlugin();
/** @type {import('next').NextConfig} */
const nextConfig = {
transpilePackages: ['@company/design-system']
};
// Next.js Vanilla Extract integration will now compile @company/design-system styles
module.exports = withVanillaExtract(nextConfig);
```
[`transpilepackages`]: https://nextjs.org/docs/app/api-reference/next-config-js/transpilePackages
---
# Vanilla Extract Documentation
# Source: https://raw.githubusercontent.com/vanilla-extract-css/vanilla-extract/master/site/docs/integrations/parcel.md
# Path: site/docs/integrations/parcel.md
---
title: Parcel
parent: integrations
---
# Parcel
A Parcel transformer for integrating vanilla-extract with [Parcel](https://parceljs.org/).
## Installation
```bash
npm install --save-dev @vanilla-extract/parcel-transformer
```
## Setup
Add the transformer to your Parcel configuration.
```json
// .parcelrc
{
"transformers": {
"*.css.ts": ["@vanilla-extract/parcel-transformer"]
}
}
```
---
# Vanilla Extract Documentation
# Source: https://raw.githubusercontent.com/vanilla-extract-css/vanilla-extract/master/site/docs/integrations/remix.md
# Path: site/docs/integrations/remix.md
---
title: Remix
parent: integrations
---
# Remix
[Remix](https://remix.run) provides support for Vanilla Extract out of the box. See [Vanilla Extract | Remix](https://remix.run/docs/en/main/styling/vanilla-extract) for details.
Remix's (unstable) Vite compiler works with the [Vite integration]. It's as simple as adding the `@vanilla-extract/vite-plugin` to your Vite config:
```js
import { unstable_vitePlugin as remix } from '@remix-run/dev';
import { vanillaExtractPlugin } from '@vanilla-extract/vite-plugin';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [remix(), vanillaExtractPlugin()]
});
```
See [Vite (Unstable) | Remix](https://remix.run/docs/en/main/future/vite#add-vanilla-extract-plugin) for more details.
[vite integration]: /documentation/integrations/vite
---
# Vanilla Extract Documentation
# Source: https://raw.githubusercontent.com/vanilla-extract-css/vanilla-extract/master/site/docs/integrations/rollup.md
# Path: site/docs/integrations/rollup.md
---
title: Rollup
parent: integrations
---
# Rollup
A plugin for integrating vanilla-extract with [Rollup](https://rollupjs.org/).
This plugin is useful for library development but not suitable for application bundles.
Rollup has no built-in CSS bundling, so this plugin just outputs styles as individual CSS assets.
For applications we instead recommend to use Vite
(which itself uses Rollup under the hood but comes with its own CSS bundling).
## Installation
```bash
npm install --save-dev @vanilla-extract/rollup-plugin
```
## Setup
Add the plugin to your Rollup configuration, along with any desired [plugin configuration](#configuration).
```js
// rollup.config.js
import { vanillaExtractPlugin } from '@vanilla-extract/rollup-plugin';
export default {
plugins: [vanillaExtractPlugin()]
};
```
This plugin works well with Rollup's `preserveModules`.
Rollup by default places assets in an "assets" directory.
You can configure [asset file names](https://rollupjs.org/guide/en/#outputassetfilenames) if you care about CSS assets being placed right next to the corresponding JS files.
```js
// rollup.config.js
import { vanillaExtractPlugin } from '@vanilla-extract/rollup-plugin';
export default {
plugins: [vanillaExtractPlugin()],
output: {
preserveModules: true,
assetFileNames({ name }) {
return name?.replace(/^src\//, '') ?? '';
}
}
};
```
## Configuration
```js
// rollup.config.js
import { vanillaExtractPlugin } from '@vanilla-extract/rollup-plugin';
export default {
plugins: [
vanillaExtractPlugin({
// configuration
})
]
};
```
The plugin accepts the following as optional configuration:
### identifiers
Different formatting of identifiers (e.g. class names, keyframes, CSS Vars, etc) can be configured by selecting from the following options:
- `short` identifiers are a 7+ character hash. e.g. `hnw5tz3`
- `debug` identifiers contain human readable prefixes representing the owning filename and a potential rule level debug name. e.g. `myfile_mystyle_hnw5tz3`
- A custom identifier function takes an object parameter with properties `hash`, `filePath`, `debugId`, and `packageName`, and returns a customized identifier. e.g.
```ts
vanillaExtractPlugin({
identifiers: ({ hash }) => `prefix_${hash}`
});
```
Each integration will set a default value based on the configuration options passed to the bundler.
### untable_injectedFilescopes
Injects filescopes into Vanilla Extract modules instead of generating CSS.
This is useful for utility or component libraries that prefer their consumers to process Vanilla Extract files instead of bundling CSS.
Note that this flag only works with `preserveModules: true`.
### esbuildOptions
esbuild is used internally to compile `.css.ts` files before evaluating them to extract styles.
You can pass additional options here to customize that process.
Accepts a subset of esbuild build options (`plugins`, `external`, `define`, `loader`, `tsconfig` and `conditions`).
See the [build API](https://esbuild.github.io/api/#build-api) documentation.
### extract
Extract all generated `.css` into one bundle. This also removes side effect `import '*.css'` statements.
```ts
vanillaExtractPlugin({
extract: {
name: 'bundle.css',
sourcemap: false
}
});
```
- `name`: Default `'bundle.css'`. Name the bundled CSS. [`output.assetFilenames`](https://rollupjs.org/configuration-options/#output-assetfilenames) can affect this.
- `sourcemap`: Default `false`. Set to `true` to also output `.css.map` file.
---
# Vanilla Extract Documentation
# Source: https://raw.githubusercontent.com/vanilla-extract-css/vanilla-extract/master/site/docs/integrations/vite.md
# Path: site/docs/integrations/vite.md
---
title: Vite
parent: integrations
---
# Vite
A plugin for integrating vanilla-extract with [Vite](https://vitejs.dev/).
## Installation
```bash
npm install --save-dev @vanilla-extract/vite-plugin
```
## Setup
Add the plugin to your Vite configuration, along with any desired [plugin configuration](#configuration).
```js
// vite.config.js
import { vanillaExtractPlugin } from '@vanilla-extract/vite-plugin';
export default {
plugins: [vanillaExtractPlugin()]
};
```
## Configuration
```js
// vite.config.js
import { vanillaExtractPlugin } from '@vanilla-extract/vite-plugin';
export default {
plugins: [
vanillaExtractPlugin({
// configuration
})
]
};
```
The plugin accepts the following as optional configuration:
### identifiers
Different formatting of identifiers (e.g. class names, keyframes, CSS Vars, etc) can be configured by selecting from the following options:
- `short` identifiers are a 7+ character hash. e.g. `hnw5tz3`
- `debug` identifiers contain human readable prefixes representing the owning filename and a potential rule level debug name. e.g. `myfile_mystyle_hnw5tz3`
- A custom identifier function takes an object parameter with properties `hash`, `filePath`, `debugId`, and `packageName`, and returns a customized identifier. e.g.
```ts
vanillaExtractPlugin({
identifiers: ({ hash }) => `prefix_${hash}`
});
```
Each integration will set a default value based on the configuration options passed to the bundler.
---
# Vanilla Extract Documentation
# Source: https://raw.githubusercontent.com/vanilla-extract-css/vanilla-extract/master/site/docs/integrations/webpack.md
# Path: site/docs/integrations/webpack.md
---
title: Webpack
parent: integrations
---
# Webpack
A plugin for integrating vanilla-extract with [webpack](https://webpack.js.org).
## Installation
```bash
npm install --save-dev @vanilla-extract/webpack-plugin
```
## Setup
Add the plugin to your Webpack configuration, along with any desired [plugin configuration](#configuration).
```js
// webpack.config.js
const {
VanillaExtractPlugin
} = require('@vanilla-extract/webpack-plugin');
module.exports = {
plugins: [new VanillaExtractPlugin()]
};
```
> You'll need to ensure you're handling CSS files in your webpack config.
For example:
```js
// webpack.config.js
const {
VanillaExtractPlugin
} = require('@vanilla-extract/webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
plugins: [
new VanillaExtractPlugin(),
new MiniCssExtractPlugin()
],
module: {
rules: [
{
test: /\.vanilla\.css$/i, // Targets only CSS files generated by vanilla-extract
use: [
MiniCssExtractPlugin.loader,
{
loader: require.resolve('css-loader'),
options: {
url: false // Required as image imports should be handled via JS/TS import statements
}
}
]
}
]
}
};
```
If you already have `css-loader` configured, make sure to add `exclude: /\.vanilla\.css$/i` to that rule's configuration.
## Configuration
```js
// webpack.config.js
const {
VanillaExtractPlugin
} = require('@vanilla-extract/webpack-plugin');
module.exports = {
plugins: [
new VanillaExtractPlugin({
// configuration
})
]
};
```
The plugin accepts the following as optional configuration:
### identifiers
Different formatting of identifiers (e.g. class names, keyframes, CSS Vars, etc) can be configured by selecting from the following options:
- `short` identifiers are a 7+ character hash. e.g. `hnw5tz3`
- `debug` identifiers contain human readable prefixes representing the owning filename and a potential rule level debug name. e.g. `myfile_mystyle_hnw5tz3`
- A custom identifier function takes an object parameter with properties `hash`, `filePath`, `debugId`, and `packageName`, and returns a customized identifier. e.g.
```ts
new VanillaExtractPlugin({
identifiers: ({ hash }) => `prefix_${hash}`
});
```
Each integration will set a default value based on the configuration options passed to the bundler.
---
# Vanilla Extract Documentation
# Source: https://raw.githubusercontent.com/vanilla-extract-css/vanilla-extract/master/README.md
# Path: README.md
# 🧁 vanilla-extract
**Zero-runtime Stylesheets-in-TypeScript.**
Write your styles in TypeScript (or JavaScript) with locally scoped class names and CSS Variables, then generate static CSS files at build time.
Basically, it’s [“CSS Modules](https://github.com/css-modules/css-modules)-in-TypeScript” but with scoped CSS Variables + heaps more.
🔥 All styles generated at build time — just like [Sass](https://sass-lang.com), [Less](http://lesscss.org), etc.
✨ Minimal abstraction over standard CSS.
🦄 Works with any front-end framework — or even without one.
🌳 Locally scoped class names — just like [CSS Modules.](https://github.com/css-modules/css-modules)
🚀 Locally scoped [CSS Variables](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties), `@keyframes` and `@font-face` rules.
🎨 High-level theme system with support for simultaneous themes. No globals!
🛠 Utils for generating variable-based `calc` expressions.
💪 Type-safe styles via [CSSType.](https://github.com/frenic/csstype)
🏃♂️ Optional runtime version for development and testing.
🙈 Optional API for dynamic runtime theming.
---
🌐 [Check out the documentation site for setup guides, examples and API docs.](https://vanilla-extract.style)
---
🖥 [Try it out for yourself in CodeSandbox.](https://codesandbox.io/s/github/vanilla-extract-css/vanilla-extract/tree/master/examples/webpack-react?file=/src/App.css.ts)
---
**Write your styles in `.css.ts` files.**
```ts
// styles.css.ts
import { createTheme, style } from '@vanilla-extract/css';
export const [themeClass, vars] = createTheme({
color: {
brand: 'blue'
},
font: {
body: 'arial'
}
});
export const exampleStyle = style({
backgroundColor: vars.color.brand,
fontFamily: vars.font.body,
color: 'white',
padding: 10
});
```
> 💡 Once you've [configured your build tooling,](https://vanilla-extract.style/documentation/getting-started/) these `.css.ts` files will be evaluated at build time. None of the code in these files will be included in your final bundle. Think of it as using TypeScript as your preprocessor instead of Sass, Less, etc.
**Then consume them in your markup.**
```ts
// app.ts
import { themeClass, exampleStyle } from './styles.css.ts';
document.write(`
`);
```
---
Want to work at a higher level while maximising style re-use? Check out 🍨 [Sprinkles](https://vanilla-extract.style/documentation/packages/sprinkles), our official zero-runtime atomic CSS framework, built on top of vanilla-extract.
---
## Thanks
- [Nathan Nam Tran](https://twitter.com/naistran) for creating [css-in-js-loader](https://github.com/naistran/css-in-js-loader), which served as the initial starting point for [treat](https://seek-oss.github.io/treat), the precursor to this library.
- [Stitches](https://stitches.dev/) for getting us excited about CSS-Variables-in-JS.
- [SEEK](https://www.seek.com.au) for giving us the space to do interesting work.
## License
MIT.
---
# Vanilla Extract Documentation
# Source: https://raw.githubusercontent.com/vanilla-extract-css/vanilla-extract/master/site/docs/overview/getting-started.md
# Path: site/docs/overview/getting-started.md
---
title: Getting Started
---
# Getting Started
## Installation
```bash
npm install @vanilla-extract/css
```
## Bundler Integration
Vanilla-extract requires that you have set up a bundler and configured it to handle CSS.
This allows your styles to be handled the same as any other dependencies in your code, importing and bundling only what is required.
Install your integration of preference:
- [vite]
- [esbuild]
- [webpack]
- [next]
- [parcel]
- [rollup]
- [gatsby]
## Create a style
A stylesheet can be made by adding a `.css.ts` file into your project.
For example:
```ts compiled
// styles.css.ts
import { style } from '@vanilla-extract/css';
export const container = style({
padding: 10
});
```
Importantly, this does two things:
1. creates a locally scoped class
2. exports the generated class name.
## Apply the style
To apply the style to an element, we need to import it from our stylesheet.
By importing the style we receive the scoped class name that was generated and we can apply it to the `class` attribute on the element.
```ts compiled
// app.ts
import { container } from './styles.css.ts';
document.write(`
`);
// styles.css.ts
import { style } from '@vanilla-extract/css';
export const container = style({
padding: 10
});
```
As a side effect of this import, the CSS is also processed by the selected bundler integration and handled accordingly.
[vite]: /documentation/integrations/vite/
[esbuild]: /documentation/integrations/esbuild/
[webpack]: /documentation/integrations/webpack/
[next]: /documentation/integrations/next/
[rollup]: /documentation/integrations/rollup/
[gatsby]: /documentation/integrations/gatsby/
[parcel]: /documentation/integrations/parcel/
---
# Vanilla Extract Documentation
# Source: https://raw.githubusercontent.com/vanilla-extract-css/vanilla-extract/master/site/docs/overview/style-composition.md
# Path: site/docs/overview/style-composition.md
---
title: Composition
---
# Style Composition
Style composition is a special feature of vanilla-extract that makes it easy to get maximum re-use from your styles.
It allows you to pass an array of class names and/or [styles] but continue to treat them as if they are a single class name.
```ts compiled
// styles.css.ts
import { style } from '@vanilla-extract/css';
const base = style({ padding: 12 });
const primary = style([base, { background: 'blue' }]);
const secondary = style([base, { background: 'aqua' }]);
```
Let's look at how this works in practice.
When you create a style you receive a class name back in return.
```ts compiled
// styles.css.ts
import { style } from '@vanilla-extract/css';
// base = 'styles_base__8uideo0'
const base = style({ padding: 12 });
```
However, when using style composition you will receive multiple classes in a single string, separated by a single space character, often referred to as a classlist.
```ts compiled
// styles.css.ts
import { style } from '@vanilla-extract/css';
// base = 'styles_base__8uideo0'
const base = style({ padding: 12 });
// primary = 'styles_base__8uideo0 styles_primary__8uideo1'
const primary = style([base, { background: 'blue' }]);
```
This doesn't affect usage when assigning to the class property on DOM elements as they already accept a classlist.
However, what if we want to use our style inside another styles selector?
```ts compiled
// styles.css.ts
import { style } from '@vanilla-extract/css';
const base = style({ padding: 12 });
const primary = style([base, { background: 'blue' }]);
const text = style({
selectors: {
[`${primary} &`]: {
color: 'white'
}
}
});
```
When selectors are processed internally, the composed classes are removed, only leaving behind a single unique identifier class.
This allows you to treat them as if they were a single class within vanilla-extract selectors.
To ensure that this behaviour works as expected, when multiple pre-existing classes are composed, a new identifier is created and added to the classlist.
```ts compiled
// styles.css.ts
import { style, globalStyle } from '@vanilla-extract/css';
const background = style({ background: 'mintcream' });
const padding = style({ padding: 12 });
// container = 'styles_container__8uideo2'
export const container = style([background, padding]);
globalStyle(`${container} *`, {
boxSizing: 'border-box'
});
```
[styles]: /documentation/styling/
---
# Vanilla Extract Documentation
# Source: https://raw.githubusercontent.com/vanilla-extract-css/vanilla-extract/master/site/docs/overview/styling.md
# Path: site/docs/overview/styling.md
---
title: Styling
---
# Styling
All the styling APIs in Vanilla Extract take a style object as input.
Describing styles as a JavaScript object enables much better use of TypeScript through your styling code, as the styles are typed data-structures like the rest of your application code.
It also brings type-safety and autocomplete to CSS authoring (via [csstype]).
## CSS Properties
At the top-level of the style object, CSS properties can be set just like when writing a regular CSS class.
The only difference is all properties use `camelCase` rather than `kebab-case`.
```ts compiled
// app.css.ts
import { style, globalStyle } from '@vanilla-extract/css';
export const myStyle = style({
display: 'flex',
paddingTop: '3px'
});
globalStyle('body', {
margin: 0
});
```
### Unitless Properties
Some properties accept numbers as values. Excluding [unitless properties], these values are assumed to be a pixel and `px` is automatically appended to the value.
```ts compiled
// styles.css.ts
import { style } from '@vanilla-extract/css';
export const myStyle = style({
// cast to pixels
padding: 10,
marginTop: 25,
// unitless properties
flexGrow: 1,
opacity: 0.5
});
```
### Vendor Prefixes
If you want to target a vendor specific property (e.g. `-webkit-tap-highlight-color`), you can do so using `PascalCase` and removing the beginning `-`.
```ts compiled
// styles.css.ts
import { style } from '@vanilla-extract/css';
export const myStyle = style({
WebkitTapHighlightColor: 'rgba(0, 0, 0, 0)'
});
```
## CSS Variables
In regular CSS, variables (or CSS custom properties) are able to be set alongside the other properties within the rule.
In Vanilla Extract, CSS variables must be nested within the `vars` key — providing more accurate static typing for the other CSS properties.
```ts compiled
// styles.css.ts
import { style } from '@vanilla-extract/css';
const myStyle = style({
vars: {
'--my-global-variable': 'purple'
}
});
```
The `vars` key also accepts scoped CSS variables, created via the [createVar] API.
```ts compiled
// styles.css.ts
import { style, createVar } from '@vanilla-extract/css';
const myVar = createVar();
const myStyle = style({
vars: {
[myVar]: 'purple'
}
});
```
## Media Queries
Unlike in regular CSS, Vanilla Extract lets you embed media queries **within** your style definitions using the `@media` key.
This allows you to easily co-locate the responsive rules of a style into a single data-structure.
```ts compiled
// styles.css.ts
import { style } from '@vanilla-extract/css';
const myStyle = style({
'@media': {
'screen and (min-width: 768px)': {
padding: 10
},
'(prefers-reduced-motion)': {
transitionProperty: 'color'
}
}
});
```
When processing your code into CSS, Vanilla Extract will always render your media queries **at the end of the file**. This means styles inside the `@media` key will always have higher precedence than other styles due to CSS rule order precedence.
> 🧠 When it's safe to do so, Vanilla Extract will merge your `@media`, `@supports`, and `@container` condition blocks together to create the smallest possible CSS output.
## Selectors
There are two methods of specifying selectors for a given style, simple pseudo selectors that can be used alongside all other CSS properties, and the `selectors` option which allows construction of more complex rules.
> 🧠 All selectors are not available for `globalStyle`. This API accepts a selector as its first parameter (e.g. `ul li:first-of-type, a > span`), merging selectors may produce unexpected results.
### Simple Pseudo Selectors
Simple pseudo selectors are those that don’t take any parameters and therefore can be easily detected and statically typed. These can be used at the top level alongside the other [CSS properties] and can only contain [CSS Properties] and [CSS Variables].
```ts compiled
// styles.css.ts
import { style } from '@vanilla-extract/css';
const myStyle = style({
':hover': {
color: 'pink'
},
':first-of-type': {
color: 'blue'
},
'::before': {
content: ''
}
});
```
### Complex Selectors
More complex rules can be written using the `selectors` key.
To improve maintainability, each style block can only target a single element. To enforce this, all selectors must target the `&` character which is a reference to the current element.
```ts compiled
// styles.css.ts
import { style } from '@vanilla-extract/css';
const link = style({
selectors: {
'&:hover:not(:active)': {
border: '2px solid aquamarine'
},
'nav li > &': {
textDecoration: 'underline'
}
}
});
```
Selectors can also reference other scoped class names.
```ts compiled
// styles.css.ts
import { style } from '@vanilla-extract/css';
export const parent = style({});
export const child = style({
selectors: {
[`${parent}:focus &`]: {
background: '#fafafa'
}
}
});
```
Invalid selectors are those attempting to target an element other than the current class.
```ts
// styles.css.ts
import { style } from '@vanilla-extract/css';
const invalid = style({
selectors: {
// ❌ ERROR: Targetting `a[href]`
'& a[href]': {...},
// ❌ ERROR: Targetting `.otherClass`
'& ~ div > .otherClass': {...}
}
});
```
If you want to target another scoped class then it should be defined within the style block of that class instead.
```ts
// styles.css.ts
import { style } from '@vanilla-extract/css';
// Invalid example:
export const child = style({});
export const parent = style({
selectors: {
// ❌ ERROR: Targetting `child` from `parent`
[`& ${child}`]: {...}
}
});
// Valid example:
export const parent = style({});
export const child = style({
selectors: {
[`${parent} &`]: {...}
}
});
```
If you need to globally target child nodes within the current element (e.g. `'& a[href]'`), you should use [globalStyle] instead.
```ts compiled
// styles.css.ts
import { style, globalStyle } from '@vanilla-extract/css';
export const parent = style({});
globalStyle(`${parent} a[href]`, {
color: 'pink'
});
```
### Circular Selectors
If your selectors are dependent on each other you can use [getters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get) to define them:
```ts compiled
// styles.css.ts
import { style } from '@vanilla-extract/css';
export const child = style({
background: 'blue',
get selectors() {
return {
[`${parent} &`]: {
color: 'red'
}
};
}
});
export const parent = style({
background: 'yellow',
selectors: {
[`&:has(${child})`]: {
padding: 10
}
}
});
```
## Container Queries
Container queries work the same as [media queries] and are nested inside the `@container` key.
> 🚧 Ensure your target browsers [support container queries]. Vanilla Extract supports the [container query syntax] but does not polyfill the feature in unsupported browsers.
```ts compiled
// styles.css.ts
import { style } from '@vanilla-extract/css';
const myStyle = style({
'@container': {
'(min-width: 768px)': {
padding: 10
}
}
});
```
You can also create scoped containers using [createContainer].
```ts compiled
// styles.css.ts
import {
style,
createContainer
} from '@vanilla-extract/css';
const sidebar = createContainer();
const myStyle = style({
containerName: sidebar,
'@container': {
[`${sidebar} (min-width: 768px)`]: {
padding: 10
}
}
});
```
## Layers
As with media queries above, Vanilla Extract lets you assign styles to [layers][layer] by using the `@layer` key **within** your style definition.
> 🚧 Ensure your target browsers [support layers].
> Vanilla Extract supports the [layers syntax][layer] but does not polyfill the feature in unsupported browsers.
[layer]: https://developer.mozilla.org/en-US/docs/Web/CSS/@layer
[support layers]: https://caniuse.com/css-cascade-layers
```ts compiled
// styles.css.ts
import { style } from '@vanilla-extract/css';
const text = style({
'@layer': {
typography: {
fontSize: '1rem'
}
}
});
```
The `@layer` key also accepts a scoped layer reference, created via the [layer][layer api] API.
```ts compiled
// styles.css.ts
import { style, layer } from '@vanilla-extract/css';
const typography = layer();
const text = style({
'@layer': {
[typography]: {
fontSize: '1rem'
}
}
});
```
To learn more about managing layers, check out the API documentation for [layer][layer api] and [globalLayer][global layer api].
## Supports Queries
Supports queries work the same as [Media queries] and are nested inside the `@supports` key.
```ts compiled
// styles.css.ts
import { style } from '@vanilla-extract/css';
const myStyle = style({
'@supports': {
'(display: grid)': {
display: 'grid'
}
}
});
```
## Fallback Styles
When using CSS property values that don't exist in some browsers, you'll often declare the property twice and the older browser will ignore the value it doesn't understand.
This isn't possible using JS objects as you can't declare the same key twice.
So instead, we use an array to define fallback values.
```ts compiled
// styles.css.ts
import { style } from '@vanilla-extract/css';
export const myStyle = style({
// In Firefox and IE the "overflow: overlay" will be
// ignored and the "overflow: auto" will be applied
overflow: ['auto', 'overlay']
});
```
[csstype]: https://github.com/frenic/csstype
[unitless properties]: https://github.com/vanilla-extract-css/vanilla-extract/blob/6068246343ceb58a04006f4ce9d9ff7ecc7a6c09/packages/css/src/transformCss.ts#L25
[createvar]: /documentation/api/create-var/
[layer api]: /documentation/api/layer/
[global layer api]: /documentation/global-api/global-layer/
[createcontainer]: /documentation/api/create-container/
[css properties]: #css-properties
[css variables]: #css-variables
[globalstyle]: /documentation/global-api/global-style
[media queries]: #media-queries
[support container queries]: https://caniuse.com/css-container-queries
[container query syntax]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Container_Queries
---
# Vanilla Extract Documentation
# Source: https://raw.githubusercontent.com/vanilla-extract-css/vanilla-extract/master/site/docs/overview/test-environments.md
# Path: site/docs/overview/test-environments.md
---
title: Test Environments
---
# Test Environments
When executing a `.css.ts` file, class name identifiers will be returned as expected, and if running in a browser-like environment, such as [jsdom], then real styles will be injected into the document. However for vanilla-extract styles to work in a test environment, a transform needs to be applied to the code.
Currently, [Jest] and [Vitest] have official integrations. Please reach out in the [Discord] or [Discussions] for help with other setups.
## Jest
Install the [Jest] transformer
```bash
npm install --save-dev @vanilla-extract/jest-transform
```
Add the transform to your Jest configuration.
```json
// jest.config.js
{
"transform": {
"\\.css\\.ts$": "@vanilla-extract/jest-transform"
}
}
```
### Remove Style Mocking
It is very common in Jest setups to have a mock file returned for all `.css` files. This clashes with vanilla-extract as Jest can't differentiate between `.css` and `.css.ts` imports.
For example:
```json
// package.json
{
"jest": {
"moduleNameMapper": {
// ❌ Breaks .css.ts imports
"\\.css$": "/styleMock.js"
}
}
}
```
Ideally, remove this mock from your setup. However, if you need to support both at the same time you'll need a way to target your regular CSS files. Using a folder for all your CSS files, or giving your CSS files a custom extension will work.
```json
// package.json
{
"jest": {
"moduleNameMapper": {
"my-css-folder/.*\\.css$": "/styleMock.js",
"\\.legacy\\.css$": "/styleMock.js"
}
}
}
```
## Vitest
If you are already using vanilla-extract with [Vite] then no setup should be required as [Vitest] references your existing vite config file.
If using [Vitest] in other environments, install the `@vanilla-extract/vite-plugin`:
```bash
npm install --save-dev @vanilla-extract/vite-plugin
```
Add the plugin to your Vitest configuration.
```ts
// vitest.config.ts
import { defineConfig } from 'vitest/config';
import { vanillaExtractPlugin } from '@vanilla-extract/vite-plugin';
export default defineConfig({
plugins: [vanillaExtractPlugin()]
});
```
## Disabling Runtime Styles
While testing against actual styles is often desirable, it can be a major slowdown in your tests. If your tests don’t require styles to be available, importing `disableRuntimeStyles` will prevent all style creation.
```ts
// setupTests.ts
import '@vanilla-extract/css/disableRuntimeStyles';
```
[vite]: https://vitejs.dev/
[vitest]: https://vitest.dev/
[jsdom]: https://github.com/jsdom/jsdom
[jest]: https://jestjs.io/
[discord]: https://discord.gg/6nCfPwwz6w
[discussions]: https://github.com/vanilla-extract-css/vanilla-extract/discussions
---
# Vanilla Extract Documentation
# Source: https://raw.githubusercontent.com/vanilla-extract-css/vanilla-extract/master/site/docs/overview/theming.md
# Path: site/docs/overview/theming.md
---
title: Theming
---
# Theming
Themes are often thought of as global, application wide concepts. While vanilla-extract themes are great for that, they can also be used for more focussed, lower level use-cases.
For example, a component being rendered in different color schemes.
Theming in vanilla-extract is really just a set of helpers on top of the scoped CSS variable creation provided by [createVar].
To understand how it works, let's take a look at an example.
```ts compiled
// theme.css.ts
import { createTheme } from '@vanilla-extract/css';
export const [themeClass, vars] = createTheme({
color: {
brand: 'blue'
},
font: {
body: 'arial'
}
});
```
Here we’ve called [createTheme] with our theme implementation.
Based on this, vanilla-extract will return two things:
- **A class name:** a container class for the provided theme variables.
- **A theme contract:** a typed data-structure of CSS variables, matching the shape of the provided theme implementation.
After processing this file, the resulting compiled JS will look something like this:
```js
// theme.js
// Example result of the compiled JS
import './theme.css';
export const vars = {
color: {
brand: 'var(--color-brand__l520oi1)'
},
font: {
body: 'var(--font-body__l520oi2)'
}
};
export const themeClass = 'theme_themeClass__l520oi0';
```
To create an alternative version of this theme, call [createTheme] again.
But this time pass the existing theme contract (i.e. `vars`), as well as the new values.
```ts compiled
// theme.css.ts
import { createTheme } from '@vanilla-extract/css';
export const [themeClass, vars] = createTheme({
color: {
brand: 'blue'
},
font: {
body: 'arial'
}
});
export const otherThemeClass = createTheme(vars, {
color: {
brand: 'red'
},
font: {
body: 'helvetica'
}
});
```
By passing in an existing theme contract, instead of creating new CSS variables the existing ones are reused, but assigned to new values within a new CSS class.
On top of this, vanilla-extract knows the type of the existing theme contract and requires you implement it completely and correctly.
After processing the updated file, the resulting compiled JS will look something like this:
```js
// theme.js
// Example result of the compiled JS
import './theme.css';
export const vars = {
color: {
brand: 'var(--color-brand__l520oi1)'
},
font: {
body: 'var(--font-body__l520oi2)'
}
};
export const themeClass = 'theme_themeClass__l520oi0';
export const otherThemeClass =
'theme_otherThemeClass__l520oi3';
```
As can be observed, the only addition here is the reference to the new theme class name.
## Code Splitting Themes
While [createTheme] makes getting started with a theme really easy, it has some trade-offs.
It couples the definition of our theme contract to a specific theme implementation.
It also means all your alternative themes must import the original theme to access the theme contract.
This causes you to unintentionally import the original theme's CSS as well, making it impossible to CSS code-split your themes.
This is where [createThemeContract] comes in.
Remember before when we said themes are comprised of a theme contract and a CSS class implementing the theme?
Well [createThemeContract] lets us define the contract without generating any CSS!
Implementing the above scenario with [createThemeContract] would look something like the following:
```ts compiled
// contract.css.ts
import { createThemeContract } from '@vanilla-extract/css';
export const vars = createThemeContract({
color: {
brand: ''
},
font: {
body: ''
}
});
```
Based on this contract individual themes can now be created. Each theme will need to populate the contract in its entirety.
```ts compiled
// blueTheme.css.ts
import { createTheme } from '@vanilla-extract/css';
import { vars } from './contract.css.ts';
export const blueThemeClass = createTheme(vars, {
color: {
brand: 'blue'
},
font: {
body: 'arial'
}
});
// redTheme.css.ts
import { createTheme } from '@vanilla-extract/css';
import { vars } from './contract.css.ts';
export const redThemeClass = createTheme(vars, {
color: {
brand: 'red'
},
font: {
body: 'helvetica'
}
});
// contract.css.ts
import { createThemeContract } from '@vanilla-extract/css';
export const vars = createThemeContract({
color: {
brand: ''
},
font: {
body: ''
}
});
```
> 🧠 When creating a theme contract, the values of the input are ignored so you can pass an empty string, null, or real values. Whatever makes sense to you.
Now we have two themes implementing the same contract, but importing either one will only import their respective CSS!
## Dynamic Theming
Sometimes theme values aren't known until runtime.
Theme contracts are a perfect fit for this situation as they are just collections of CSS variables.
This means they can easily be set as inline styles while still retaining type safety.
We can use the [assignInlineVars] API from the [tiny] `@vanilla-extract/dynamic` package to apply our theme contract at runtime.
> This example uses React, but [assignInlineVars] will work with any framework or vanilla JS.
```ts compiled
// app.tsx
import { assignInlineVars } from '@vanilla-extract/dynamic';
import { container, themeVars } from './theme.css.ts';
interface ContainerProps {
brandColor: string;
fontFamily: string;
}
const Container = ({
brandColor,
fontFamily
}: ContainerProps) => (
);
const App = () => (
...
);
// theme.css.ts
import {
createThemeContract,
style
} from '@vanilla-extract/css';
export const themeVars = createThemeContract({
color: {
brand: null
},
font: {
body: null
}
});
export const container = style({
background: themeVars.color.brand,
fontFamily: themeVars.font.body
});
```
This pattern opens up a lot of interesting possibilities. Type-safe runtime theming without the need for runtime creation and injection of CSS.
[createtheme]: /documentation/api/create-theme/
[createthemecontract]: /documentation/api/create-theme/
[assigninlinevars]: /documentation/packages/dynamic/#assigninlinevars
[createvar]: /documentation/api/create-var
[tiny]: https://bundlephobia.com/package/@vanilla-extract/dynamic
---
# Vanilla Extract Documentation
# Source: https://raw.githubusercontent.com/vanilla-extract-css/vanilla-extract/master/site/docs/packages/css-utils.md
# Path: site/docs/packages/css-utils.md
---
title: CSS Utils
parent: packages
---
# CSS Utils
An optional package providing utility functions that make it easier to work with CSS in TypeScript.
```bash
npm install @vanilla-extract/css-utils
```
This package is not limited to vanilla-extract—it can be used with any CSS-in-JS library.
## calc
Streamlines the creation of CSS calc expressions.
### Simple expressions
```tsx
import { calc } from '@vanilla-extract/css-utils';
const styles = {
height: calc.multiply('var(--grid-unit)', 2)
};
```
The following functions are available.
- `calc.add`
- `calc.subtract`
- `calc.multiply`
- `calc.divide`
- `calc.negate`
### Chainable expressions
The `calc` export is also a function, providing a chainable API for complex calc expressions.
> When using expression chains it is necessary to call `toString()` to return the constructed expression as the final value.
```tsx
import { calc } from '@vanilla-extract/css-utils';
const styles = {
marginTop: calc('var(--space-large)')
.divide(2)
.negate()
.toString()
};
```
---
# Vanilla Extract Documentation
# Source: https://raw.githubusercontent.com/vanilla-extract-css/vanilla-extract/master/site/docs/packages/dynamic.md
# Path: site/docs/packages/dynamic.md
---
title: Dynamic
parent: packages
---
# Dynamic
A tiny ([< 1kB compressed](https://bundlephobia.com/package/@vanilla-extract/dynamic@2.0.2)) runtime for performing dynamic updates to scoped theme variables.
```bash
npm install @vanilla-extract/dynamic
```
## assignInlineVars
Allows variables to be assigned dynamically that have been created using vanilla-extract APIs, e.g. `createVar`, `createTheme`, etc.
As these APIs produce variable references that contain the CSS var function, e.g. `var(--brandColor__8uideo0)`, it is necessary to remove the wrapping function when setting its value.
Variables with a value of `null` or `undefined` will be omitted from the resulting inline style.
> 🧠 `null` and `undefined` values can only be passed to `assignInlineVars` if a theme contract is not provided
```tsx compiled
// app.tsx
import { assignInlineVars } from '@vanilla-extract/dynamic';
import {
container,
brandColor,
textColor
} from './styles.css.ts';
// If `tone` is `undefined`, the following inline style becomes:
// { '--brandColor__8uideo0': 'pink' }
const MyComponent = ({ tone }: { tone?: critical }) => (
);
// styles.css.ts
import { createVar, style } from '@vanilla-extract/css';
export const brandColor = createVar();
export const textColor = createVar();
export const container = style({
background: brandColor,
color: textColor
});
```
Even though this function returns an object of inline styles, it implements the `toString` method, returning a valid `style` attribute value so that it can be used in string templates.
```ts
// app.ts
import { assignInlineVars } from '@vanilla-extract/dynamic';
import { container, brandColor } from './styles.css.ts';
// The following inline style becomes:
// "--brandColor__8uideo0: pink;"
document.write(`
`);
```
### Assigning theme contracts dynamically
[Theme contracts](/documentation/theming/) can also be assigned dynamically by passing one as the first argument.
All variables must be assigned or it’s a type error.
This API makes the concept of dynamic theming much simpler.
```tsx compiled
// app.tsx
import { assignInlineVars } from '@vanilla-extract/dynamic';
import { container, themeVars } from './theme.css.ts';
interface ContainerProps {
brandColor: string;
fontFamily: string;
}
const Container = ({
brandColor,
fontFamily
}: ContainerProps) => (
);
const App = () => (
...
);
// theme.css.ts
import {
createThemeContract,
style
} from '@vanilla-extract/css';
export const themeVars = createThemeContract({
color: {
brand: null
},
font: {
body: null
}
});
export const container = style({
background: themeVars.color.brand,
fontFamily: themeVars.font.body
});
```
## setElementVars
An imperative API, allowing variables created using vanilla-extract APIs, e.g. `createVar`, `createTheme`, etc, to be assigned dynamically on a DOM element.
Variables with a value of `null` or `undefined` will not be assigned a value.
> 🧠 `null` and `undefined` values can only be passed to `setElementVars` if a theme contract is not provided
```ts compiled
// app.ts
import { setElementVars } from '@vanilla-extract/dynamic';
import { brandColor, textColor } from './styles.css.ts';
const el = document.getElementById('myElement');
setElementVars(el, {
[brandColor]: 'pink',
[textColor]: null
});
// styles.css.ts
import { createVar, style } from '@vanilla-extract/css';
export const brandColor = createVar();
export const textColor = createVar();
```
### Setting theme contracts dynamically
[Theme contracts](/documentation/theming/) can also be set dynamically by passing one as the second argument.
All variables must be assigned or it’s a type error.
```ts compiled
// app.ts
import { setElementVars } from '@vanilla-extract/dynamic';
import { themeVars } from './theme.css.ts';
const el = document.getElementById('myElement');
setElementVars(el, themeVars, {
color: { brand: 'pink' },
font: { body: 'Arial' }
});
// theme.css.ts
import { createThemeContract } from '@vanilla-extract/css';
export const themeVars = createThemeContract({
color: {
brand: null
},
font: {
body: null
}
});
```
---
# Vanilla Extract Documentation
# Source: https://raw.githubusercontent.com/vanilla-extract-css/vanilla-extract/master/site/docs/packages/recipes.md
# Path: site/docs/packages/recipes.md
---
title: Recipes
parent: packages
---
# Recipes
Create multi-variant styles with a type-safe runtime API, heavily inspired by [Stitches](https://stitches.dev).
As with the rest of vanilla-extract, all styles are generated at build time.
> 💡 Recipes is an optional package built on top of vanilla-extract using its [function serialization API.](/documentation/api/add-function-serializer) It doesn't have privileged access to vanilla-extract internals so you're also free to build alternative implementations.
## Setup
```bash
npm install @vanilla-extract/recipes
```
## recipe
Creates a multi-variant style function that can be used at runtime or statically in `.css.ts` files.
Accepts an optional set of `base` styles, `variants`, `compoundVariants` and `defaultVariants`.
```ts compiled
// button.css.ts
import { recipe } from '@vanilla-extract/recipes';
export const button = recipe({
base: {
borderRadius: 6
},
variants: {
color: {
neutral: { background: 'whitesmoke' },
brand: { background: 'blueviolet' },
accent: { background: 'slateblue' }
},
size: {
small: { padding: 12 },
medium: { padding: 16 },
large: { padding: 24 }
},
rounded: {
true: { borderRadius: 999 }
}
},
// Applied when multiple variants are set at once
compoundVariants: [
{
variants: {
color: 'neutral',
size: 'large'
},
style: {
background: 'ghostwhite'
}
}
],
defaultVariants: {
color: 'accent',
size: 'medium'
}
});
```
With this recipe configured, you can now use it in your templates.
```ts
// app.ts
import { button } from './button.css.ts';
document.write(`
Hello world
`);
```
Your recipe configuration can also make use of existing variables, classes and styles.
For example, you can pass in the result of your [`sprinkles`](/documentation/packages/sprinkles) function directly.
```ts
// button.css.ts
import { recipe } from '@vanilla-extract/recipes';
import { reset } from './reset.css.ts';
import { sprinkles } from './sprinkles.css.ts';
export const button = recipe({
base: [reset, sprinkles({ borderRadius: 'round' })],
variants: {
color: {
neutral: sprinkles({ background: 'neutral' }),
brand: sprinkles({ background: 'brand' }),
accent: sprinkles({ background: 'accent' })
},
size: {
small: sprinkles({ padding: 'small' }),
medium: sprinkles({ padding: 'medium' }),
large: sprinkles({ padding: 'large' })
}
},
defaultVariants: {
color: 'accent',
size: 'medium'
}
});
```
The recipes function also exposes an array property `variants` that includes all the variants from your recipe.
```ts
button.variants();
// -> ['color', 'size']
```
## Recipe class name selection
Recipes function exposes internal class names in `classNames` property.
The property has two predefined props: `base` and `variants`. The `base` prop includes base class name. It is always defined even if you do not have any base styles. The `variants` prop includes class names for each defined variant.
```ts
// app.css.ts
console.log(button.classNames.base);
// -> app_button__129pj250
console.log(button.classNames.variants.color.neutral);
// -> app_button_color_neutral__129pj251
console.log(button.classNames.variants.size.small);
// -> app_button_size_small__129pj254
```
## RecipeVariants
A utility to make use of the recipe’s type interface. This can be useful when typing functions or component props that need to accept recipe values as part of their interface.
```ts
// button.css.ts
import {
recipe,
RecipeVariants
} from '@vanilla-extract/recipes';
export const button = recipe({
variants: {
color: {
neutral: { background: 'whitesmoke' },
brand: { background: 'blueviolet' },
accent: { background: 'slateblue' }
},
size: {
small: { padding: 12 },
medium: { padding: 16 },
large: { padding: 24 }
}
}
});
// Get the type
export type ButtonVariants = RecipeVariants;
// the above will result in a type equivalent to:
export type ButtonVariants = {
color?: 'neutral' | 'brand' | 'accent';
size?: 'small' | 'medium' | 'large';
};
```
---
# Vanilla Extract Documentation
# Source: https://raw.githubusercontent.com/vanilla-extract-css/vanilla-extract/master/site/docs/packages/sprinkles.md
# Path: site/docs/packages/sprinkles.md
---
title: Sprinkles
parent: packages
---
# Sprinkles
A zero-runtime atomic CSS framework for vanilla-extract.
Generate a static set of custom utility classes and compose them either statically at build time, or dynamically at runtime, without the usual style generation overhead of CSS-in-JS.
Basically, it’s like building your own zero-runtime, type-safe version of [Tailwind], [Styled System], etc.
> 💡 Sprinkles is an optional package built on top of vanilla-extract using its [function serialization API.](/documentation/api/add-function-serializer) It doesn't have privileged access to vanilla-extract internals so you're also free to build alternative implementations, e.g. [Rainbow Sprinkles.](https://github.com/wayfair/rainbow-sprinkles)
## Setup
```bash
npm install @vanilla-extract/sprinkles
```
Create a `sprinkles.css.ts` file, then configure and export your `sprinkles` function.
> 💡 This is just an example! Feel free to customise properties, values and conditions to match your requirements.
```ts compiled
// sprinkles.css.ts
import {
defineProperties,
createSprinkles
} from '@vanilla-extract/sprinkles';
const space = {
none: 0,
small: '4px',
medium: '8px',
large: '16px'
// etc.
};
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'],
justifyContent: [
'stretch',
'flex-start',
'center',
'flex-end',
'space-around',
'space-between'
],
alignItems: [
'stretch',
'flex-start',
'center',
'flex-end'
],
paddingTop: space,
paddingBottom: space,
paddingLeft: space,
paddingRight: space
// etc.
},
shorthands: {
padding: [
'paddingTop',
'paddingBottom',
'paddingLeft',
'paddingRight'
],
paddingX: ['paddingLeft', 'paddingRight'],
paddingY: ['paddingTop', 'paddingBottom'],
placeItems: ['justifyContent', 'alignItems']
}
});
const colors = {
'blue-50': '#eff6ff',
'blue-100': '#dbeafe',
'blue-200': '#bfdbfe',
'gray-700': '#374151',
'gray-800': '#1f2937',
'gray-900': '#111827'
// etc.
};
const colorProperties = defineProperties({
conditions: {
lightMode: {},
darkMode: { '@media': '(prefers-color-scheme: dark)' }
},
defaultCondition: 'lightMode',
properties: {
color: colors,
background: colors
// etc.
}
});
export const sprinkles = createSprinkles(
responsiveProperties,
colorProperties
);
// It's a good idea to export the Sprinkles type too
export type Sprinkles = Parameters[0];
```
## Usage
You can use your `sprinkles` function in `.css.ts` files for zero-runtime usage.
```ts
// styles.css.ts
import { sprinkles } from './sprinkles.css.ts';
export const container = sprinkles({
display: 'flex',
paddingX: 'small',
// Conditional sprinkles:
flexDirection: {
mobile: 'column',
desktop: 'row'
},
background: {
lightMode: 'blue-50',
darkMode: 'gray-700'
}
});
```
If you want, you can even use your `sprinkles` function at runtime! 🏃♂️
```tsx
// app.ts
import { sprinkles } from './sprinkles.css.ts';
const flexDirection =
Math.random() > 0.5 ? 'column' : 'row';
document.write(`
`);
```
> 💡 Although you don’t need to use this library at runtime, it’s designed to be as small and performant as possible. The runtime is only used to look up pre-existing class names. All styles are still generated at build time!
Within `.css.ts` files, combine with any custom styles by providing an array to vanilla-extract’s [style](/documentation/api/style) function.
```ts
// styles.css.ts
import { style } from '@vanilla-extract/css';
import { sprinkles } from './sprinkles.css.ts';
export const container = style([
sprinkles({
display: 'flex',
padding: 'small'
}),
{
':hover': {
outline: '2px solid currentColor'
}
}
]);
```
Sprinkles uses this internally, which means that a class list returned by `sprinkles` can be treated as if it were a single class within vanilla-extract selectors.
```ts
// styles.css.ts
import { globalStyle } from '@vanilla-extract/css';
import { sprinkles } from './sprinkles.css.ts';
export const container = sprinkles({
padding: 'small'
});
globalStyle(`${container} *`, {
boxSizing: 'border-box'
});
```
## defineProperties
Defines a collection of utility classes with [properties](#properties), [conditions](#conditions) and [shorthands.](#shorthands)
If you need to scope different conditions to different properties (e.g. some properties support breakpoints, some support light mode and dark mode, some are unconditional), you can provide as many collections of properties to [createSprinkles](#createsprinkles) as you like.
```ts
// sprinkles.css.ts
import {
defineProperties,
createSprinkles
} from '@vanilla-extract/sprinkles';
const space = {
none: 0,
small: '4px',
medium: '8px',
large: '16px'
};
const colors = {
blue50: '#eff6ff',
blue100: '#dbeafe',
blue200: '#bfdbfe'
// etc.
};
const responsiveProperties = defineProperties({
conditions: {
mobile: {},
tablet: { '@media': 'screen and (min-width: 768px)' },
desktop: { '@media': 'screen and (min-width: 1024px)' }
},
defaultCondition: 'mobile',
properties: {
display: ['none', 'block', 'flex'],
flexDirection: ['row', 'column'],
padding: space
// etc.
}
});
const colorProperties = defineProperties({
conditions: {
lightMode: {
'@media': '(prefers-color-scheme: light)'
},
darkMode: { '@media': '(prefers-color-scheme: dark)' }
},
defaultCondition: false,
properties: {
color: colors,
background: colors
}
// etc.
});
export const sprinkles = createSprinkles(
responsiveProperties,
colorProperties
);
```
> 💡 If you want a good color palette to work with, you might want to consider importing [tailwindcss/colors](https://tailwindcss.com/docs/customizing-colors#color-palette-reference).
### properties
Define which CSS properties and values should be available.
For simple mappings (i.e. valid CSS values), values can be provided as an array.
```ts
// sprinkles.css.ts
import { defineProperties } from '@vanilla-extract/sprinkles';
const responsiveProperties = defineProperties({
properties: {
display: ['none', 'block', 'flex'],
flexDirection: ['row', 'column'],
alignItems: [
'stretch',
'flex-start',
'center',
'flex-end'
],
justifyContent: [
'stretch',
'flex-start',
'center',
'flex-end'
]
// etc.
}
});
```
For semantic mappings (e.g. space scales, color palettes), values can be provided as an object.
```ts
// sprinkles.css.ts
import { defineProperties } from '@vanilla-extract/sprinkles';
const responsiveProperties = defineProperties({
properties: {
gap: {
none: 0,
small: 4,
medium: 8,
large: 16
}
// etc.
}
});
```
You can also use [vanilla-extract themes](/documentation/theming) to configure themed values.
```ts
// sprinkles.css.ts
import { defineProperties } from '@vanilla-extract/sprinkles';
import { vars } from './vars.css.ts';
const responsiveProperties = defineProperties({
properties: {
gap: vars.space
// etc.
}
});
```
For more complicated scenarios, values can even be entire style objects. This works especially well when combined with CSS Variables.
> 💡 Styles are created in the order that they were defined in your config. Properties that are less specific should be higher in the list.
```ts
// sprinkles.css.ts
import { createVar } from '@vanilla-extract/css';
import { defineProperties } from '@vanilla-extract/sprinkles';
const alpha = createVar();
const responsiveProperties = defineProperties({
properties: {
background: {
red: {
vars: { [alpha]: '1' },
background: `rgba(255, 0, 0, ${alpha})`
}
},
backgroundOpacity: {
1: { vars: { [alpha]: '1' } },
0.1: { vars: { [alpha]: '0.1' } }
}
// etc.
}
});
```
### shorthands
Maps custom shorthand properties to multiple underlying CSS properties. This is useful for mapping values like `padding`/`paddingX`/`paddingY` to their underlying longhand values.
> 💡 Shorthands are evaluated in the order that they were defined in your configuration. Shorthands that are less specific should be higher in the list, e.g. `padding` should come before `paddingX`/`paddingY`.
```ts
// sprinkles.css.ts
import { defineProperties } from '@vanilla-extract/sprinkles';
import { vars } from './vars.css.ts';
const responsiveProperties = defineProperties({
properties: {
paddingTop: vars.space,
paddingBottom: vars.space,
paddingLeft: vars.space,
paddingRight: vars.space
},
shorthands: {
padding: [
'paddingTop',
'paddingBottom',
'paddingLeft',
'paddingRight'
],
paddingX: ['paddingLeft', 'paddingRight'],
paddingY: ['paddingTop', 'paddingBottom']
}
});
```
### conditions
Define a set of media/feature/container queries for the provided properties.
For example, properties can be scoped to media queries.
```ts
// sprinkles.css.ts
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'
// etc.
});
```
Properties can also be scoped to selectors.
```ts
// sprinkles.css.ts
import { defineProperties } from '@vanilla-extract/sprinkles';
const properties = defineProperties({
conditions: {
default: {},
hover: { selector: '&:hover' },
focus: { selector: '&:focus' }
},
defaultCondition: 'default'
// etc.
});
```
Properties can also be scoped to container queries.
> 🚧 Ensure your target browsers [support container queries]. Vanilla-extract supports the [container query syntax] but does not polyfill the feature in unsupported browsers.
```ts
// sprinkles.css.ts
import {
createContainer,
style
} from '@vanilla-extract/css';
import { defineProperties } from '@vanilla-extract/sprinkles';
const containerName = createContainer();
export const container = style({
containerName,
containerType: 'size'
});
const containerProperties = defineProperties({
conditions: {
small: {},
medium: {
'@container': `${containerName} (min-width: 768px)`
},
large: {
'@container': `${containerName} (min-width: 1024px)`
}
},
defaultCondition: 'small'
// etc.
});
```
### defaultCondition
Defines which condition(s) should be used when a non-conditional value is requested, e.g. `sprinkles({ display: 'flex' })`.
If you're using mobile-first responsive conditions, this should be your lowest breakpoint.
```ts
// sprinkles.css.ts
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'
// etc.
});
```
If your conditions are mutually exclusive (e.g. light mode and dark mode), you can provide an array of default conditions. For example, the following configuration would automatically expand `sprinkles({ background: 'white' })` to the equivalent of `sprinkles({ background: { lightMode: 'white', darkMode: 'white' }})`.
```ts
// sprinkles.css.ts
import { defineProperties } from '@vanilla-extract/sprinkles';
const responsiveProperties = defineProperties({
conditions: {
lightMode: {
'@media': '(prefers-color-scheme: light)'
},
darkMode: { '@media': '(prefers-color-scheme: dark)' }
},
defaultCondition: ['lightMode', 'darkMode']
// etc.
});
```
You can also set `defaultCondition` to `false`, which forces you to be explicit about which conditions you’re targeting.
```ts
// sprinkles.css.ts
import { defineProperties } from '@vanilla-extract/sprinkles';
const responsiveProperties = defineProperties({
conditions: {
lightMode: {
'@media': '(prefers-color-scheme: light)'
},
darkMode: { '@media': '(prefers-color-scheme: dark)' }
},
defaultCondition: false
// etc.
});
```
### responsiveArray
Providing an array of condition names enables the responsive array notation (e.g. `['column', 'row']`) by defining the order of conditions.
```ts
// sprinkles.css.ts
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',
responsiveArray: ['mobile', 'tablet', 'desktop']
// etc.
});
```
### @layer
Optionally defines a layer to assign styles to for a given set of properties.
> 🚧 Ensure your target browsers [support layers].
> Vanilla Extract supports the [layers syntax][layer] but does not polyfill the feature in unsupported browsers.
```ts
// sprinkles.css.ts
import { defineProperties } from '@vanilla-extract/sprinkles';
import { layer } from '@vanilla-extract/css';
export const sprinklesLayer = layer();
const properties = defineProperties({
'@layer': sprinklesLayer
// etc.
});
```
## createSprinkles
Creates a type-safe function for accessing your [defined properties](#defineProperties). You can provide as many collections of properties as you like.
> 🚧 Ensure properties are defined as variables before passing them into `createSprinkles`.
> Calling `defineProperties` inside a `createSprinkles` call will cause types to be inferred incorrectly, resulting in a type-unsafe sprinkles function.
```ts
// sprinkles.css.ts
import {
defineProperties,
createSprinkles
} from '@vanilla-extract/sprinkles';
const responsiveProperties = defineProperties({
/* ... */
});
const unconditionalProperties = defineProperties({
/* ... */
});
const colorProperties = defineProperties({
/* ... */
});
export const sprinkles = createSprinkles(
responsiveProperties,
unconditionalProperties,
colorProperties
);
```
The sprinkles function also exposes a static `properties` key that lets you check whether a given property can be handled by the function.
```ts
sprinkles.properties.has('paddingX');
// -> boolean
```
> 💡 This is useful when building a Box component with sprinkles available at the top level (e.g. ``) since you’ll need some way to filter sprinkle props from non-sprinkle props.
## createMapValueFn
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.
This function should be created and exported from your `sprinkles.css.ts` file using the conditions from your defined properties.
You can name the generated function whatever you like, typically based on the name of your conditions.
```ts
// sprinkles.css.ts
import {
defineProperties,
createSprinkles,
createMapValueFn
} from '@vanilla-extract/sprinkles';
const responsiveProperties = defineProperties({
/* ... */
});
export const sprinkles = createSprinkles(
responsiveProperties
);
export const mapResponsiveValue = createMapValueFn(
responsiveProperties
);
```
You can then import the generated function in your app code.
```ts
// app.ts
import { mapResponsiveValue } from './sprinkles.css.ts';
const alignToFlexAlign = {
left: 'flex-start',
center: 'center',
right: 'flex-end',
stretch: 'stretch'
} as const;
mapResponsiveValue(
'left',
(value) => alignToFlexAlign[value]
);
// -> 'flex-start'
mapResponsiveValue(
{
mobile: 'center',
desktop: 'left'
} as const,
(value) => alignToFlexAlign[value]
);
// -> { mobile: 'center', desktop: 'flex-start' }
mapResponsiveValue(
['center', null, 'left'] as const,
(value) => alignToFlexAlign[value]
);
// -> { mobile: 'center', desktop: 'flex-start' }
```
> 💡 You can generate a custom conditional value type with the [ConditionalValue](#conditionalvalue) type.
## createNormalizeValueFn
Creates a function for normalizing conditional values into a consistent object structure. Any primitive values or responsive arrays will be converted to conditional objects.
This function should be created and exported from your `sprinkles.css.ts` file using the conditions from your defined properties.
> 💡 You can name the generated function whatever you like, typically based on the name of your conditions.
```ts
// sprinkles.css.ts
import {
defineProperties,
createSprinkles,
createNormalizeValueFn
} from '@vanilla-extract/sprinkles';
const responsiveProperties = defineProperties({
/* ... */
});
export const sprinkles = createSprinkles(
responsiveProperties
);
export const normalizeResponsiveValue =
createNormalizeValueFn(responsiveProperties);
```
You can then import the generated function in your app code.
```ts
// app.ts
import { normalizeResponsiveValue } from './sprinkles.css.ts';
normalizeResponsiveValue('block');
// -> { mobile: 'block' }
normalizeResponsiveValue(['none', null, 'block']);
// -> { mobile: 'none', desktop: 'block' }
normalizeResponsiveValue({
mobile: 'none',
desktop: 'block'
});
// -> { mobile: 'none', desktop: 'block' }
```
## ConditionalValue
Creates a custom conditional value type.
> 💡 This is useful for typing high-level prop values that are [mapped to low-level sprinkles](#createmapvaluefn), e.g. supporting left/right prop values that map to flex-start/end.
This type should be created and exported from your `sprinkles.css.ts` file using the conditions from your defined properties.
> 💡 You can name the generated type whatever you like, typically based on the name of your conditions.
```ts
// sprinkles.css.ts
import {
defineProperties,
ConditionalValue
} from '@vanilla-extract/sprinkles';
const responsiveProperties = defineProperties({
/* ... */
});
export type ResponsiveValue =
ConditionalValue;
```
You can then import the generated type in your app code.
```ts
// app.ts
import { ResponsiveValue } from './sprinkles.css.ts';
type ResponsiveAlign = ResponsiveValue<
'left' | 'center' | 'right'
>;
const a: ResponsiveAlign = 'left';
const b: ResponsiveAlign = {
mobile: 'center',
desktop: 'left'
};
const c: ResponsiveAlign = ['center', null, 'left'];
```
## RequiredConditionalValue
Same as [ConditionalValue](#conditionalvalue) except the default condition is required. For example, if your default condition was `'mobile'`, then a conditional value of `{ desktop: '...' }` would be a type error.
```ts
// sprinkles.css.ts
import {
defineProperties,
RequiredConditionalValue
} from '@vanilla-extract/sprinkles';
const responsiveProperties = defineProperties({
defaultCondition: 'mobile'
// etc.
});
export type RequiredResponsiveValue<
Value extends string | number
> = RequiredConditionalValue<
typeof responsiveProperties,
Value
>;
```
You can then import the generated type in your app code.
```ts
// app.ts
import { RequiredResponsiveValue } from './sprinkles.css.ts';
type ResponsiveAlign = RequiredResponsiveValue<
'left' | 'center' | 'right'
>;
const a: ResponsiveAlign = 'left';
const b: ResponsiveAlign = {
mobile: 'center',
desktop: 'left'
};
const c: ResponsiveAlign = ['center', null, 'left'];
// Type errors:
const d: ResponsiveAlign = [null, 'center'];
const e: ResponsiveAlign = { desktop: 'center' };
```
[tailwind]: https://tailwindcss.com
[styled system]: https://github.com/styled-system/styled-system
[support container queries]: https://caniuse.com/css-container-queries
[container query syntax]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Container_Queries
[layer]: https://developer.mozilla.org/en-US/docs/Web/CSS/@layer
[support layers]: https://caniuse.com/css-cascade-layers