Telerik blogs

CSS can improve performance and UX of an application. Vanilla Extract is an alternative to CSS-in-JS that allows writing zero-runtime CSS using TypeScript or JavaScript.

User experience is important—and always was, but we can agree that’s been more of a focus nowadays—because it centers on creating a positive and efficient experience for the user when they interact with a website or application. It helps to increase user satisfaction and loyalty, and can also help to improve the overall effectiveness of the website or application.

CSS plays a big role in user experience because it is used to control the visual appearance of the content. We can determine which elements we’re going to render, and how and when they are going to be rendered. Effectively using CSS makes a difference not only for customers but for developers too.

Having well-written CSS can make a difference for developers because it can help to make their code more organized, maintainable and efficient. This can save time and reduce the risk of errors, allowing developers to work more efficiently and effectively.

CSS can also improve the performance of an application by reducing the amount of code that needs to be loaded and processed. Doing this will improve the loading speed and overall responsiveness of the application. This is why CSS is so important for modern applications and why there should be a concern about how we currently write our CSS code.

The Problem with CSS-in-JS

CSS-in-JS was widely adopted by the React community a few years ago. We can say that the majority of React developers are using CSS-in-JS libraries.

So, what’s the problem with CSS-in-JS? We can name a few:

  • Increases bundle size
    By including CSS styles in JavaScript code rather than a separate CSS file, the bundle size increases. Even though it isn’t much, it can make a difference in the user experience and loading speed.

  • Increases runtime overhead
    Every time you render a single style component, a Context.Consumer will be created. This Context.Consumer will be used for managing the general style rules of your application. Now, imagine that you have 500 or 1000 different styled components inside your application. That’s a lot of added runtime.

  • Lack of portability
    As Brad Frost stated in his “What’s wrong with css-in-js?”:

Even if CSS-in-JS is a more powerful, elegant solution, it only works for JS-driven environments. So what happens if an organization has some React projects in play, but also has some Drupal, WordPress, or [anything else] projects they need to unify under the same design system? Do they have to re-platform their whole technical architecture just to get updated button styles?

Even though CSS-in-JS libraries are very good, we still need to consider their performance costs before using them.

Zero Runtime

When a web application is run without requiring any additional downloads, this is referred to as zero runtime. Users can access and use modern web applications quickly and easily without having to wait for additional resources to be loaded or installed, which is critical for modern web apps. What does it have to do with CSS? Everything.

Zero CSS runtime is a technique for applying styles to a website or app without the need for additional browser runtime processing. Using zero CSS runtime can help improve the performance of a website or app by reducing the amount of runtime processing required to apply the styles. This can result in faster loading times and a more pleasant user experience.

Vanilla Extract and Zero-Runtime CSS

Vanilla Extract is a package for writing zero-runtime CSS using TypeScript or JavaScript. It has amazing features like locally scoped class names, CSS variables, framework agnostic, high-level theme system, and many more. Let’s see the most important features of Vanilla Extract and why it can be a good alternative for writing CSS code.

Fully Type-Safe Using TypeScript

For starting, the best feature of Vanilla Extract is that the library is totally written in TypeScript. The code editor will autocomplete based on the shape of your theme object when you start writing your styles.

import { style } from '@vanilla-extract/css';

const styles = style({
  backgroundColor: 'green',
  color: 'white',
  fontSize: '1.2rem'
  fontWeight: 'bold', 
});

Theming

We can define themes easily using deeply nested token contracts. It helps us to make sure that we’re using consistent values across our all styles. This way, we will never add a variable that doesn’t exist in our CSS code.

import { style } from '@vanilla-extract/css';

const [theme, vars] = createTheme({
  color: {
    blue: '#3366FF',
    green: '#78E83C',
    red: '#FC5623',
    yellow: '#FCDD44',
  },
});

Variables

We can define variables and consume them across our styles without any abstraction.

import { style, createVar } from '@vanilla-extract/css';

const accentVar = createVar();

export const green = style({
  vars: {
    [accentVar]: 'green'
  }
});

export const red = style({
  vars: {
    [accentVar]: 'red'
  }
});

There are plenty more advantages worth mentioning. A good feature of Vanilla Extract is its easy-to-use syntax. We can get started quickly and don’t need to write a lot of code since we’re using TypeScript—which is essentially JavaScript. It is also worth mentioning that the library is framework agnostic, which allows us to reuse our code wherever we want.

