background

Currently, the community is concerned about the “isolation” of micro-front-end application styles, and there have been many articles on the Internet describing the style isolation of micro-front-end. However, if the style of a microapplication is only “isolated” but not “manipulable,” then the style of a microapplication is deterministic: like a function that takes no external arguments, always does pre-defined things, and does not accept control or configuration from the caller.

In this inflexible microapplication architecture, it is difficult for the host application to control the style theme of the entire site because microapplications are inherently closed and stubborn. Application scenarios will be greatly compromised, such as the following requirements are difficult to achieve:

  • The theme brand color was upgraded from orange to light blue
  • Switch the light/dark theme online
  • The same microapplication is used in multiple hosts. In some hosts, it appears orange; In other hosts, it appears blue.

The old approach was very expensive: for example, for each microapp, you could build a CSS for each theme, and then use JS to switch the CSS for each microapp based on the host’s current theme state. Moreover, any modification involving a theme requires developers to modify, rebuild and release multiple micro-applications, which is extremely tedious. The root cause is that the microapplications have hard-coded styles (and implemented style isolation) that cannot be changed by the host’s configuration, and there is a lack of information transfer between the host and child applications.

Hence the need for style control: when UIs from different projects (different teams, or applications developed at different times) are integrated together, style control is needed to combine the encapsulation and controllability of style.

Style control theory also applies to business components and blocks.

Introduction to style control scheme

In this paper, we share a solution to achieve style control of micro-application through CSS Variable. The style of micro-application is no longer fixed, but the host application can control it by “passing the arguments”. In this scenario, the style of the microapplication is like a function with optional parameters:

Function renderStyle(cssVar1 = defaultVar1, cssVar2 = defaultVar2) { CssVar3 to render style // where cssVar3 is captured from the environment closure without being explicitly passed in by the caller}

While encapsulating internal styles, it also provides configurable APIs to the outside. More flexible to meet the needs of a variety of scenarios.

Based on the default source of style variables, style control patterns can be divided into two types:

  • Quarantine priority:

    • Microapps have their own default theme. Use your own theme by default, independent of the host environment. Out of the box, no configuration is required.
    • The host is in control. When the host wants style control, it can precisely, on-demand, and explicitly override each micro-application’s topic variables to ensure that the entire application is consistent.
  • Inheritance First: By default, style variables for microapplications inherit the values of the host environment. No explicit host overwrite is required.

The following with a few simple examples, to introduce the implementation of this scheme.

Isolation first mode

Microapplications use their own themes by default, independent of the host environment. The host can explicitly override style variables.

Out of the box default theme

Here’s how the micro-application mount looks:

/* Widget CSS */. Widget-k7na5-default-theme {-- Button-bg: orange; } .widget-k7na5-btn { background-color: var(--button-bg); }

Among them,
widget-k7na5Is a prefix to the class name of a micro application that implements style isolation. You can implement style isolation in a variety of ways on the web (such as CSS Module, CSS-In-JS), all of which can be combined with this solution.

Its DOM structure is as follows:

<! -- <div classname=" Widget-k7na5-default-theme "> <button classname=" Widget-k7na5-btn ">button</button> </div>

Therefore, the microapplication displays the default orange theme. Out of the box, no configuration required.

The key is to define a default style variable on the microapplication root element; Then, within the micro-application, the style variables are referenced to implement the style, rather than hard-coding the specific values in the style.

Even if the host accidentally uses a variable name with the same name (as the host CSS does)
html { --button-bg: red; }), and micro apps are not affected. Because the microapplication root element resets this variable to its default value, it ensures good encapsulation and isolation.

The host explicitly overrides the style variables of the micro application

If the host wants to change the theme of the entire site to blue, then the microapplication should not continue to appear orange. Therefore, the host needs to have the ability to override the default theme of the microapplication.

How do you do that?

First, the micro application provides an API that allows the host to configure the class name of the root element of the micro application.

/* host js */ // When the host loads the microapplication, Function App() {return < loadWidget id="widget-instance-list" className="theme-blue" />}

So the microapp has the following DOM structure:

<! <div classname=" Widget-k7na5-default-theme-theme-blue "> <button classname=" Widget-k7na5-btn ">button</button>  </div>

The loading style of the microapp is the same as before, without changing:

