The text/swim deer

As alibaba’s four-year-old front-end base component library, Fusion Next has been updated to support CSS-variable capability in version 1.20, which supports run-time peeling. The purpose of this article is to explain why Fusion Next supports this capability, how it works, what the challenges are, and how it can be applied to business projects.

It has to be mentioned that in the upgrade of Css Variable supported by FusionNext, ali Cloud console team **@ Xiao Yu **@ Liu Si and Ali Cloud Hybrid cloud experience technology **@ Zuo Qi ** participated in the whole process of contribution, deeply participated in and supported the research and construction of core capabilities, which is really quite reliable, like ๐Ÿ‘

First, why support? ๐Ÿค” ๏ธ

Css-variable is not a new technology, major browser vendors and even IE Edge have supported it since March 2017.

Fusion serves the vast majority of BU of Ali Group. In fact, since 2019, it has received requests from users to support CSS variables to meet their demands for online theme switching. The solution was to compile multiple CSS files and load and unload styles online. Now that we have a solution, what was the reason we decided to launch in S1 2020?

1.1 Unification of cross-end technology stacks

With the development of mobile services, more and more middle and background services require that they can be used on both PC and H5. Front end committee also set up cross – end direction, support a yard cross – end. The cross-ends here include cross-PC, H5, and various applets available.

In implementation, @alifd/ Next (based on React) and @alifd/meet (based on RAx) are used to meet PC and Mobile respectively (@alifd/meet will be converted to @alifd/meet-react via rax2react technology) The React implementation, combined with the PC). One code multi terminal, in the development of time will inevitably use the style variable, so the style scheme needs to be unified.

The small program is based on RAX. Due to the limitation of raX’s underlying capabilities, the style is directly written on the style (className is not supported), so the choice of Fusion Mobile style scheme is limited and only CSS-variable scheme can be used. That is to say, on Fusion Mobile, All variables are CSS variables. Therefore, the unified technology stack for cross-end projects is React + CSS variable.

1.2 Slow compilation of SASS affects development efficiency

Fusion style system is bound to SASS scheme. Sass compilation style belongs to the compile-time scheme. Plug-ins such as Webpack are used to convert SASS codes into CSS. According to the data from icEJS Optimization Exploration for Engineering Construction, the initial compilation time of FusionDesignPro-TS style is 48.3s in development environment and 41.1s in production environment.

If the CSS -variable scheme is used in Fusion, all files are final and sASS compilation is not required. In this case, part of the sass- Loader time can be saved.

1.3 Online skin changing

“Online skin” has gradually become a strong appeal of background business, here we can divide online skin into two categories:

  • Scene switching, such as light theme <=> dark theme, business 1 theme <=> Business 2 theme
  • Lightweight & personalized customization, such as changing theme colors, rounded corners <=> right angles

For the first type, we can use scope to switch the entire CSS variable, for the second type, we can simply override the theme color related variables or rounded variables.

As you can see, switching themes through CSS variables is much more flexible and elegant than the previous theme pack style file generated for each theme.

In addition, I received the demand from the Taichung team of Ali Cloud Control. Due to business needs, designers upgrade the style once a year. Although XConsole supports the console business, most of the specific consoles are not directly developed by them, so it is difficult to push the business to upgrade the style.

If the csS-VAR scheme is changed, it is equivalent to changing the injection of style variables from the compilation time of SASS to the runtime time of CSS-variable. In this way, the purpose of style update can be realized only once in the container environment without business upgrade.

(In addition, the compatibility of CSS-variable can be checked in Chapter 4.)

2. Solution ๐Ÿ’ป

2.1 Preliminary Investigation

Research must be done before new functionality is supported to reduce the waste of resources in the specific implementation process. The requirements of this iteration are: ** Must support new CSS variable functionality while minimizing structural changes and code modifications. ** in order of execution:

  • Starting from the minimum change, determine the overall transformation thinking;
  • From the user use, determine the component library package structure;
  • From a technical point of view, investigate the technical points of converting SASS variables into CSS variables.

2.1.1 Overall transformation idea

The Development of FusionNext uses SCSS variables, including basic variables and component variables (basic variables are recommended to be used in service components and pages, but component variables are not recommended).

