CSS in JS

CSS in JS is a collection of ideas for solving CSS problems, not a designated library. As you can see from the literal meaning of CSS in JS, it is to write CSS styles in JavaScript files, rather than separate files such as.css and.less. Putting CSS in JS makes it easier to use JS variables, modularity, tree-shaking. It also solves some problems in CSS, such as making it easier to resolve state-based styles, making it easier to trace dependencies, and generating unique selectors to lock scopes. Although CSS in JS is not a very new technology, its popularity in China is not high. CSS in JS is popular in the React community because Vue and Angular have their own stylesheets and React doesn’t dictate how users style components.

There are many third-party libraries that implement CSS in JS so far: click here. Like JSS, Styled – Components, etc. We won’t go into details here (links are below), but the focus of this article is JS in CSS😀.

What is JS in CSS

You can use JavaScript scripts in your CSS, as shown below. You can write the functionality of the Paint API in CSS. Also accessible: CTX, GEOM. We can even write our own CSS custom properties and so on. The implementation of these functions is based on CSS Houdini.

.el{-color: cyan;
  --multiplier: 0.24;
  --pad: 30;
  --slant: 20;
  --background-canvas: (ctx, geom) => {
    let multiplier = var(--multiplier);
    let c = `var(--color) `; let pad =var(--pad);
    let slant = var(--slant);

    ctx.moveTo(0.0);
    ctx.lineTo(pad + (geom.width - slant - pad) * multiplier, 0);
    ctx.lineTo(pad + (geom.width - slant - pad) * multiplier + slant, geom.height);
    ctx.lineTo(0, geom.height);
    ctx.fillStyle = c;
    ctx.fill(a); };background: paint(background-canvas);
  transition: --multiplier .4s;
}
.el:hover {
  --multiplier: 1;
}
Copy the code

Click here for the above online demo

What problem does Houdini solve

Compare the standard making process of CSS and JS

In today’s Web development, JavaScript is almost the majority of project code. We can use ES 2020, ES2021, or even the new features in the proposal (such as decorators) in our project development, and even if the browser doesn’t support it, we can write Polyfill or translate it using tools like Babel, allowing us to apply the latest features to production (as shown below).

CSS is different. In addition to the time required to develop CSS standards and specifications, the versions of different browsers and the actual progress are different for a long time (as shown in the following figure). At most, PostCSS, Sass and other tools are used to help us translate the CSS that browsers can accept. Developers can control the DOM and CSSOM through JS to affect page changes, but have little control over Layout, Paint, and Composite. Houdini was born to solve these problems, and to take the magic of CSS out of the browser.

CSS Polyfill

As we mentioned above, we can write Polyfill to introduce new features into production in a very short amount of time. At this point, the first thought that came to mind was CSS Polyfill. As long as CSS Polyfill is strong enough, CSS may develop at the same speed as JavaScript. Sadly, it is very difficult to write CSS Polyfill. And most of the time you can’t do it without breaking performance. This is because JavaScript is a dynamic scripting language. It’s extremely extensible, and because of that, it’s very easy to use JavaScript to do JavaScript polyfills. But CSS is not dynamic, and in some cases it is possible to convert one form of CSS to another at compile time (e.g., PostCSS). If your Polyfill depends on the DOM structure or the layout, positioning, etc., of an element, then our Polyfill will not be compile-time and will need to be run in the browser. Unfortunately, implementing this solution in a browser is not easy.

As shown in the image above, from getting the HTML from the browser to rendering it on the screen, we can see that only the parts with colors (pink, blue) are controlled by JavaScript. First of all, we cannot control the browser parses HTML and CSS and turning it into the DOM and CSSOM process, as well as the Cascade, Layout, Paint, Composite we are powerless. The only thing we have complete control over is the DOM, and the CSSOM part of it.

The CSS Houdini draft states that this level of exposure is uncertain, incompatible, and lacks support for critical features. For example, CSSOM in the browser doesn’t tell us how it handles cross-domain stylesheets, and it doesn’t handle CSS statements that the browser can’t parse. That is, if we were to use CSS polyfill to make the browser support properties it doesn’t already support, We can’t do that in CSSOM, we can just walk through the DOM, find the

