Technical background

The choice to use JSS is becoming more acceptable for modern UI frameworks, rather than viewed as “retrogress.” However, JSS is the cutting edge solution for management styles, and some JSS solutions can be called the cutting edge of the cutting edge. Such as:

  • linaria

A zero-runtime open source JSS solution that extracts your JSS at compile time, does not have to maintain a runtime to parse the JSS and allows browsers to download CSS and JSS in parallel, with higher performance and volume advantages.

  • stylex

The main feature of Facebook’s internal closed source atomized JSS scheme is to extract the JSS into atomic CSS at compile time for maximum reuse

const styles = stylex.create({
  blue: {color: 'blue'},
  red: {color: 'red'}
});
function MyComponent(props) {
  return (
    <span className={styles('blue', 'red')}>
      I'm blue
    </span>
  )
}
Copy the code

Will be compiled to

.c0 { color: blue}
.c1 { color: red}


const styles = stylex.create({
  blue: {color: 'blue'},
  red: {color: 'red'}
});
function MyComponent(props) {
  return (
    <span className={"c0 c1"}>
      I'm blue
    </span>
  )
}
Copy the code

Inspired by Linarira and Stylex, Broken-CSS is also a zero-runtime atomized JSS solution. The difference is that the API form chosen by Broken-CSS is not react-native like. Instead, it uses template string functions, which makes it easy for broken-CSS to support animation and pseudo-class-related CSS rules, and to use them in a very traditional way.

introduce

use

First you need to install the following two libraries:

  • yarn add @broken-css/core
  • yarn add -D @broken-css/webpack-loader

@broken-css/core

What @broken-CSS /core is doing is very simple, just giving @broken-css/webpack-loader a signal to tell it to compile, and actually if we look at the source code for @broken-css/core, you’ll see it’s pretty neat

export const css = (_literal: TemplateStringsArray, ... _DOES_NOT_SUPPORT_EXPRESSIONS: never[]): string => { throw new SyntaxError('Do not call css() on runtime! ')};Copy the code

Because CSS… The expression is replaced by a string after compilation, so the function is not actually executed at runtime, so this error is not thrown.

@broken-css/webpack-loader

@broken-CSS /webpack-loader completes the core steps, replacing the code JSS with the atomized CSS class name, and importing the compiled atomized CSS into the corresponding JS file, so that WebPack takes over the process of importing CSS. To use the relevant loaders and plugins.

example

Let’s say we have two components Foo and Bar

// Foo.tsx import { css } from "@broken-css/core"; import React, { FC } from "react"; const Foo: FC = () => { return ( <div className={css` color: red; font-size: 24px; border: 1px solid black; @keyframes shake { 10%, 90% { transform: translate3d(-1px, 0, 0); } 20%, 80% { transform: translate3d(2px, 0, 0); } 30%, 50%, 70% { transform: translate3d(-4px, 0, 0); } 40%, 60% { transform: translate3d(4px, 0, 0); }} &:hover {animation: shake 0.82s cubic-bezier(.36,.07,.19,.97) both; transform: translate3d(0, 0, 0); backface-visibility: hidden; perspective: 1000px; } &::after { content: ' after'; color: brown; } `}>foo</div> ); } export default Foo; // Bar.tsx import { css } from "@broken-css/core"; import React, { FC } from "react"; const Bar: FC = () => { return ( <div className={css` color: red; font-size: 24px; border: 1px black solid; `}>bar</div> ); } export default Bar;Copy the code

When compiled, it will become

