CSS isolation issue in Blazor

CSS isolation issue in Blazor

1. Environment

VS 2019 16.9.0 Preview 1.0

.NET SDK 5.0.100

2. Introduction

Once CSS takes effect, it will be applied globally, so conflicts are likely to occur. In order to solve this problem, CSS isolation was born. Blazor was born in 2018, which is more than 2 years ago. However, CSS isolation was not supported until .NET 5.

3. Isolation between Razor components

CSS isolation between Razor components should be the simplest and most convenient way to use CSS isolation. It is very simple to achieve CSS isolation between Razor components. You only need to create a ".razor.css" file with the same name in the directory where the component is located. If there is a component named "Component.razor" in folder A, you only need to create "Component.razor.css" in folder A to set a separate style for the "Component.razor" component without affecting other components.

Taking the default template as an example, create a new "Index.razor.css" with the following content:

h1 {
    font-size: 48px;
    font-weight: bold;
}

Create a new "Counter.razor.css" with the following content:

h1 {
    font-size: 16px;
    font-weight: 400;
}

The effect is as follows:

The above component CSS files will be generated as "project name.styles.css" files, which will be added to "index.html" by default in .NET 5. The above two CSS files will be compiled into the following results:

/* /Pages/Counter.razor.rz.scp.css */
h1[b-g5zg69lne1] {
    font-size: 16px;
    font-weight: 400;
}
/* /Pages/Index.razor.rz.scp.css */
h1[b-f3rb2cn7la] {
    font-size: 48px;
    font-weight: bold;
}

Viewing the DOM element in the browser, the result is as follows:

<h1 b-f3rb2cn7la>Hello, world!</h1>

<h1 b-g5zg69lne1>Counter</h1>

That is to say, an attribute starting with "b-" plus 10 random characters is added to the DOM of these two components, which seems to be similar to Angular (I have not used it, but I have seen similar things in browsers). CSS isolation in Blazor seems to be achieved through random property names. So, what does it look like to generate styles.css through id and class? This is also achieved through random attribute names. For example, the following component CSS file:

#zxyao-a {
    font-size: 48px;
    font-weight: bold;
}

#zxyao-b {
    font-size: 24px;
    font-weight: bold;
    background-color: #ff0000;
    padding: 16px;
}

.zxyao-cls {
    font-size: 24px;
    font-weight: bold;
    background-color: #000;
    color: #fff;
    padding: 16px;
}

It will be compiled into the following result:

/* /Pages/Index.razor.rz.scp.css */
#zxyao-a[b-f3rb2cn7la] {
    font-size: 48px;
    font-weight: bold;
}

#zxyao-b[b-f3rb2cn7la] {
    font-size: 24px;
    font-weight: bold;
    background-color: #ff0000;
    padding: 16px;
}

.zxyao-cls[b-f3rb2cn7la] {
    font-size: 24px;
    font-weight: bold;
    background-color: #000;
    color: #fff;
    padding: 16px;
}

The results are as follows:

That is to say, no matter how the component CSS file summary is written, it will be converted into the form of CSS selector [random attribute].

4. CSS isolated subcomponent support

By default, component CSS is applied only to the current component. For example, there are two components:

/* Index.razor */
<div class="my-text">
    Welcome to your new app.
    <CssIsolation.Components.Child />
</div>

/* Components/Child.razor */
<h1>Child</h1>
<div class="my-text">
    This is a child component</div>

If the style in "Index.razor.css" is as follows,

.my-text {
    border:2px solid #000;
    padding: 16px;
}

Then it only works for "Index.razor" - the border appears on the outermost Index component.

If you want it to work on this component and its subcomponent ".my-text" element, you can use "::deep" to mark it:

::deep .my-text {
    border:2px solid #000;
    padding: 16px;
} 

Did you find that the border of ".my-text" of this component is gone? As mentioned before, here, ::deep will be replaced by random attributes, that is, the compilation result is as follows:

/* /Pages/Index.razor.rz.scp.css */
[b-f3rb2cn7la] .my-text {
    border:2px solid #000;
    padding: 16px;
}

Among them, b-f3rb2cn7la refers to the root element of this component, as shown in the figure.

If there is no unique parent element tag within this group, each native HTML tag within this group will have the same random attributes. For example, in the component below, both "div" and "h1" will have the same random attributes, and the "::deep" flag will be replaced with this attribute. Elements in the "Child" component will not have random attributes.

<div class="my-text">
    Welcome to your new app.
</div>
<h1>
    Welcome to your new app.
</h1>
<CssIsolation.Components.Child />

Some component libraries provide components such as "Template", such as Ant Design Blazor. If you use components to wrap all elements, such as:

<AntDesign.Template>
    <div class="my-text">
        Welcome to your new app.
        <CssIsolation.Components.Child />
    </div>
</AntDesign.Template>

Blazor will ignore the outer components until it finds the first native HTML element in this group, and then add random attributes to all native HTML elements in this layer.

