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