Even so, some people might say, “There’s no other way to do this, and it won’t have a significant impact on site performance.” So this is true for some sites. But what if our Polyfill is needed on an interactive page? For example, scroll, resize, Mousemove, keyup, etc., these events will be triggered at any time, which means that the page will be re-rendered at any time, interaction will not be as smooth as the original, and even lead to page crash, which is extremely bad for the user experience.

In summary, if we want the browser to parse a style it doesn’t know (older browsers use grid layouts) and we can’t intervene in the rendering process, we can only manually update the DOM, which brings a lot of problems, and Houdini is designed to solve them.

Houdini API

Houdini is a set of low-level apis that expose the various parts of the CSS engine. The new apis for each section are shown below (in gray), allowing developers to extend CSS by incorporating the styling and layout processes of the browser rendering engine. Houdini was designed by a team of engineers from Mozilla, Apple, Opera, Microsoft, HP, Intel and Google. They give developers direct access to the CSS object Model (CSSOM) and allow developers to write code that the browser can parse into CSS to create new CSS features without waiting for them to be implemented natively in the browser.

Properties & Values API

Although there are already CSS variables that allow developers to control property values, there is no way to constrain types or define them more rigorously. With the new CSS Houdini API, we can extend CSS variables. We can define CSS variable types, initial values, and inheritance. It makes CSS variables more powerful and flexible.

Status of CSS variables:

.dom {
  --my-color: green;
  --my-color: url('not-a-color'); // It does not know the current variable typecolor: var(--my-color);
}

Copy the code

Houdini provides two ways to register custom attributes, one in JS and the other in CSS.

CSS.registerProperty({
  name: '--my-prop'.// String Custom attribute name
  syntax: '<color>'.// How does String parse the current attribute, the attribute type, by default *
  inherits: false.// Boolean If true, child nodes will inherit
  initialValue: '#c0ffee'.// String The initial value of the attribute point
});
Copy the code

We can also register in CSS to achieve the above effect

@property --my-prop {
  syntax: '<color>';
  inherits: false;
  initial-value: #c0ffee;
}
Copy the code

The most exciting feature in the API is adding animations to custom properties, like this: Transition: — Multiplier 0.4s; This is a function that we used earlier in the demo to show you what JS in CSS is. We can also use the + syntax properties support one or more types, also can use | to split. More syntax attribute values:

Attribute values describe
<length> The length of the value
<number> digital
<percentage> The percentage
<length-percentage> Length or percentage,calc will be the length and percentage of the expression
<color> color
<image> image
<url> The url
<integer> The integer
<angle> The Angle
<time> time
<resolution> The resolution of the
<transform-list> The transition function
<custom-ident> ident

Worklets

Worklets are extensions to a rendering engine that are similar in concept to Web Workers, but with a few important differences:

  1. Designed in parallel, eachWorkletsThere must always be two or more instances, any one of which can be run when called
  2. Small scope, limiting access to globally scoped apis (except for Worklet’s functions)
  3. The rendering engine will call them when needed, instead of us calling them manually

Worklet is a JavaScript module that is added by calling the Worklet’s addModule method (which is a Promise). RegisterLayout, registerPaint, registerAnimator we all need to put in the Worklet

// Load a single
await demoWorklet.addModule('path/to/script.js');

// Load multiple worklets at once
Promise.all([
  demoWorklet1.addModule('script1.js'),
  demoWorklet2.addModule('script2.js'),
]).then(results= > {});

registerDemoWorklet('name'.class {

  // Each Worklet can define different functions to use
  // They will be called by the rendering engine as needed
  process(arg) {
    return !arg;
  }
});

Copy the code

The life cycle of Worklets

  1. The life cycle of the Worklet begins within the rendering engine
  2. For JavaScript, the rendering engine starts the JavaScript main thread
  3. He will then start multiple Worklet processes and they will be running. These processes are ideally threads independent of the main thread, so they don’t block the main thread (but they don’t need to)
  4. Then load our browser’s JavaScript in the main thread
  5. The JavaScript callworklet.addModuleAnd load a worklet asynchronously
  6. Once loaded, load the worklet into two or more available Worklet processes
  7. When needed, the rendering engine executes the Worklet by calling the appropriate handler function from the loaded Worklet. This call can be made to any parallel Worklet instance.