/* Widget CSS */. Widget-k7na5-default-theme {-- Button-bg: orange; } .widget-k7na5-btn { background-color: var(--button-bg); }

Therefore, the micro application style is always only prepared a, do not need to use JS to do dynamic switching, implementation and maintenance are very simple.

The host’s style contains the following topic variable definitions:

/* Host CSS */ /* Selector weight is higher than the microapplication's own variable definition */.theme-blue.theme-blue {-- Button-bg: blue; }

Done! Buttons in the app are now displayed with a blue theme! The key point here is that the host overrides the CSSVAR style variable definition on the microapplication root element.

By simply adding a.theme-blue class name to each microapp in this way, the host can make the entire site uniformly blue. In the process of updating the theme, the individual micro applications do not need to make any changes and release.

Encapsulation is maintained through the style API

Note that the host never intrudes into the implementation inside the microapplication, maintaining the encapsulation of the microapplication. The host uses only the following APIs:

  • Customize the root element className by className.
  • The name of the CSSVAR variable that the microapplication supports configuration. They are an API, just like the named arguments to a function.

If a microapplication has some internal CSSvar variable names that it does not want to be used externally, it can be avoided by using a special naming convention.

As long as the two APIs remain stable, the host and the microapplication can iterate independently. Encapsulation and flexibility.

Advanced example: precision control

This scheme is simple and flexible, and the host can precisely control each micro-application, passing different style variables to different micro-applications.

The host JS is as follows, passing the theme class name to each microapplication:

/* host js */ // When the host loads the microapplication, Function App() {return (<> < loadWidget id="widget-instance-list" className="theme-blue" /> < loadWidget id="widget-instance-list" className="theme-green" /> </> ); }

The host’s style contains variable definitions for each topic class name:

/* Host CSS */ /* Selector weight higher than the Widget's own variable definition */.theme-blue.theme-blue {-- Button-bg: blue; } .theme-green.theme-green { --button-bg: green; }

In this case, the former will be shown with a blue theme and the latter with a green theme. The two do not interfere with each other and are completely controlled by the host.

Inheritance first pattern

In the previous isolation first mode, the microapplication presents its own theme by default. The host can override the theme of a microapplication, but it is a bit of a hassle to explicitly customize the class name for each microapplication.

CSSVAR can also implement an inheritance first approach: a microapplication inherits the host’s style variables by default, without the host passing in additional configuration to the microapplication.

Some clarity of control is lost (in isolation first mode, the micro-application topic is explicitly controlled by the root element class name) in exchange for convenience.

The host js:

/* host js */ function App() {return < loadWidget id="widget-instance-list" disableDefaultTheme />}

Where, DisableDefaultTheme makes the microapplication root element do not have the default theme class name. Therefore, the micro-application DOM structure is as follows:

<! > <div classname=""> <button classname=" Widget-k7na5-btn ">button</button> </div>

The host CSS:

/* host CSS */ HTML {-- Button-bg: blue; }

Notice that the host defines the topic variables directly at the global scope. There is no need to define a variable for the root element of each micro application.

The CSS that microapplications load is the same as before, without changing:

/* microapplication CSS */ /* Note that the root element does not have a.default-theme class name, so this rule does not take effect */. Widget-k7na5-default-theme {-- Button-bg: orange; } .widget-k7na5-btn { background-color: var(--button-bg); }

Thus, by default, the microapplication will read from the host environment the value –button-bg, shown in blue.

Of course, the host can still explicitly override the style variables of the microapplication.

conclusion

This article discusses two patterns of style control between microapplications and hosts:

  • Isolation priority: By default, the microapplication uses its own style variables, which the host can explicitly override
  • Inheritance first: Inherit style variables from the host environment by default

The two patterns can be used in combination; for example, some style variables in a micro application use the Isolation Priority pattern, while others use the Inheritance Priority pattern.

Notice that in all of the previous examples, the CSS that the microapplication loads has not changed. We can implement the above micro-application style control pattern simply by manipulating the root element class name. Therefore, a microapplication does not need to package a CSS for every pattern. Js implementation is also very simple (just manipulate the root element class name). The host application can easily control and switch the overall site style.

Implementations of these two patterns can be built into the micro-front-end framework. Microapplication developers only need to define the theme variables they want to use, the default value of the variables, and the control mode of the variables. The class name of the root element is managed by the micro-application loader.