# 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: ` ``` ## 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 ; } ``` ```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(`

Hello world!

`); ``` --- 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(` `); ``` 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