Typed OM

Typed OM is an extension to the existing CSSOM and implements features related to the Parsing API and Properties & Values API. It converts CSS values into JavaScript objects of meaningful type, rather than strings as they are today. If we try to convert a string value to a meaningful type and return it, there may be a significant performance overhead, so this API allows us to use CSS values more efficiently.

The new base class CSSStyleValue has been added, which has a number of subclasses that can more accurately describe the type of CSS values:

A subclass describe
CSSKeywordValue CSS keywords and other identifiers (such as Inherit or Grid)
CSSPositionValue Position information (x, Y)
CSSImageValue Object that represents the value property of the image
CSSUnitValue Represents a single value with a single unit (for example, 50px), or a single value or percentage with no units
CSSMathValue More complex values, such as calc, min and Max. This includes subclassesCSSMathSum.CSSMathProduct.CSSMathMin.CSSMathMax.CSSMathNegate 和 CSSMathInvert
CSSTransformValue byCSS transformsConsisting of aCSSTransformComponentList, which includesCSSTranslate.CSSRotate.CSSScale.CSSSkew.CSSSkewX.CSSSkewY.CSSPerspective 和 CSSMatrixComponent

There are two main ways to use Typed OM:

  1. throughattributeStyleMapSets and gets typed interline styles
  2. throughcomputedStyleMapGets the element’s completeTyped OMstyle

Set and get using attributeStyleMap

myElement.attributeStyleMap.set('font-size', CSS.em(2));
myElement.attributeStyleMap.get('font-size'); // CSSUnitValue { value: 2, unit: 'em' }

myElement.attributeStyleMap.set('opacity', CSS.number(. 5));
myElement.attributeStyleMap.get('opacity'); // CSSUnitValue {value: 0.5, unit: 'number'};
Copy the code

The online demo

Using computedStyleMap

.foo {
  transform: translateX(1em) rotate(50deg) skewX(10deg);
  vertical-align: baseline;
  width: calc(100% - 3em);
}
Copy the code
const cs = document.querySelector('.foo').computedStyleMap();

cs.get('vertical-align');
// CSSKeywordValue {
// value: 'baseline',
// }

cs.get('width');
// CSSMathSum {
// operator: 'sum',
// length: 2,
// values: CSSNumericArray {
// 0: CSSUnitValue { value: -90, unit: 'px' },
// 1: CSSUnitValue { value: 100, unit: 'percent' },
/ /},
// }

cs.get('transform');
// CSSTransformValue {
// is2d: true,
// length: 3,
// 0: CSSTranslate {
// is2d: true,
// x: CSSUnitValue { value: 20, unit: 'px' },
// y: CSSUnitValue { value: 0, unit: 'px' },
// z: CSSUnitValue { value: 0, unit: 'px' },
/ /},
// 1: CSSRotate {... },
// 2: CSSSkewX {... },
// }
Copy the code

The online demo

Layout API

Developers can implement their own layout algorithms through this API, and we can use our own custom layouts (like display:flex, display:table) just like native CSS. In the navigation Layout Library, you can see how developers would like to implement complex layouts, some of which would not be possible with CSS alone. While these layouts can be refreshing and impressive, they tend to have poor page performance, especially on low-end devices.

The CSS Layout API exposes a registerLayout method to the developer, receives a Layout name as a property value for later use in the CSS, and a JavaScript class that contains the Layout logic.

my-div {
  display: layout(my-layout);
}
Copy the code
// layout-worklet.js
registerLayout('my-layout'.class {
  static get inputProperties() { return ['--foo']; }
  
  static get childrenInputProperties() { return ['--bar']; }
  
  async intrinsicSizes(children, edges, styleMap) {}

  async layout(children, edges, constraints, styleMap){}});Copy the code
await CSS.layoutWorklet.addModule('layout-worklet.js');
Copy the code

Online Demo is not supported by most browsers

Painting API

We can use it in CSS background-image, we can use the Canvas 2D context to control the image based on the size of the element, and we can also use custom properties.