These variables add up to hundreds, so it is not practical to change them one by one, so we decided: the development of the basic component library still uses THE SCSS variable scheme, but in the packaging phase, through the script to generate a mapping of SASS variables to CSS variables, using this mapping with the code to compile, produce new files with CSS variables. That is, CSS variables differ from sass variables in their names by replacing $with –.

$color-brand1: #5584FF! default; $size-base: 4px ! default; $corner-1: 3px ! default; $line-zero: 0px ! default; $btn-size-s-height: $s-5! default; $btn-size-s-padding: $s-2 ! default; $btn-size-s-corner: $corner-1 ! default; $btn-pure-secondary-color: $color-brand1-6 ! default;Copy the code

Of course, we encountered various problems in the implementation of the solution, which will be highlighted in the chapter “SASS variables into CSS variables technical breakthrough”.

2.1.2 Component library package structure

In line with the principle of only adding and not modifying or modifying less, from the perspective of styles, users should be supported to “fully use styles” and “quote styles on demand” without affecting existing users. Therefore, NPM structure is mainly reflected as follows:

  • indist/Add next. Var. CSS file to pack all styles.
  • inlib/[component] es/[component]New styles of CSS variables in style2.js are added in the directory, and users can choose to reference them.

The final NPM structure is as follows:

โ”œโ”€ dist โ”‚ โ”œโ”€ section.var. CSS # Does not include the CSS - var variable definitions โ”‚ โ”” โ”€ โ”€ next. Var. Min. CSS # [new] compressed version next. Var. CSS โ”œ โ”€ โ”€ lib โ”‚ โ”œ โ”€ โ”€ component1 โ”‚ โ”‚ โ”” โ”€ โ”€ SCSS โ”‚ โ”‚ โ”‚ โ”” โ”€ โ”€ Variable. SCSS # For the [style variable specific to this component], map SCSS -var to the actual value (theme can override it). โ”‚ โ”‚ โ”œโ”€ โ”œโ”€ main.scSS # This is a fusion file โ”‚ โ”‚ โ”œโ”€ index.jsx # Both SCSS -var mapped to actual value and SCSS -var mapped to CSS-var are supported. โ”‚ โ”‚ โ”œโ”€ Heavy Metal Guitar School - Style. Js # โ”‚ โ”‚ โ”œโ”€ Heavy metal Guitar School # โ”‚ โ”‚ โ”œโ”€ โ”œโ”€ โ”œโ”€ โ”œโ”€ โ”œ.css # โ”œโ”€ โ”œโ”€ โ”œ.css # โ”œโ”€ โ”œ.css # โ”œโ”€ โ”œ.css # โ”œโ”€ โ”œ.css # โ”œโ”€ โ”œ.css # โ”œโ”€ โ”œ.css # โ”œโ”€ โ”” 2 # โ”œโ”€ โ”œโ”€ โ”œโ”€ โ”œโ”€ โ”œโ”€ โ”œโ”€ โ”œโ”€ โ”œโ”€ โ”œโ”€ โ”œโ”€ โ”œโ”€ โ”œโ”€ โ”œโ”€ โ”œโ”€ โ”‚ โ”” โ”€ โ”€ style โ”‚ โ”” โ”€ โ”€ _color. CSS # [new] [to automatically generate] to CSS format โ”œ โ”€ โ”€ es / # with lib โ”œ โ”€ โ”€ no change types / # โ”” โ”€ โ”€ package. The json # no changeCopy the code

2.1.3 Turning SASS variable into CSS variable technical breakthrough โœจ@ Xiao Yu

Here are the technical difficulties and solutions from the preliminary investigation. The core contributor of the solutions and implementation is @Xiaoyu

Sass variables can/cannot be converted to CSS variables

We are known as “CSS variables”, is actually “CSS custom properties” (CSS Custom properties), also known as and variable, it can not be used in the “CSS selector”. So $CSS-prefix as a class name cannot be converted to CSS variables, which need to be converted to specific values in stylesheet, such as.next-, etc. $font-custom-path $font-custom-path

// variables.scss $css-prefix: '.next-'; $btn-pure-secondary-color: $color-brand1-6 ! default; // main.scss #{$css-prefix}btn { background-color: $btn-pure-secondary-color; }Copy the code

How are sASS math calculations handled

The mathematical calculation of SASS can be converted into CSS calculation by CALC, which can solve 80% of the problems. There are some holes, which can be viewed in “2.2.2 Modifying Some original style codes ยท CALC Mathematical Calculation Attention Rules”.

