Vanilla Extract CSS is a revolutionary approach to writing styles in modern web development. Unlike traditional CSS, Vanilla Extract allows developers to write CSS in TypeScript or JavaScript files, which then generate static CSS files at build time. This process ensures type safety and can significantly improve maintainability of styles across large codebases.
One of the core features of Vanilla Extract is its ability to define CSS variables and scoped CSS variables within a theme object. This enables developers to create consistent and reusable styles that can be easily managed and updated.
For example, defining a primary color as a CSS variable within a theme object allows for quick adjustments that propagate throughout the entire application. Vanilla Extract provides similar benefits to CSS Modules, including locally scoped class names and CSS Variables, but also offers additional features like scoped CSS Variables.
1import { createGlobalTheme } from "@vanilla-extract/css"; 2 3const themeClass = createGlobalTheme(":root", { 4 color: { primary: "#007bff", secondary: "#6c757d" }, 5 fontSizes: { small: "12px", medium: "16px", large: "24px" }, 6});
In this snippet, we define a global theme with primary and secondary colors, as well as small, medium, and large font sizes. This theme can then be used throughout the application to maintain a consistent design system.
Vanilla CSS refers to regular CSS that is written without any pre-processors like SASS or LESS, and without any frameworks or libraries. It's the foundational layer of styling for web pages, directly interpreted by browsers. Vanilla Extract takes this concept further by allowing developers to write this "vanilla" CSS within JS/TS files, leveraging the power of JavaScript to enhance the styling process.
To start using Vanilla Extract CSS, you need to set up your project to handle the extraction process. This involves configuring your build tools to recognize .css.ts files and compile them into static CSS files. Here's a basic setup using Webpack:
1const { VanillaExtractPlugin } = require('@vanilla-extract/webpack-plugin'); 2 3module.exports = { 4 // ... other webpack config 5 plugins: [ 6 new VanillaExtractPlugin() 7 ] 8};
This plugin will handle the extraction of styles defined in your .css.ts files, ensuring that they are converted into CSS files that browsers can understand.
Creating your first style block with Vanilla Extract is straightforward. You define your styles using the style function, and export them for use in your application.
1import { style } from '@vanilla-extract/css'; 2 3export const buttonStyle = style({ 4 backgroundColor: 'blue', 5 color: 'white', 6 padding: '10px 15px', 7 borderRadius: '5px' 8});
In this example, we've created a simple button style that can be applied to any HTML element by adding the buttonStyle class name.
When you build your project, Vanilla Extract takes the styles you've written and generates static CSS files. This process is done at build time, which means there's no runtime overhead for your styles—leading to faster load times and better performance. The generated CSS output is optimized and can be cached by browsers for even more efficiency.
Scoped CSS variables are a powerful feature of Vanilla Extract, allowing you to define variables that are only accessible within a certain scope. This is particularly useful for component styles, where you want to ensure that styles do not leak outside of the component's boundary.
1import { createTheme, style } from '@vanilla-extract/css'; 2 3const [themeClass, vars] = createTheme({ 4 color: { 5 buttonBackground: 'rebeccapurple', 6 buttonText: 'white' 7 } 8}); 9 10export const buttonStyle = style({ 11 backgroundColor: vars.color.buttonBackground, 12 color: vars.color.buttonText 13});
Here, we've created a scoped theme for a button component, ensuring that the styles are encapsulated and do not affect other elements.
A theme object in Vanilla Extract is a collection of CSS variables that define the visual design of your application. It can include colors, font sizes, spacing, and more. You can have multiple themes within your application, such as a light theme and a dark theme, and switch between them dynamically.
1import { createTheme } from '@vanilla-extract/css'; 2 3export const lightTheme = createTheme({ 4 color: { 5 background: 'white', 6 text: 'black' 7 } 8}); 9 10export const darkTheme = createTheme({ 11 color: { 12 background: 'black', 13 text: 'white' 14 } 15});
This code snippet demonstrates how to define a light and dark theme using Vanilla Extract. By exporting these themes, you can apply them to your application to support multiple themes, enhancing the user experience.
Vanilla Extract CSS is designed to generate static CSS files at build time, which is a process known as zero runtime stylesheets. This means that all the styles are processed and converted into CSS during the build phase, and no additional runtime processing is required. This approach results in a faster rendering time since the browser can immediately apply the pre-compiled styles.
1// This code would be part of your build setup 2const { VanillaExtractPlugin } = require('@vanilla-extract/webpack-plugin'); 3 4module.exports = { 5 // ... other webpack config 6 plugins: [ 7 new VanillaExtractPlugin({ 8 outputCss: true // This option ensures CSS files are generated 9 }) 10 ] 11};
The above configuration for the Webpack plugin ensures that your styles written with Vanilla Extract are output as static CSS files, ready to be served with your application.
The primary benefit of zero runtime stylesheets is the performance gain. Since the styles are already compiled into static CSS files, the browser does not need to spend time parsing and applying styles at runtime. This leads to quicker load times and a smoother experience for the end-user, especially on devices with limited processing power.
When crafting component styles with Vanilla Extract, you can create both global and scoped classes. A scoped class is a CSS class that is unique to a component, preventing style leakage and ensuring encapsulation.
1import { style } from '@vanilla-extract/css'; 2 3export const containerStyle = style({ 4 display: 'flex', 5 justifyContent: 'center', 6 alignItems: 'center' 7}); 8 9export const scopedClass = style({ 10 selectors: { 11 '&:hover': { 12 backgroundColor: 'yellow' 13 } 14 } 15});
In this example, containerStyle is a global class that can be reused across different components, while scopedClass is a class with a hover state that is scoped to a specific component.
Exporting style definitions is a straightforward process with Vanilla Extract. You define your styles and use the export const syntax to make them available for use in your application.
1import { style } from '@vanilla-extract/css'; 2 3export const textStyle = style({ 4 fontSize: '16px', 5 lineHeight: '1.5', 6 color: 'navy' 7});
This textStyle can then be applied to any text element within your application to ensure consistent typography.
Managing multiple themes with Vanilla Extract is made simple through the use of theme contracts and tokens. You can define a set of shared properties and then create different themes that adhere to this contract.
1import { createThemeContract, createTheme } from '@vanilla-extract/css'; 2 3const themeContract = createThemeContract({ 4 color: { 5 background: null, 6 text: null 7 } 8}); 9 10export const lightTheme = createTheme(themeContract, { 11 color: { 12 background: 'white', 13 text: 'black' 14 } 15}); 16 17export const darkTheme = createTheme(themeContract, { 18 color: { 19 background: 'black', 20 text: 'white' 21 } 22});
This allows you to switch between lightTheme and darkTheme dynamically, providing support for multiple themes within your application.
When defining a dark theme or other theme variants, you simply create a new theme with the desired properties. This can include not only color schemes but also different font sizes, padding, and any other CSS properties that contribute to the look and feel of your application.
1import { createTheme } from '@vanilla-extract/css'; 2 3export const darkTheme = createTheme({ 4 color: { 5 background: '#333', 6 text: '#fff' 7 }, 8 padding: { 9 small: '4px', 10 medium: '8px', 11 large: '16px' 12 } 13});
By exporting darkTheme, you make it available for use throughout your application, allowing users to switch to a dark mode if they prefer.
With Vanilla Extract, you can globally target child nodes using CSS selectors. This is useful when you want to apply styles to all instances of a particular element within a component or across your entire application.
1import { globalStyle } from "@vanilla-extract/css"; 2 3globalStyle(".listItem > li", { 4 listStyleType: "none", 5 padding: "8px", 6 borderBottom: "1px solid #ccc", 7}); 8 9globalStyle(".listItem > li:last-child", { borderBottom: "none" });
In this code snippet, we target all li elements that are children of elements with the class listItem, applying a consistent style to each list item. The :last-child pseudo-class is used to remove the border from the last item in the list.
Styling lists and leveraging the nth-child selector can create visually appealing and organized lists without additional classes.
1import { style } from '@vanilla-extract/css'; 2 3export const listStyle = style({ 4 selectors: { 5 '& ul li': { 6 paddingLeft: '20px', 7 position: 'relative' 8 }, 9 '& ul li:nth-child(odd)': { 10 backgroundColor: '#f9f9f9' 11 } 12 } 13});
This example demonstrates how to style all li elements within ul elements, giving them a paddingLeft and a position. Additionally, it applies a different backgroundColor to odd-numbered list items, creating a striped effect.
Responsive design is crucial in modern web development, and Vanilla Extract CSS supports media queries to help you create responsive styles.
1import { style } from '@vanilla-extract/css'; 2 3export const responsiveStyle = style({ 4 display: 'block', 5 '@media': { 6 'screen and (min-width: 768px)': { 7 display: 'flex' 8 } 9 } 10});
In this example, the responsiveStyle class will change the display property from block to flex when the viewport width is at least 768px, demonstrating how to use min-width in media queries with Vanilla Extract.
To improve maintainability, Vanilla Extract allows the use of theme tokens and contracts. This ensures that your theme properties are consistent and that any changes to the theme are propagated throughout your application.
1import { createThemeContract } from '@vanilla-extract/css'; 2 3export const myThemeContract = createThemeContract({ 4 space: { 5 none: '0', 6 small: '4px', 7 medium: '8px', 8 large: '16px' 9 }, 10 fontSizes: { 11 small: '0.75rem', 12 medium: '1rem', 13 large: '1.5rem' 14 } 15});
By defining a theme contract, you can ensure that all components use the same spacing and font size tokens, which simplifies updates and scaling.
Organizing styles and class names is essential for scalability. Vanilla Extract CSS allows you to create styles that are easy to manage and reuse.
1import { style } from '@vanilla-extract/css'; 2 3export const baseButtonStyle = style({ 4 padding: '10px 20px', 5 border: 'none', 6 borderRadius: '4px', 7 cursor: 'pointer' 8}); 9 10export const primaryButtonStyle = style([baseButtonStyle, { 11 backgroundColor: 'blue', 12 color: 'white' 13}]); 14 15export const secondaryButtonStyle = style([baseButtonStyle, { 16 backgroundColor: 'grey', 17 color: 'black' 18}]);
In this example, we have a base button style that is extended by the primary and secondary button styles. This approach promotes reusability and makes it easier to maintain consistent styling across different button variants.
Integrating Vanilla Extract CSS with React is seamless. You can create styled components using the styles you've defined with Vanilla Extract.
1import * as React from 'react'; 2import { buttonStyle } from './styles.css'; 3 4const Button = ({ children }) => { 5 return <button className={buttonStyle}>{children}</button>; 6}; 7 8export default Button;
Here, we create a React component called Button that applies the buttonStyle class name to a button HTML element, demonstrating how to use Vanilla Extract styles in a React component.
Applying global styles to React components is also straightforward with Vanilla Extract CSS. You can define global styles that will apply to all instances of a component or HTML element.
1import { globalStyle } from "@vanilla-extract/css"; 2 3globalStyle("body", { 4 margin: 0, 5 fontFamily: 6 '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif', 7 backgroundColor: "#f7f7f7", 8 lineHeight: "1.6", 9}); 10 11globalStyle("h1, h2, h3, h4, h5, h6", { fontWeight: "normal", color: "#333" });
In this code snippet, we apply a global style to the body element, setting the margin to 0 and defining a font stack. We also ensure that all heading elements (h1 through h6) have a consistent font weight and color. This global styling approach ensures that these base styles are applied across all React components in the application.
Vanilla Extract CSS is not just a styling library; it's a paradigm shift in how we approach CSS in modern web development. By embracing type safety and design systems, Vanilla Extract ensures that styles are consistent, maintainable, and scalable. The library's ability to generate zero runtime stylesheets means that developers can enjoy the benefits of a CSS-in-JS approach without the typical performance penalties.
As the web continues to evolve, tools like Vanilla Extract will play a pivotal role in shaping the future of CSS. They offer a glimpse into a world where styles are more than just visual attributes—they are a robust and integral part of the development process.
In this blog post, we've explored the capabilities of Vanilla Extract CSS, from setting up and generating static CSS files to creating responsive designs and integrating with React components. By leveraging the power of Vanilla Extract, developers can create complex, themeable, and responsive styles with ease, all while maintaining the simplicity and familiarity of vanilla CSS.
Tired of manually designing screens, coding on weekends, and technical debt? Let DhiWise handle it for you!
You can build an e-commerce store, healthcare app, portfolio, blogging website, social media or admin panel right away. Use our library of 40+ pre-built free templates to create your first application using DhiWise.