Therefore, when the root element of this component has the same CSS selector as the element whose subcomponent needs to set the style, if you want to isolate the style to take effect on both this component and the subcomponent, there are two ways: one is to write CSS styles for this group and the subcomponent at the same time, and the other is to wrap all components and elements with one element, that is, change the root element.

5. CSS Preprocessor Support

Many times, we may use SCSS or LESS to write style files. Blazor does not natively support these preprocessors. We can use the task runner resource manager to compile SCSS or LESS before the project is generated, or use some third-party libraries for support, such as Delegate.SassBuilder mentioned by Microsoft. I tried Delegate.SassBuilder. Maybe because I used it incorrectly, the generation of CSS files seemed to be later than the project generation, and the CSS files could not be compiled when the program was generated for the first time. Next, I will share another way, which is to use the "Task Runner Explorer".

Here I simply used "node-sass" and compiled it directly through the command line without using advanced tools such as Gulp or Webpack. The solution steps are as follows (the installation of node-sass will not be discussed here):

Download and install the extension "Command Task Runner"

Write the SCSS file compilation command line program "scss.bat"

Create a new scss.bat file in the root directory of the project:

And write the following command.

node-sass -r ./ -o ./ --source-map true --source-map-contents sass --output-style compressed

This command will compile the SCSS file and generate a compressed CSS file and a corresponding source map file.

Add bat file to Task Runner

Right click on the scss.bat file and select the "Add to Task Runner" option.

Bind Run Task

Open View | Other Windows | Task Runner Explorer, find the scss command, right-click, and select Binding | Before Generation. After binding, you can see the command under Before Generation in the Binding window on the right.

After enabling the task runner, a "commands.json" file will be generated in the solution directory. The content of my file is as follows. The "-vs-binding" option indicates the location of the runtime of the task binding.

{
  "commands": {
    "scss": {
      "fileName": "cmd.exe",
      "workingDirectory": ".",
      "arguments": "/c scss.bat"
    }
  },
  "-vs-binding": { "BeforeBuild": [ "scss" ] }
}

Next, just run the program directly to see the effect.

Of course, in SCSS, we can also use the "::deep" tag, which can also be displayed correctly, for example:

/* Pages/Index.razor.scss */
.my-text {
    border: 2px solid #000;
    padding: 16px;

    ::deep {
           .my-text {
            border: 2px solid #ff0000;
            background-color: #000;
            color: #fff;
        }
    }
}

/* Components/Child.razor.scss */
h1 {
    background-color: #efefef;
    font-weight: 700;
}

The corresponding Razor components are as follows:

/* Pages/Index.razor */
@page "/"

<div class="my-text">
    Welcome to your new app.
    <CssIsolation.Components.Child />
</div>

/* Components/Child.razor */
<h1>Child</h1>
<div class="my-text">
    This is a child component</div>

The operation effect is as follows:

However, I personally feel that using the "::deep" tag in SCSS may be a bit confusing. It is recommended to either not use the "::deep" tag or put the "::deep" tag on the outermost layer, as shown below.

// Some SCSS styling code...


::deep {
	// Some SCSS style codes for subcomponents ...
}


// Some SCSS styling code...

6. Modify random attribute identification

As mentioned earlier, the default form of random attribute names generated by Blazor is to start with "b-" plus 10 random characters. Microsoft's official documentation shows that this can be changed. This is more friendly for their own applications. For example, Xiaomi can define the random attribute form to start with "mi", Taobao can define the random attribute form to start with "tb", and so on. However, this feature seems to have problems. Someone has raised an issue on Github - Custom CSS Scope Identifier not working. I hope Blazor can become more and more perfect.

This is the end of this article about CSS isolation in Blazor. For more information about CSS isolation in Blazor, please search for previous articles on 123WORDPRESS.COM or continue to browse the following related articles. I hope you will support 123WORDPRESS.COM in the future!

<<:  Example of assigning values ​​to ActiveX control properties by param name in a web page

>>:  How to set horizontal navigation structure in Html

Recommend

Tutorial on how to quickly deploy a Nebula Graph cluster using Docker swarm

1. Introduction This article describes how to use...

Detailed process of installing nginx1.9.1 on centos8

1.17.9 More delicious, really Nginx download addr...

Web page HTML code: production of scrolling text

In this section, the author describes the special...

A very detailed summary of communication between Vue components

Table of contents Preface 1. Props, $emit one-way...

In-depth study of vue2.x--Explanation of the h function

Table of contents Solution, Summarize: vue projec...

Detailed process of using nginx to build a webdav file server in Ubuntu

Install nginx Note that you must install nginx-fu...

Example code of vue custom component to implement v-model two-way binding data

In the project, you will encounter custom public ...

How to connect XShell and network configuration in CentOS7

1. Linux network configuration Before configuring...

Solution for Nginx installation without generating sbin directory

Error description: 1. After installing Nginx (1.1...

Analysis and solution of MySQL connection throwing Authentication Failed error

[Problem description] On the application side, th...

WeChat applet realizes multi-line text scrolling effect

This article example shares the specific code for...

How to use JSX in Vue

What is JSX JSX is a syntax extension of Javascri...