await CSS.paintWorklet.addModule('paint-worklet.js');
Copy the code
registerPaint('sample-paint'.class {
  static get inputProperties() { return ['--foo']; }

  static get inputArguments() { return ['<color>']; }

  static get contextOptions() { return {alpha: true}; }

  paint(ctx, size, props, args){}});Copy the code

The online demo

Animation API

This API allows us to control keyframe animation based on user input in a non-blocking manner. It is also possible to change the properties of a DOM element without causing the rendering engine to recalculate layout or style properties, such as transform, opacity, or scroll offset. The Animation API is used in a slightly different way from the Paint API and Layout API. We also need to register the Worklet with a New WorkletAnimation.

// animation-worklet.js
registerAnimator('sample-animator'.class {
  constructor(options){}animate(currentTime, effect){ effect.localTime = currentTime; }});Copy the code
await CSS.animationWorklet.addModule('animation-worklet.js');

// The element to animate
const elem = document.querySelector('#my-elem');
const scrollSource = document.scrollingElement;
const timeRange = 1000;
const scrollTimeline = new ScrollTimeline({
  scrollSource,
  timeRange,
});

const effectKeyframes = new KeyframeEffect(
  elem,
  // Animation needs to bind the keyframe[{transform: 'scale(1)'},
    {transform: 'scale(.25)'},
    {transform: 'scale(1)'}, {duration: timeRange,
  },
);
new WorkletAnimation(
  'sample-animator',
  effectKeyframes,
  scrollTimeline,
  {},
).play();
Copy the code

More on this API click here

Parser API

Allow developers to extend the CSS lexical analyzer freely.

Parsing rules:

const background = window.cssParse.rule("background: green");
console.log(background.styleMap.get("background").value) // "green"

const styles = window.cssParse.ruleSet(".foo { background: green; margin: 5px; }");
console.log(styles.length) / / 5
console.log(styles[0].styleMap.get("margin-top").value) / / 5
console.log(styles[0].styleMap.get("margin-top").type) // "px"
Copy the code

Parsing the CSS:

const style = fetch("style.css")
        .then(response= > CSS.parseStylesheet(response.body));
style.then(console.log);
Copy the code

Font Metrics API

It will provide methods to measure the size of text elements rendered on the screen and will allow developers to control how text elements are rendered on the screen. These values are difficult or impossible to measure using current features, so this API will make it easier for developers to create CSS features related to text and fonts. Such as:

  • Flex layout:align-itemsBaseline characteristics. You need to know the baseline location of the first element in each Flex box.
  • Initials: You need to know the baseline height of each letter and the maximum height of the letter, as well as the baseline length of the newline content.
  • Single glyph forward and backward.
  • Newline: You need access to the font data, all style input for the text, and layout information (available paragraph lengths, etc.).
  • Each of the elementsline boxesYou need a baseline. (line boxesRepresents a multitude ofinline boxesThis line)

Houdini is currently progressing

Click here to view the website

The blueprint of Houdini

Knowing this, some developers might say, “I don’t need all this fancy technology, it doesn’t pay. I just want to write a few pages and build a normal Web App, and I don’t want to try to interfere with the browser rendering process to implement experimental or cool features.” If so, we might as well take a step back. Think back to a recent project where the technology used to achieve page effects had to be abandoned when the grid layout was considered compatible with older browsers. We want to control how the browser renders pages not just to show off, but to help developers solve two problems:

  1. Unify the behavior of major browsers
  2. likeJavaScriptAgain, when introducing new features, we can passPolyfillQuickly put into production environment.

Look back a few years later, when Houdini is fully supported in major browsers. We can use any CSS properties we want in the browser, and they work perfectly. Problems like the grid layout today were not friendly enough to be supported in older browsers. Back then, we simply installed the corresponding Polyfill to solve similar problems.

If any errors are found in the article, feedback and correction are welcome.

Write in the last

Finally, recommend a full set of TS tutorials. Recent TS in ascension, collect a set of very good tutorial, free to share with XDM www.yidengxuetang.com/pub-page/in…

Refer to the link

Css-in-js in detail

Houdini: Maybe The Most Exciting Development In CSS You’ve Never Heard Of

Is Houdini ready yet

CSS-TAG Houdini Editor Drafts

Houdini Demo