Since we’ve read a little about Vanilla Extract, it’s time to take a look at some examples. We will use Vanilla Extract to write our CSS code and create a React to-do project.

Vanilla Extract in Action

First of all, we’re going to create our App component. Inside this component, we’re going to have an input and a button. We will use them to gather the tasks we want to add, and the button will add them.

After that, we will have a list of tasks which will be rendered to us with a button to delete the task. Pretty simple, the intention here is to show the powerful features of Vanilla Extract. Our whole App component should look like this:

import * as React from "react";

const App = () => {
  const [value, setValue] = React.useState<string>("");
  const [items, setItems] = React.useState<Array<string>>([]);

  const onAddItem = (item: string) => {
    setItems([...items, item]);
    setValue("");
  };

  const onRemoveItem = (index: number) => {
    setItems([...items.slice(0, index), ...items.slice(index + 1)]);
  };

  return (
    <div className="App">
      <div>
        <input
          value={value}
          type="text"
          onChange={(e) => setValue(e.target.value)}
        />
        <button onClick={() => onAddItem(value)}>Add</button>
      </div>

      <div>
        {items.map((item, index) => (
          <div key={index}>
            <h3>{item}</h3>
            <button onClick={() => onRemoveItem(index)}>x</button>
          </div>
        ))}
      </div>
    </div>
  );
};

export default App;

Now, we’re going to create a new file called theme.css.ts. Inside this file, we’re going to create our theme. Let’s define a new theme and add some colors, fonts, etc.:

import { createTheme } from "@vanilla-extract/css";

export const [theme, vars] = createTheme({
  color: {
    light_gray: "#edf2f4",
    gray: "#8d99ae",
    red: "#ef233c",
    navy: "#2b2d42"
  },
  fonts: {
    heading: "Georgia, Times, Times New Roman, serif",
    body: "system-ui"
  },
  space: {
    small: "4px",
    medium: "8px"
  }
});

Using createTheme from Vanilla Extract will make it return a class name and a name contract (which is a typed data-structure of CSS variables).

Now, we’ll make a new file called styles.css.ts and add styles to our components. The first thing we’re going to style is our text. Style will be applied to a new variable called todoText. We use the vars object that we imported from our theme to access the data from the theme that we just created, and then we can get any value we want from our theme.

export const todoText = style({
  color: vars.color.gray,
  fontFamily: vars.fonts.body,
  fontSize: 16
});

We’re going to create a new variable named todoButton for the button as well.

export const todoButton = style({
  backgroundColor: vars.color.red,
  color: vars.color.light_gray,
  fontFamily: vars.fonts.body,
  fontSize: 16
});

Our whole file should look like this:

import { style } from "@vanilla-extract/css";
import { vars } from "./theme.css";

export const todoText = style({
  color: vars.color.gray,
  fontFamily: vars.fonts.body,
  fontSize: 16
});

export const todoButton = style({
  backgroundColor: vars.color.red,
  color: vars.color.light_gray,
  fontFamily: vars.fonts.body,
  fontSize: 16
});

We’re now ready to put our styles to use. All we need to do is import our theme and styles into our component and then pass them to our elements.

import { theme } from "./theme.css";
import * as styles from "./styles.css";

We can now use our styles to access any style value we assigned to our components. We’ll apply the todoText and todoButton styles to our text and button, so it should look something like this:

<div key={index}>
  <h3 className={styles.todoText}>{item}</h3>
  <button onClick={() => onRemoveItem(index)}>x</button>
</div>

This is a simple but effective demonstration of the power of Vanilla Extract. One thing to note is that Vanilla Extract cannot be used with plain create-react-app, but it can be used with Craco. If you want to use it with CRA and are looking for assistance, this blog will show you how.

Conclusion

When you set up Vanilla Extract and begin using it, you will notice an increase in your productivity. It has a great type-safe styling API and requires no runtime.

Zero runtime is an intriguing technique that can help improve the performance of a website. However, it is not a one-size-fits-all solution and may not be appropriate for all projects. It is critical to weigh the trade-offs and determine whether zero runtime CSS is a good fit for your specific requirements.


CSS
Leonardo Maldonado
About the Author

Leonardo Maldonado

Leonardo is a full-stack developer, working with everything React-related, and loves to write about React and GraphQL to help developers. He also created the 33 JavaScript Concepts.

Related Posts

Comments

Comments are disabled in preview mode.