How to deal with sASS color calculation

CSS has no corresponding color calculation processing function, and the color calculation used in Fusion Next is rgba transparentize and less than 10. Therefore, the final value of the part that uses SCSS color calculation can be directly calculated.

Note here that CSS rgba() is not exactly the same as Sass rgba(), e.g. Rgba (#FFF, 0.5).

Sass can be converted to RGBA (255, 255, 255, 0.5) and is not valid on CSS

$btn-ghost-dark-color-disabled: rgba($btn-ghost-dark-color-disabled-rgb, $btn-ghost-dark-color-disabled-opacity); => --btn-ghost-dark-color-disabled: rGBA (255, 255, 255, 0.4);Copy the code

How to deal with sASS process control statements

Css-var cannot participate in any compile-time calculation of SASS because its data is determined at run time. Flow control statements are also compile-time calculations.

Fortunately, there are very few violations of this principle in the Fusion source code, most notably @mixin icon-size. Most of the other @if statements do not involve CSS-var, so they do not violate this principle.

The problem

The following code replaces $shadow with var(–shadow) (a sass string value), @if $shadow == $shadow-zero does a string comparison, makes no sense, and silently executes the error.

@if $shadow == $shadow-zero {
  $shadow-top: null;
  $shadow-right: null;
  $shadow-bottom: null;
  $shadow-left: null;
}
Copy the code
plan

The main difficulty in solving this problem is that such mixins with flow control are difficult to implement with native CSS.

Since there are very few csS-VAR flow control statements in the Fusion source code, we can discard the dynamic switching capability of this style based on CSS-VAR.

That is, such @if statements are expanded when the topic package SCSS is built, resulting in the corresponding CSS rule. These CSS rules will not be switched when switching csS-var. If the expansion behavior of the @if statement needs to change due to a change in the style variable, the user project needs to rebuild the publication.

Add a utility function to Fusion:

@use "sass:list"; @use "sass:meta"; @use "sass:map"; @use "sass:string"; $compiling-value($var) {@function get-compiling-value($var) {@if(type-of($var) == 'number') {@return $var; } @return map.get($var-map, $var); }Copy the code

Where $var-map contains the value of the style variable determined when compiling SCSS:

*/ $var-map: (var(--color-brand1-1): #F3FAFF, var(--checkbox-size): 14px,)Copy the code

Sass treats something like var(–color-brand1-1) as a normal string!

Change [csS-var process control statement] as follows:

@ the if (size 12) instead of @ else {}, {} / / @ the if (the get - the compiling - value (size) < 12) {} @ the else {}Copy the code

If $size is var(–checkbox-size) (a sass string value), resolveBuildTimeValue($size) returns 14px, and @if expands to negative branch.

This scheme requires very little modification.

disadvantages

This expansion behavior is determined at compile time, and even if the line is actively changed with csS-variable –checkbox-size: 8px, the negative branch of the @if statement is still in effect. But we think we can accept this inconsistency for now because:

  • There are very few flow control statements involving CSS-VAR in Fusion

  • Css-var hot switching usually happens when the same series of theme switch (such as dark/light mode), theme update. For icon-size mixins, the @if statement goes into the same branch before and after the switch

  • The end result is an almost imperceptible inconsistency caused by eager exchange

If you want to re-expand the @if statement with the latest style variable, just rebuild the publish application.

2.2 Modifying FusionNext Component Library โœจ @Xiao Yu โœจ @Zuo Qi

This part accounts for 70% of the workload of this upgrade and is the main co-construction point. โœจ @Xiao Yu โœจ @Zuo Qidu participated in it

Recall that our solution is: ** The development of the base component library still uses the SCSS variable scheme, but in the packaging phase, the script generates a mapping of SASS variables to CSS variables, which is compiled with the code to produce new files with CSS variables. ** So the first step is to solve the “transition problem of SASS variables to CSS variables”. As mentioned above, since SASS syntax is more comprehensive than CSS syntax (such as @if) and better to use (such as four operations, etc.), we still need to “modify some of the original style code” in the process of collecting from large collections to small collections.

2.2.1 Solve the transition problem of SASS variables to CSS variables

Sass files have associations with CSS files, and we decided to document these associations by adding temporary files at compile time. The original file of the project is variable. SCSS main. SCSS

{background-color: $main-bg-color; } / / $main-bg-color: $color-brand1;Copy the code

A temporary file will be generated using variable. SCSS of the following two types:

  • Scss-var-to-css-var. SCSS: Maps SCss-var to CSs-var, which can actually be generated automatically by./variable. SCSS

  • Css-var-def-default. SCSS: Defines the actual value of CSs-var (theme can override it), which can actually be generated automatically by./variable. SCSS

    */ $main-bg-color: var(–main-bg-color)! */ $main-bg-color: var(–main-bg-color)! default;

    */ –main-bg-color: var(–color-brand1);

The final compiled entry file for styles with CSS variables changes to:

SCSS sass variable version */ @import "variable. SCSS" @import "main. SCSS "/* index.scss CSS variable version */ @import "scss-var-to-css-var.scss" @import "css-var-def-default.scss" @import "main.scss"Copy the code

The mapping of basic variables and component variables is stored in SRC /core-temp lib/[Component]/sass/ and deleted after construction. The structure is as follows

โ”œ โ”€ โ”€ the SRC โ”‚ โ”œ โ”€ โ”€ the core โ”‚ โ”‚ โ”” โ”€ โ”€ style โ”‚ โ”‚ โ”” โ”€ โ”€ _color. SCSS โ”‚ โ”œ โ”€ โ”€ the core - temp # temporary files โ”‚ โ”‚ โ”” โ”€ โ”€ style โ”‚ โ”‚ โ”œ โ”€ โ”€ _color - to - CSS - var. SCSS # [temporary file] [base variable], map SCSS-var to CSS-var. File name to be determined. It can actually be made by.. / core/style / _color SCSS automatically generated โ”‚ โ”‚ โ”” โ”€ โ”€ _color - def - default. CSS # temporary file based variable ใ€‘ ใ€, defines the actual value of the CSS - var (topic can override it). File name to be determined. It can actually be made by.. / core/style / _color SCSS automatically generated โ”‚ โ”” โ”€ โ”€ index. The js โ”œ โ”€ โ”€ lib โ”‚ โ”” โ”€ โ”€ component1 โ”‚ โ”” โ”€ โ”€ SCSS โ”‚ โ”‚ โ”œ โ”€ โ”€ SCSS - var - to - CSS - var. SCSS # [temporary file] [special style variable for this component], map SCSS-var to CSS-var. โ”‚ โ”‚ โ”œโ”€ CSS-var -def-default. SCSS # [temporary file] [csS-var] [theme can override it] โ”‚ โ”œโ”€ โ”œโ”€ style โ””โ”€ test โ”œโ”€ types โ”œโ”€ package.jsonCopy the code

2.2.2 Modify part of the original style code

Through the above modification can basically complete the transformation of SASS variables to CSS variables, after the transformation of some fine (Keng) section (dian) problems need special attention, such as four operations. In SASS, you can use + – * / directly, while in CSS, you need to wrap calc around it. Some students may worry about the compatibility of CALC and whether the performance is OK. We have investigated and dealt with these problems:

Calc performance and compatibility โœจ@ Xiao Yu

There is no performance problem with using CALc here, because in the Fusion transformation, it does simple calculations (such as 24px / 2). Calc () typically causes performance problems when percentages are involved. For this simple calculation, calc() is faster than csS-in-JS schemes anyway.

And for simple calculations, calc(24px / 2), we’ll use the postCSs-calc plugin to change it to 12px.

Also, CalC is compatible with IE9, FusionNext basically does not use percentages in Calc, and concerns about calC compatibility are not addressed by Duck.

In the actual operation, we found that sASS mathematical calculation was replaced by CALC, and there was compilation failure in some component style codes. After debugging, we found that the use of CALC should pay attention to the following points:

Calc details

  1. The + – operator must be preceded by one space, otherwise it will not be valid (/ * is also recommended for consistency).

  2. No negative sign is allowed before calc(). You need to use ‘0px -‘ to resolve this

  3. If there is a specific value in calc(), specify units such as px. This is illegal in most CSS properties (except for line-height).

    // right -(
    a + a +
    b) => calc( 0px – #{a} – #{b}) // wrong -(
    a + a +
    b) => -calc(#{a} + #{b}) -(
    a + a +
    b) => -calc(#{a}+#{b})

    // right 2.3 Generating a Theme Package with CSS Variables

    In order to support the online peels of CSS variable versions, you need to generate the CSS variable set of the current theme through the theme package, that is, @alifd/theme-xxx/variable. CSS. This file contains “base variables” and “component variables”.

    If the current theme has a custom icon, you will need to import additional ICONS.var.css.

    Note here that dist/ Next-var.css for the theme package may not be identical to dist/ Next-var.css for the base component. The theme package is recompiled based on the result of the designer’s configuration, such as $font-custom-path compilations of different values, generated next. Var. CSS.

    The final theme package NPM structure is:

    . โ”œ โ”€ โ”€ dist โ”‚ โ”œ โ”€ โ”€ next. Var. CSS # [new] using the CSS - all variable component style โ”‚ โ”” โ”€ โ”€ next. Var. Min. CSS # [new] next. Var. The compressed version of CSS โ”œ โ”€ โ”€ โ”œโ”€ โ”œโ”€ moves.vs.css # [add] moves.vs.css # [add] moves.vs.css # [add] moves.vs.css # [add] moves.vs.css # [add] moves.vs.css # [add] moves.vs.css # [add] moves.vs.css # package.jsonCopy the code

    How to use this new ability โœจ@ Sasaki

    Refer to the CSS-VAR project demo provided by @Zuoqi. The idea is as follows: Introduce a basic style file and multiple variable declaration files into the project:

    /* App.css */
    @import '~@alifd/next/dist/next.var.css';
    @import '~@alifd/theme-3/variables.css';
    @import '~@alifd/theme-1/variables.css';
    
    .App {
      text-align: center;
    }
    
    .App-header {
      background-color: var(--color-fill1-2);
      transition: all .3s;
      min-height: 100vh;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      font-size: calc(10px + 2vmin);
      color: var(--color-text1-4);
    }
    Copy the code

    Modify the style scope for @alifd/theme-1/variables. CSS via the plugin:

    /* @alifd/theme-3/variables.css */ :root { --color-fill1-2: #474959; } into = > / * * / HTML. The dark {- color - fill1-2: # 474959; }Copy the code

    Switch the theme by adding a different className for the body:

    /* App.js */ import React, { useState } from 'react'; import { Switch } from '@alifd/next'; import './App.css'; function App() { const [content, setContent] = useState('light theme'); const handleChange = checked => { document.documentElement.classList.toggle('dark'); if (checked) { setContent('dark theme'); } else { setContent('light theme'); Return (<div className="App"> <header className=" app-header "> <Switch unCheckedChildren="๐ŸŒž" checkedChildren="๐ŸŒ™") onChange={handleChange}/> <h1>{content}</h1> </header> </div> ); } export default App;Copy the code

    Other issues

    Why not upgrade 2.x instead of adding this ability in the minor version of 1.x?

    1. This change is a new capability, no BreakChange

    2. Learn from the lessons learned in the 0.x upgrade. Before upgrading 2.x, you must do a good job in the ability alignment, and do the corresponding warning processing in 1.x

    How to solve the compatibility problem of CSS-variable

    CSS variables are relatively new. The ability of Internet Explorer is currently completely unsupported. Considering that Fusion is for the middle background, most of the middle background services have relatively loose requirements for browsers, even chrome. At the same time, technical capabilities need to embrace the future and not be held back for adaptation, so Fusion has made this upgrade.

    This does not mean that we have completely abandoned IE users, we have set fallback scheme in the compiled files, IE may not support skin, but it can still be used normally.

    /* dist/next.var.css */ ... . Box {/ * for IE, through postcss - custom - the properties plug-ins add https://github.com/postcss/postcss-custom-properties * / color: Rgba (255, 255, 255, 0.4); Color: var(--btn-ghost-dark-color-disabled, rGBA (255, 255, 255, 0.4)); }...Copy the code

    Five, the end

    Once again, this upgrade, ali Cloud console team @ Xiao Yu @ Liu Si, Ali Cloud hybrid cloud experience technology @ Zuo Qi and other students’ strong participation, so that this function can be implemented as soon as possible, they support their own business while participating in the construction, high efficiency and reliable.

    In particular, @Xiao Yu has made constructive contributions to the overall scheme making and code details implementation. As an ADC, she is rated as the MVP of this battle ๐Ÿ†

    The resources

    • Across the server-side component project: yuque.antfin.com/multix/modu…
    • Developer.mozilla.org/zh-CN/docs/…
    • Github.com/postcss/pos…
    • caniuse.com/#feat=calc