preface

The concept of atomized CSS is familiar to many front-end students, and its main driver is tailwind CSS. According to the satisfaction survey in the industry, tailwind CSS has been ranked at the top in recent years. The characteristics of its atomized CSS are indeed very useful, but in China, it is a bit of “big talk but little rain”. Perhaps because of the rough front-end development style in China, developers write out “disposable” CSS styles, it is difficult to precipitate a set of atomized styles. The author of this blog focuses on the atomization of CSS for some preliminary attempts to analyze, of course, many of the use of the framework or recommend you go directly to see the corresponding framework of the official website, after all, the official website is more comprehensive and keep updated status.

Front-end style development pain points

The well-known front end development triad: HTML CSS JS, but in the author engaged in front-end development for nearly five years, since most FEer is more concerned with JS direction, pay attention to vue react Angular three frameworks, for the style of front-end developers pay little attention to CSS, early direct use of CSS, In the future, preprocessing frameworks such as less will be used, which brings a lot of improvement, but there are still a lot of pain points.

With the author’s experience summed up some front-end development of some pain points or some common phenomena

  1. Directly copy the style of the design without too much thinking, this is more common in some novice front-end, to ensure the restore degree of the design draft as the first goal, coupled with the style of the unfamiliar, so directly choose to copy the style of the design draft to solve the problem
  2. No repair and maintenance of variable to the style statement, general design can have a set of complete design specification, or directly with the UI library some common color specification size variables, in the early stages of the project, no maintenance for these variables, will appear in the style of direct use value rather than variables, make it difficult to follow-up.
  3. It is difficult to conduct code review for style, which is different from code review for JS. Style can only be judged by page to determine whether it meets the expectation, so it is difficult to do bayonet in code to ensure code quality.
  4. Lack of style-related analysis ability, different from various analysis plug-ins of JS, style-related analysis tools or plug-ins are still relatively lacking, making front-end development in the prevention of deterioration when unable to start.

why utility css

Through some of the above analysis, we can see a lot of pain points, for example, in the case of a research and development, we choose a new transaction to solve is nothing more than two things, one is the development experience of ascension (that is, let’s writing the code in a better), one is to improve performance (speed of ascension and package volume control)

Choosing atomized CSS addresses the following pain points:

  1. To reduce the size of the packaged CSS, atomized CSS can be partitioned with minimal granularity and packaged into the final CSS. For example, a common Flex layout, where classes may be declared multiple times and packaged, atomized CSS can be packaged only once. And there is good tree shaking, styles that are not used are not packaged.
  2. Reduce the declaration of various semantic class names, compared to the previous need to define a variety of semantic class names to ensure readability, atomized CSS itself has a set of specifications, no longer need to think of a semantic class name.
  3. Complete design constraints, rather than scattered across.CSS or.less files, are more consistent with color selection, line spacing, typography, shading, etc.
  4. The ability to compare and handle states and pseudo-classes such as focus without having to declare them through styles
  5. Night mode support

Of course there are some inescapable aspects to using Utility CSS:

  1. Familiar with the utility CSS pre-declared good class name, basically agreed class name is readable are relatively strong, some subtle differences need to see themselves familiar with, with a variety of development plug-ins can improve the efficiency of development.
  2. The problem of style coverage, front-end development will inevitably appear some need to cover component library style, these author can only be covered by less these.
  3. Through devTools debugging style problems, the author does feel that the debugging of atomized CSS is still a certain inconvenience before the debugging of some style problems, into a number of class names, so we should pay more attention to the use of which “atomic class”.

why windi not tailwind

Some students may ask why windi library is chosen as the primary exploration of this project instead of Tailwind. In fact, the author used Tailwind 2.0 in the previous project, and this version has many functions that cannot meet the actual development needs. These functions have been perfectly solved in tailwind3.0 and windi. Moreover, the JIT mode of tailwind3.0 also refers to the implementation of windi. In addition, windi provides analysis tools, which is the author’s favorite, so I directly use windi in the new project. Tailwind3.0 will be followed up to try the author.

For some comparisons between Tailwind and Windi, take a look at the result of this comparison on Twitter. The latter is Windi.

Create a Windi Demo project

I’m going to use modernJS to create my application. I’m going to use modernJS to switch to the apps directory and create my project. Windi-demo is the name of the project

cd apps
pnpx @modern-js/create windi-demo
Copy the code

Then we just follow the prompts to select and enter. This is how I chose

Take a look at the current monorepo directory

Since we are monorepo project, we will delete apps/windi-demo/node_modules, configure monorepo and then re-install our new project in rush.json

"projects": [
    ......
    {
      "packageName": "windi-demo",
      "projectFolder": "apps/windi-demo"
    }
]
Copy the code