// Foo.tsx import { css } from "@broken-css/core"; import React, { FC } from "react"; const Foo: FC = () => { return <div className={"_0e91 _b38a _43fe _b04b _4b6c"}>foo</div>; }; export default Foo; ; require(".. /node_modules/.cache/broken-css-webpack-loader/broken.css"); // Bar.tsx import { css } from "@broken-css/core"; import React, { FC } from "react"; const Bar: FC = () => { return <div className={"_43fe _b04b _f617"}>bar</div>; }; export default Bar; ; require(".. /node_modules/.cache/broken-css-webpack-loader/broken.css"); /** broken.css **/ @keyframes shake { 10%, 90% { transform: translate3d(-1px, 0, 0); } 20%, 80% { transform: translate3d(2px, 0, 0); } 30%, 50%, 70% { transform: translate3d(-4px, 0, 0); } 40%, 60% { transform: translate3d(4px, 0, 0); }}._0e91:hover {animation: shake 0.82s cubic-bezier(.36,.07,.19,.97) both; transform: translate3d(0, 0, 0); backface-visibility: hidden; perspective: 1000px; } ._b38a::after { content: ' after'; color: brown; } ._43fe {color: red; } ._b04b {font-size: 24px; } ._4b6c {border: 1px solid black; } ._f617 {border: 1px black solid; }Copy the code

atomization

As the example shows, broken-CSS computs a hash value to represent the style rule based on the content of the style, and this hash value is replaced as the class name in the corresponding JS file. Because the hash value is calculated based on the content, the hash value from the same style in two files is the same. Therefore, it can be screened out in the subsequent deduplication steps to achieve the purpose of reuse. One more thing to say here is that for the style border: 1px solid black; And border: 1px black solid; Broken-css is not considered to be the same style, although the effect is the same. There are too many boundary cases to consider if you want to achieve this level of reuse. I hope there is a simple general method to solve this problem. I am still working on it.

Volume advantage

With broken-CSS, the size of your CSS will not decrease significantly at first, but as the project progresses, more and more styles will be duplicated, making it more likely that they will be reused, and the size will decrease. If the volume changes are combined into a line, the size increases in traditional CSS in a straight line. Broken-css is a curve.Pictures fromThe Atomic CSS – in – JS

Pseudo class supports

Broken-css supports pseudo-class selectors for objects such as &::after {… }, broken-CSS calculates the hash value based on the overall style rule, and then replaces & with the corresponding hash value.

// a.js
const cls1 = css`
        color: red;
`

// b.js

const cls2 = css`
        &:hover {
                color: red;
                font-size: 24px;
        }
`
Copy the code

Will be compiled into

// a.js
const cls1 = 'c1'
/*
    color: red;
*/

// b.js
const cls2 = 'c2 c3'
/*
    &:hover {
            color: red;
            font-size: 24px;
    }
*/


.c1, .c2:hover { color: red; }
.c3:hover { font-size: 24px; }  
Copy the code

For more fine-grained reuse, ~~ but for existing versions, will only compile to already implemented, ~~ there are cases of style conflicts that have been switched to older implementations

@ Rule Support

@rules support comes naturally, so you can use the animation and media query rules freely. Broken-css doesn’t do anything with them, just unpack them into the final CSS file. One thing I struggled with here was whether to isolate @keyframes, but on subsequent reflection, I found that it was not simple. For example, the scope of naming is isolated in every CSS… How do you reuse this during the call? Global scope, which means maintaining a state table and parsing the global CSS code to replace the corresponding names, is also complicated.

CSS variable

Broken-css treats a CSS variable as a normal style declaration without any special treatment, again calculating a hash value based on its contents and assigning a unique class name

const cls1 = css`
        --main-color: red;
        backgroud-color: var(--main-color);
`

const cls2 = css`
        backgroud-color: var(--main-color);
`
Copy the code

Will be compiled to

const cls1 = 'c1 c2'
const cls2 = 'c2'


.c1 { --main-color: red; }
.c2 { backgroud-color: var(--main-color); }
Copy the code

The problem

Smart Grammar Hints

I am not familiar with other ides, and this problem can be solved perfectly if you use VSCode. The BROKEN-CSS API form is compatible with the VScode-Styled – Components extension.

stylelint

Broken-css is extracted from the JSS during compilation, and the subsequent behavior is delegated to WebPack after conversion to atomic CSS, so you can choose whether to use stylelint-webpack-plugin or not. And VScode-Styled – Components supports lint checking during writing.

The byte e-commerce advertising front end team I work for has a large number of HCS. Internship, both junior and senior front-end. You can add wechat private chat (Yunfeihe), or send your resume directly to [email protected], please specify [RESUME] in the subject of the email.

Author: He Yunfei