Research
Security News
Threat Actor Exposes Playbook for Exploiting npm to Build Blockchain-Powered Botnets
A threat actor's playbook for exploiting the npm ecosystem was exposed on the dark web, detailing how to build a blockchain-powered botnet.
@vanilla-extract/css
Advanced tools
@vanilla-extract/css is a zero-runtime CSS-in-TypeScript library. It allows you to write type-safe, themeable, and modular CSS using TypeScript. The library compiles your styles to static CSS at build time, ensuring no runtime overhead.
Creating Styles
This feature allows you to create CSS styles using TypeScript. The `style` function is used to define a class with various CSS properties.
import { style } from '@vanilla-extract/css';
const buttonStyle = style({
backgroundColor: 'blue',
color: 'white',
padding: '10px 20px',
borderRadius: '5px',
':hover': {
backgroundColor: 'darkblue'
}
});
Theming
Theming allows you to create and use design tokens in your styles. The `createTheme` function generates a theme class and variables that can be used in your styles.
import { createTheme, style } from '@vanilla-extract/css';
const [themeClass, vars] = createTheme({
color: {
primary: 'blue',
secondary: 'green'
},
font: {
body: 'Arial, sans-serif'
}
});
const themedStyle = style({
backgroundColor: vars.color.primary,
color: vars.color.secondary,
fontFamily: vars.font.body
});
Composition
Composition allows you to combine multiple styles into one. The `composeStyles` function is used to merge multiple style classes into a single class.
import { style, composeStyles } from '@vanilla-extract/css';
const baseStyle = style({
padding: '10px',
borderRadius: '5px'
});
const primaryButton = style({
backgroundColor: 'blue',
color: 'white'
});
const composedStyle = composeStyles(baseStyle, primaryButton);
styled-components is a popular library for writing CSS-in-JS. It allows you to use ES6 and CSS to style your components. Unlike @vanilla-extract/css, styled-components has a runtime dependency and supports dynamic styling.
Emotion is a performant and flexible CSS-in-JS library. It provides both a styled API similar to styled-components and a low-level CSS API. Emotion also has runtime dependencies but offers more flexibility in terms of styling approaches.
Linaria is a zero-runtime CSS-in-JS library similar to @vanilla-extract/css. It allows you to write CSS in JavaScript and extracts it to static CSS files at build time. Linaria focuses on performance and type safety, much like @vanilla-extract/css.
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-in-TypeScript” but with scoped CSS Variables + heaps more.
🔥 All styles generated at build time — just like Sass, Less, etc.
✨ Minimal abstraction over standard CSS.
🦄 Works with any front-end framework — or even without one.
🌳 Locally scoped class names — just like CSS Modules.
🚀 Locally scoped CSS Variables, @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.
🏃♂️ Optional runtime version for development and testing.
🙈 Optional API for dynamic runtime theming.
🖥 Try it out for yourself in CodeSandbox.
Write your styles in .css.ts
files.
// 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, 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.
// app.ts
import { themeClass, exampleStyle } from './styles.css.ts';
document.write(`
<section class="${themeClass}">
<h1 class="${exampleStyle}">Hello world!</h1>
</section>
`);
Want to work at a higher level while maximising style re-use? Check out 🍨 Sprinkles, our official zero-runtime atomic CSS framework, built on top of vanilla-extract.
There are currently a few integrations to choose from.
npm install @vanilla-extract/css @vanilla-extract/webpack-plugin
💡 This plugin accepts an optional configuration object.
const { VanillaExtractPlugin } = require('@vanilla-extract/webpack-plugin');
module.exports = {
plugins: [new VanillaExtractPlugin()],
};
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
}
}
]
}
]
}
};
$ npm install @vanilla-extract/babel-plugin
{
"plugins": ["@vanilla-extract/babel-plugin"]
}
npm install @vanilla-extract/css @vanilla-extract/esbuild-plugin
💡 This plugin accepts an optional configuration object.
const { vanillaExtractPlugin } = require('@vanilla-extract/esbuild-plugin');
require('esbuild').build({
entryPoints: ['app.ts'],
bundle: true,
plugins: [vanillaExtractPlugin()],
outfile: 'out.js',
}).catch(() => process.exit(1))
Please note: There are currently no automatic readable class names during development. However, you can still manually provide a debug ID as the last argument to functions that generate scoped styles, e.g.
export const className = style({ ... }, 'className');
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.
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));
npm install @vanilla-extract/css @vanilla-extract/vite-plugin
💡 This plugin accepts an optional configuration object.
import { vanillaExtractPlugin } from '@vanilla-extract/vite-plugin';
// vite.config.js
export default {
plugins: [vanillaExtractPlugin()]
}
Please note: There are currently no automatic readable class names during development. However, you can still manually provide a debug ID as the last argument to functions that generate scoped styles, e.g.
export const className = style({ ... }, 'className');
npm install @vanilla-extract/css @vanilla-extract/snowpack-plugin
💡 This plugin accepts an optional configuration object.
// snowpack.config.json
{
"plugins": ["@vanilla-extract/snowpack-plugin"]
}
Please note: There are currently no automatic readable class names during development. However, you can still manually provide a debug ID as the last argument to functions that generate scoped styles, e.g.
export const className = style({ ... }, 'className');
npm install @vanilla-extract/css @vanilla-extract/babel-plugin @vanilla-extract/next-plugin
.babelrc
file in the root of your project, create one. Add the Babel plugin to your .babelrc
file, ensuring that you're also including "next/babel"
in your presets
array.{
"presets": ["next/babel"],
"plugins": ["@vanilla-extract/babel-plugin"]
}
next.config.js
file in the root of your project, create one. Add the Next.js plugin to your next.config.js
file.💡 This plugin accepts an optional configuration object.
const {
createVanillaExtractPlugin
} = require('@vanilla-extract/next-plugin');
const withVanillaExtract = createVanillaExtractPlugin();
const nextConfig = {};
module.exports = withVanillaExtract(nextConfig);
If required, this plugin can be composed with other plugins.
const {
createVanillaExtractPlugin
} = require('@vanilla-extract/next-plugin');
const withVanillaExtract = createVanillaExtractPlugin();
const withMDX = require('@next/mdx')({
extension: /\.mdx$/
});
const nextConfig = {};
module.exports = withVanillaExtract(withMDX(nextConfig));
To add to your Gatsby site, use the gatsby-plugin-vanilla-extract plugin.
$ npm install @vanilla-extract/babel-plugin
{
"plugins": ["@vanilla-extract/babel-plugin"]
}
In testing environments (like jsdom
) vanilla-extract will create and insert styles. While this is often desirable, it can be a major slowdown in your tests. If your tests don’t require styles to be available, the disableRuntimeStyles
import will disable all style creation.
// setupTests.ts
import '@vanilla-extract/css/disableRuntimeStyles';
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
Each integration will set a default value based on the configuration options passed to the bundler.
🍬 If you're a treat user, check out our migration guide.
Creates styles attached to a locally scoped class name.
import { style } from '@vanilla-extract/css';
export const className = style({
display: 'flex'
});
CSS Variables, simple pseudos, selectors and media/feature queries are all supported.
import { style } from '@vanilla-extract/css';
import { vars } from './vars.css.ts';
export const className = style({
display: 'flex',
vars: {
[vars.localVar]: 'green',
'--global-variable': 'purple'
},
':hover': {
color: 'red'
},
selectors: {
'&:nth-child(2n)': {
background: '#fafafa'
}
},
'@media': {
'screen and (min-width: 768px)': {
padding: 10
}
},
'@supports': {
'(display: grid)': {
display: 'grid'
}
}
});
Selectors can also contain references to other scoped class names.
import { style } from '@vanilla-extract/css';
export const parentClass = style({});
export const childClass = style({
selectors: {
[`${parentClass}:focus &`]: {
background: '#fafafa'
}
},
});
💡 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.
For example,
'&:hover:not(:active)'
and[`${parentClass} &`]
are considered valid, while'& a[href]'
and[`& ${childClass}`]
are not.If you want to target another scoped class then it should be defined within the style block of that class instead.
For example,
[`& ${childClass}`]
is invalid since it doesn’t target “&”, so it should instead be defined in the style block forchildClass
.If you want to globally target child nodes within the current element (e.g.
'& a[href]'
), you should useglobalStyle
instead.
Multiple styles can be composed into a single rule by providing an array of styles.
import { style } from '@vanilla-extract/css';
const base = style({ padding: 12 });
export const primary = style([
base,
{ background: 'blue' }
]);
export const secondary = style([
base,
{ background: 'aqua' }
]);
When composed styles are used in selectors, they are assigned an additional class if required so they can be uniquely identified. When selectors are processed internally, the composed classes are removed, only leaving behind the unique identifier classes. This allows you to treat them as if they were a single class within vanilla-extract selectors.
import {
style,
globalStyle,
} from '@vanilla-extract/css';
const background = style({ background: 'mintcream' });
const padding = style({ padding: 12 });
export const container = style([background, padding]);
globalStyle(`${container} *`, {
boxSizing: 'border-box'
});
Creates a collection of named style variants.
import { styleVariants } from '@vanilla-extract/css';
export const variant = styleVariants({
primary: { background: 'blue' },
secondary: { background: 'aqua' },
});
💡 This is useful for mapping component props to styles, e.g.
<button className={styles.variant[props.variant]}>
Multiple styles can be composed into a single rule by providing an array of styles.
import { styleVariants } from '@vanilla-extract/css';
const base = style({ padding: 12 });
export const variant = styleVariants({
primary: [base, { background: 'blue' }],
secondary: [base, { background: 'aqua' }],
});
You can also transform the values by providing a map function as the second argument.
import { styleVariants } from '@vanilla-extract/css';
const base = style({ padding: 12 });
const backgrounds = {
primary: 'blue',
secondary: 'aqua'
} as const;
export const variant = styleVariants(
backgrounds,
(background) => [base, { background }]
);
Creates styles attached to a global selector.
import { globalStyle } from '@vanilla-extract/css';
globalStyle('html, body', {
margin: 0
});
Global selectors can also contain references to other scoped class names.
import { style, globalStyle } from '@vanilla-extract/css';
export const parentClass = style({});
globalStyle(`${parentClass} > a`, {
color: 'pink'
});
Creates a locally scoped theme class and a theme contract which can be consumed within your styles.
Ensure this function is called within a .css.ts
context, otherwise variable names will be mismatched between files.
// theme.css.ts
import { createTheme } from '@vanilla-extract/css';
export const [themeClass, vars] = createTheme({
color: {
brand: 'blue'
},
font: {
body: 'arial'
}
});
You can create theme variants by passing a theme contract as the first argument to createTheme
.
// themes.css.ts
import { createTheme } 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'
}
});
💡 All theme variants must provide a value for every variable or it’s a type error.
Creates a theme attached to a global selector, but with locally scoped variable names.
Ensure this function is called within a .css.ts
context, otherwise variable names will be mismatched between files.
// theme.css.ts
import { createGlobalTheme } from '@vanilla-extract/css';
export const vars = createGlobalTheme(':root', {
color: {
brand: 'blue'
},
font: {
body: 'arial'
}
});
💡 All theme variants must provide a value for every variable or it’s a type error.
If you want to implement an existing theme contract, you can pass it as the second argument.
// theme.css.ts
import {
createThemeContract,
createGlobalTheme
} from '@vanilla-extract/css';
export const vars = createThemeContract({
color: {
brand: null
},
font: {
body: null
}
});
createGlobalTheme(':root', vars, {
color: {
brand: 'blue'
},
font: {
body: 'arial'
}
});
Creates a contract of locally scoped variable names for themes to implement.
Ensure this function is called within a .css.ts
context, otherwise variable names will be mismatched between files.
💡 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.
// themes.css.ts
import {
createThemeContract,
createTheme
} 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'
}
});
Creates a contract of globally scoped variable names for themes to implement.
💡 This is useful if you want to make your theme contract available to non-JavaScript environments.
// themes.css.ts
import {
createGlobalThemeContract,
createGlobalTheme
} from '@vanilla-extract/css';
export const vars = createGlobalThemeContract({
color: {
brand: 'color-brand'
},
font: {
body: 'font-body'
}
});
createGlobalTheme(':root', vars, {
color: {
brand: 'blue'
},
font: {
body: 'arial'
}
});
You can also provide a map function as the second argument which has access to the value and the object path.
For example, you can automatically prefix all variable names.
// themes.css.ts
import { createGlobalThemeContract } from '@vanilla-extract/css';
export const vars = createGlobalThemeContract({
color: {
brand: 'color-brand'
},
font: {
body: 'font-body'
}
}, (value) => `prefix-${value}`);
You can also use the map function to automatically generate names from the object path, joining keys with a hyphen.
// themes.css.ts
import { createGlobalThemeContract } from '@vanilla-extract/css';
export const vars = createGlobalThemeContract({
color: {
brand: null
},
font: {
body: null
}
}, (_value, path) => `prefix-${path.join('-')}`);
Assigns a collection of CSS Variables anywhere within a style block.
💡 This is useful for creating responsive themes since it can be used within
@media
blocks.
import { createThemeContract, style, assignVars } from '@vanilla-extract/css';
export const vars = createThemeContract({
space: {
small: null,
medium: null,
large: null
}
});
export const responsiveSpaceTheme = style({
vars: assignVars(vars.space, {
small: '4px',
medium: '8px',
large: '16px'
}),
'@media': {
'screen and (min-width: 1024px)': {
vars: assignVars(vars.space, {
small: '8px',
medium: '16px',
large: '32px'
})
}
}
});
💡 All variables passed into this function must be assigned or it’s a type error.
Creates a single CSS Variable.
import { createVar, style } from '@vanilla-extract/css';
export const colorVar = createVar();
export const exampleStyle = style({
color: colorVar
});
Scoped variables can be set via the vars
property on style objects.
import { createVar, style } from '@vanilla-extract/css';
import { colorVar } from './vars.css.ts';
export const parentStyle = style({
vars: {
[colorVar]: 'blue'
}
});
Provides fallback values when consuming variables.
import { createVar, fallbackVar, style } from '@vanilla-extract/css';
export const colorVar = createVar();
export const exampleStyle = style({
color: fallbackVar(colorVar, 'blue');
});
Multiple fallbacks are also supported.
import { createVar, fallbackVar, style } from '@vanilla-extract/css';
export const primaryColorVar = createVar();
export const secondaryColorVar = createVar();
export const exampleStyle = style({
color: fallbackVar(primaryColorVar, secondaryColorVar, 'blue');
});
Creates a custom font attached to a locally scoped font name.
import { fontFace, style } from '@vanilla-extract/css';
const myFont = fontFace({
src: 'local("Comic Sans MS")'
});
export const text = style({
fontFamily: myFont
});
Creates a globally scoped custom font.
import {
globalFontFace,
style
} from '@vanilla-extract/css';
globalFontFace('MyGlobalFont', {
src: 'local("Comic Sans MS")'
});
export const text = style({
fontFamily: 'MyGlobalFont'
});
Creates a locally scoped set of keyframes.
import { keyframes, style } from '@vanilla-extract/css';
const rotate = keyframes({
'0%': { transform: 'rotate(0deg)' },
'100%': { transform: 'rotate(360deg)' }
});
export const animated = style({
animation: `3s infinite ${rotate}`,
});
Creates a globally scoped set of keyframes.
import { globalKeyframes, style } from '@vanilla-extract/css';
globalKeyframes('rotate', {
'0%': { transform: 'rotate(0deg)' },
'100%': { transform: 'rotate(360deg)' }
});
export const animated = style({
animation: `3s infinite rotate`,
});
Create multi-variant styles with a type-safe runtime API, heavily inspired by Stitches.
As with the rest of vanilla-extract, all styles are generated at build time.
$ npm install @vanilla-extract/recipes
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
.
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.
import { button } from './button.css.ts';
document.write(`
<button class="${button({
color: 'accent',
size: 'large',
rounded: true
})}">
Hello world
</button>
`);
Your recipe configuration can also make use of existing variables, classes and styles.
For example, you can use your atoms
function from Sprinkles.
import { recipe } from '@vanilla-extract/recipes';
import { reset } from './reset.css.ts';
import { atoms } from './sprinkles.css.ts';
export const button = recipe({
base: [reset, atoms({ borderRadius: 'round' })],
variants: {
color: {
neutral: atoms({ background: 'neutral' }),
brand: atoms({ background: 'brand' }),
accent: atoms({ background: 'accent' })
},
size: {
small: atoms({ padding: 'small' }),
medium: atoms({ padding: 'medium' }),
large: atoms({ padding: 'large' })
}
},
defaultVariants: {
color: 'accent',
size: 'medium'
}
});
Dynamically update theme variables at runtime.
npm install @vanilla-extract/dynamic
Assigns CSS Variables as inline styles.
// app.tsx
import { assignInlineVars } from '@vanilla-extract/dynamic';
import { vars } from './vars.css.ts';
const MyComponent = () => (
<section
style={assignInlineVars({
[vars.colors.brand]: 'pink',
[vars.colors.accent]: 'green'
})}
>
...
</section>
);
You can also assign collections of variables by passing a theme contract as the first argument. All variables must be assigned or it’s a type error.
// app.tsx
import { assignInlineVars } from '@vanilla-extract/dynamic';
import { vars } from './vars.css.ts';
const MyComponent = () => (
<section
style={assignInlineVars(vars.colors, {
brand: 'pink',
accent: 'green'
})}
>
...
</section>
);
Even though this function returns an object of inline styles, its toString
method returns a valid style
attribute value so that it can be used in string templates.
// app.ts
import { assignInlineVars } from '@vanilla-extract/dynamic';
import { vars } from './vars.css.ts';
document.write(`
<section style="${assignInlineVars({
[vars.colors.brand]: 'pink',
[vars.colors.accent]: 'green'
})}">
...
</section>
`);
Sets CSS Variables on a DOM element.
// app.ts
import { setElementVars } from '@vanilla-extract/dynamic';
import { vars } from './styles.css.ts';
const el = document.getElementById('myElement');
setElementVars(el, {
[vars.colors.brand]: 'pink',
[vars.colors.accent]: 'green'
});
You can also set collections of variables by passing a theme contract as the second argument. All variables must be set or it’s a type error.
// app.ts
import { setElementVars } from '@vanilla-extract/dynamic';
import { vars } from './styles.css.ts';
const el = document.getElementById('myElement');
setElementVars(el, vars.colors, {
brand: 'pink',
accent: 'green'
});
We also provide a standalone package of optional utility functions to make it easier to work with CSS in TypeScript.
💡 This package can be used with any CSS-in-JS library.
npm install @vanilla-extract/css-utils
Streamlines the creation of CSS calc expressions.
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
The calc
export is also a function, providing a chainable API for complex calc expressions.
import { calc } from '@vanilla-extract/css-utils';
const styles = {
marginTop: calc('var(--space-large)')
.divide(2)
.negate()
.toString()
};
MIT.
FAQs
Zero-runtime Stylesheets-in-TypeScript
The npm package @vanilla-extract/css receives a total of 754,984 weekly downloads. As such, @vanilla-extract/css popularity was classified as popular.
We found that @vanilla-extract/css demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
A threat actor's playbook for exploiting the npm ecosystem was exposed on the dark web, detailing how to build a blockchain-powered botnet.
Security News
NVD’s backlog surpasses 20,000 CVEs as analysis slows and NIST announces new system updates to address ongoing delays.
Security News
Research
A malicious npm package disguised as a WhatsApp client is exploiting authentication flows with a remote kill switch to exfiltrate data and destroy files.