At this point we do a rush update to update the dependencies, and when the command runs we can run our project

cd apps/windi-demo
pnpm run dev
Copy the code

There is a warning about tsconfig configuration in the console, it should be missing modernJS tsconfig package, here you can add it

rush add -p @modern-js/tsconfig --dev
Copy the code

When we see this screen, our project initialization is complete

Install the Windi plug-in

Add the corresponding plug-in according to the situation of the development tool you use. The author uses VScode, here we can find the corresponding plug-in to install, mainly because there are more code tips, which will improve our local development a lot. Vscode can search the installation of WindiCSS IntelliSense

Once the installation is complete, you can try it out. When you type in className, you will get a code prompt

Access windi

Windi access needs to add the corresponding Webpack plug-in, here we first install the Webpack plug-in

rush add -p windicss-webpack-plugin --dev
Copy the code

Take a look after the installation is successfulpackage.json We will need to import this plugin once it is installed, since modern.js configuration requires a separate config file, so we will create this file in the project root directory

touch modern.config.js
Copy the code

Use this to configure the plugin. The main node should be older than 14.17.0 or you will have problems running it

import { defineConfig } from "@modern-js/app-tools"; import WindiCSSWebpackPlugin from "windicss-webpack-plugin"; export default defineConfig({ tools: { webpack: (_config, { chain }) => { chain .plugin("windiCSSWebpackPlugin") .use(WindiCSSWebpackPlugin, [ { virtualModulePath: "src", }, ]) .end(); ,}}});Copy the code

We introduce this in the app.tsx entry, which corresponds to our WebPack configuration item above

import "./virtual:windi.css";
Copy the code

So once we’re done with that, let’s see what it looks like. Let’s write a page with an atomic class

Export default function Demo() {return (<div className="w-full m-10"> <div className="border border-solid p-3"> </div> </div>); }Copy the code

Look at the effect

At this stage, our access to Windi is also completed, and then we can develop as usual

Configuration windi

Like taiwind, windi also offer configuration files, and windi configuration files is to support the windi. Config. (j | t) s and tailwind. (j) | t s, it is very convenient we switch from tailwind to windi, even don’t have to modify the name of the configuration

Take a look at the official documentation

Here we create a windi.config.ts file to use as our configuration file

touch windi.config.ts
Copy the code

Our Windi-related configuration can be added in the windi.config.ts file, for example

import { defineConfig } from 'windicss/helpers';

export default defineConfig({
  extract: {
    // A common use case is scanning files from the root directory
    include: ['**/*.{vue,html,jsx,tsx}'],
    // if you are excluding files, make sure you always include node_modules and .git
    exclude: ['node_modules', '.git', 'dist'],
  },
})

Copy the code

Arbitrary value class

In tailwind 2.0, we could only use the atomic classes specified by tailwind, but we couldn’t use the atomic classes if they didn’t match the preset atomic classes. The most common ones were the width and height font sizes, such as a 13px font. There was no preset atom class in tailwind, so we had to write the style manually. There were also some width styles, and the preset atom class could not be displayed at all px widths. In Tailwind 3.0 and Windi, this was resolved.

Let’s take a look at the windi plugin to give us a hint that we can already support a variety of arbitrary values and calculated properties

Let’s try this atomic class with arbitrary values, again from the Demo above, and let’s tweak the w-full

Export default function Demo() {return (<div className=" m-10w -[166px]"> <div className="border border-solid p-3"> </div> </div>); }Copy the code

To see the effect, at this time our arbitrary value class is in effect, when checking the style can see the class name and the corresponding style

The theme access

Theme configuration allows us to quickly expand atomic classes, such as color, size, screen and so on. The author takes his own design as an example. Most designers have their own color palette when designing, and the definition of this color palette is usually readable, such as theme color, brand color and so on. Tailwind and Windi’s built-in palette doesn’t always fit 100 percent of the time, so the front end has to expand these colors during development, which is where the theme configuration comes in.

Let’s take color as an example, configure some theme colors, winDI will automatically recognize and supplement such as BG-xx, color-xx equivalent, the configuration is as follows:

import { defineConfig } from 'windicss/helpers' export default defineConfig({ extract: { // A common use case is scanning files from the root directory include: ['**/*.{vue,html,jsx,tsx}'], // if you are excluding files, make sure you always include node_modules and .git exclude: [' node_modules', 'git', 'dist],}, theme: {the extend: {colors: {brand: {/ * * brand color/main color/B1-1 - E8F4FF - light, white floating * / 1: '# E8F4FF, / * * brand color/color/B1-3-94 c2ff - disable * / 3:' # 94 c2ff, / * * brand color/main color/B1-5-4086 ff - suspended * / 5: '# 4086 ff, / * * brand color/main color/B1-6-1664 ff - regular * / :' # 1664 ff, / * * brand color/main color/Brand1-7-0 e49d2 - click * / 7: '# 0 e49d2'}}}}})Copy the code

After replenishment, the plug-in will also have the corresponding prompt, we re-run the project to make it effective (the author used to need to re-run the project).

pnpm run dev
Copy the code

Let’s try out these new color values in the demo component above

Export default function Demo() {return (<div className=" m-10w -[166px]"> <div className="border border-solid p-3"> </div> <p className=" BG-brand-1 "> </p> <p className=" BG-brand-3 "> </p> <p className=" BG-brand-5 "> </p> </div> ); }Copy the code

Take a look at the effect on the page

We reviewed the style with DevTools and saw that the corresponding class could see the style correctly, which would satisfy our need to extend the atomic class

Shortcut to deal with

Shortcut is a very useful capability that Windi provides. It allows us to greatly reduce the writing of some atomic classes and improve reuse. For the simplest example, like flex layout, in the absence of shortcut, Flex kitems-Center context-center is used every time you use flex layout center. This allows you to write multiple times within each element, which is not reusable. Although you can solve some of this problem by writing the React component, there are still plenty of areas where you need multiple Flex layouts. I have talked about this situation before, how to make a choice between reuse and atomization of class names. Later I saw windi’s shortcut, which can basically solve the original pain point.

Using the flex layout above as an example, we put multiple atomic classes together to form a new shortcut class name and then use windi.config.ts in the page

import { defineConfig } from 'windicss/helpers'

export default defineConfig({
  extract: {
    // A common use case is scanning files from the root directory
    include: ['**/*.{vue,html,jsx,tsx}'],
    // if you are excluding files, make sure you always include node_modules and .git
    exclude: ['node_modules', '.git', 'dist'],
  },
  shortcuts: {
    'flex-center': 'flex items-center justify-center'
  },
})
Copy the code

Once set up we use the Flex-Center in the page

<div className="flex-center w-full">
    <span>shortcut content</span>
</div>
Copy the code

Take a look at the effect and review the styleFrom the screenshot of the review element above, our shortcut is in effect, as you can see with the class name Flex-Center.

After all, the purpose of atomized CSS is to slice styles to the smallest granularity and then reduce the size. Shortcut configuration is convenient for our code writing, but it has an impact on package size, so we should be careful about the trade-off. I still recommend using atomized CSS directly if you can use shortcut CSS, and communicating this trade-off with other team members.

Template string dynamic atomic class

There may be some students who have encountered similar situations as this one, where different styles need to be displayed in different situations, a little different if it’s an arbitrary value class, where this one encounters the width of a sidebar, one width to expand, another width to close, and so on

Let’s try this out in the original demo page:

const HEADER_WIDTH = 300; Export default function Demo() {return (<div className=" m-10w -[166px]"> <div className="border border-solid p-3"> </div> <p className=" BG-brand-1 "> </p> <p className=" BG-brand-3 "> </p> <p className=" BG-brand-5 "> </p> <div className="flex-center w-full"> <span>shortcut content</span> </div> <div ClassName ={' w-[calc(100vw-${HEADER_WIDTH}px)] border border-solid h-8 '} > Template string content </div> </div>; }Copy the code

As a walkthrough, we’re actually using the template string to get the height, and passing it in as a parameter to the evaluated property, which is kind of dynamic, so let’s look at the page at this point

After reviewing the elements, we found that there was no style left corresponding to the class name of the template string, which did not meet our expectations. This dynamic style requires us to inform Windi in advance which class names are in line with expectations, so that it will take effect. This requires us to add configuration to Windi

windi.config.ts

import { defineConfig } from 'windicss/helpers'

export default defineConfig({
  extract: {
    // A common use case is scanning files from the root directory
    include: ['**/*.{vue,html,jsx,tsx}'],
    // if you are excluding files, make sure you always include node_modules and .git
    exclude: ['node_modules', '.git', 'dist'],
  },
  safelist: 'w-[calc(100vw-300px)]'
})
Copy the code

Let’s look at the effect again

This is where the pattern meets our expectations

Analysis ability

Analysis ability is a characteristic ability provided by WinDI, which enables us to understand the use of atomic classes. Here, the author uses his previous project to take a look

Run in the project directory

npx windicss-analysis
Copy the code

Open the page and have a look at the report according to the prompts. The analysis can provide us with some ideas for optimization. We will not go into details here for the moment, but interested students can have a look at their own project analysis report