📜 ⬆️ ⬇️

The end of the era of global CSS

All CSS selectors live in the global scope.

Everyone who ever dealt with CSS had to put up with this global feature. The model, once created for stylization of academic documents, can hardly be called a convenient tool for creating modern web applications.

Absolutely every selector could potentially enter into a fight with another selector or stylize an "extraneous" element. In this "global" fight, the selector may even lose completely, ultimately without applying any of its rules to the page.
')
Each time modifying the css-file, you need to think carefully about the global environment in which our styles will exist. No other web development technology requires so much effort just to provide the code with a minimum level of support.

It should not be. It's time to leave behind the era of global styles. It's time for the closed CSS.

In many languages, it is assumed that any modification of the global scope is extremely rare, if it occurs at all.

Thanks to tools like Browserify , Webpack and jspm frontend, developers were able to write code consisting of small modules, each of which explicitly requests other modules on which it depends.

But CSS continues to live on its own with impunity.

Many of us are so accustomed to the features of CSS that, until recently, have not seen other ways to solve this problem, but to wait for support from browser vendors. And even after that, the moment will not come soon when most users will acquire a browser that fully supports the Shadow DOM .

Developers bypassed the problem of global classes by proposing the use of a certain system of agreements that dictates how classes should be called. OOCSS, SMACSS, BEM, SUIT — all of these techniques are designed to help avoid namespace collisions and mimic the scope.

Undoubtedly, this was a good attempt to tame CSS. But none of these methods in fact solves the problem, but only tries to get around - which of them you choose, the selectors will remain global.

Everything has changed 22 April 2015

Webpack allows you to import CSS right inside the javascript module. If you hear about such a "trick" for the first time, you can read more here and here .

The case comes webpack css-loader , allowing you to write this:

require('./MyComponent.css'); 

At first glance it looks strange. Even if you close your eyes to the fact that imported .css , and not javascript .

After all, usually the require call must be saved to a variable. If this is not done, then, as a rule, this indicates that a global variable has been declared. A clear sign of bad architecture.

But this CSS is not a global scope. So it was considered earlier.

On April 22, 2015, Tobias Koppers - the author of Webpack - added a new feature to the css-loader , and called it placeholders . Now it is known as a closed scope .

This function allows you to export class names from a CSS file and request them inside our javascript.

In short, instead:

 require('./MyComponent.css'); 

You can write this:

 import styles from './MyComponent.css'; 

What will be the value of the styles variable? Let's first take a look at how CSS looks like:

 :local(.foo) { color: red; } :local(.bar) { color: blue; } 

In this example, the syntax recognized by the css-loader is :local(.identifier) - :local(.identifier) . This code exports two identifiers [ let's call them "identifiers" because of the uniqueness that will be provided later - approx. trans. ]: foo and bar .

These identifiers indicate the class names that we can use in javascript. Here is an example of using React with om:

 import styles from './MyComponent.css'; import React, { Component } from 'react'; export default class MyComponent extends Component { render() { return ( <div> <div className={styles.foo}>Foo</div> <div className={styles.bar}>Bar</div> </div> ); } } 

Most importantly, identifiers point to guaranteed unique class names.

There is no longer any need to sculpt long prefixes for each selector, trying to imitate a closed scope. Different components can safely use their own foo and bar , and this will not lead to a collision of names.

Just think how serious the paradigm shift is going on here.

Now you can make changes to the CSS files in full confidence that the extraneous elements of the page will not be accidentally affected. So we introduced an adequate model for constructing a closed scope in CSS.

At the same time, all the advantages that the “global” classes had were still available to us. The only difference is that now, as in other areas of development, you need to explicitly import the necessary classes. Our code should not rely on global variables.

Writing supported CSS code was not possible using a set of rules for inventing names, but by encapsulating styles at the design stage.

In this scenario, we placed all control over the real class names on the webpack. And this is something that is fully customizable.

By default, css-loader translates our classes into hashes.

For example, the following entry:

 :local(.foo) { … } 

It will be compiled to:

 ._1rJwx92-gmbvaLiDdzgXiJ { … } 

This is not particularly convenient during development and debugging. To make the generated classes easier to read, you can set the desired format in the webpack configuration as a parameter passed to the css-loader:

 loaders: [ ... { test: /\.css$/, loader: 'css?localIdentName=[name]__[local]___[hash:base64:5]' } ] 

And in this case, our class will be compiled like this:

 .MyComponent__foo___1rJwx { … } 

Now both the identifier and the name of the component to which this code belongs are immediately visible.

And with the help of the environment variable NODE_ENV ( environment variable ) we can separate the compilation logic for development and production:

 loader: 'css?localIdentName=' + ( process.env.NODE_ENV === 'development' ? '[name]__[local]___[hash:base64:5]' : '[hash:base64:5]' ) 

Since we put the management of our styles on the webpack, adding the minification of class names is now easier than ever.

If you already adhere to any method of creating a namespace, for example, BEM, then translate all css code into isolated styles will be a simple and logical action.

Soon it will be possible to find out that most CSS files use exclusively closed identifiers:

 :local(.backdrop) { … } :local(.root_isCollapsed .backdrop) { … } :local(.field) { … } :local(.field):focus { … } etc.… 

The need for global classes arises only occasionally. Hence the thought arises:

What if all our selectors are closed by default, and the special syntax will be used only if you want to enter a global selector?

What if our code still looks like this:

 .backdrop { … } .root_isCollapsed .backdrop { … } .field { … } .field:focus { … } 

Normally, such names would be too general, but the css-loader solves this problem and makes them visible only within the scope of our module.

And for those cases when the need for global classes cannot be avoided, we can simply use a special :global syntax.

So, for example, an entry using the standard classes added by the ReactCSSTransitionGroup addon will look like:

 .panel :global .transition-active-enter { … } 

This code creates a private .panel identifier that relies on the global class .transition-active-enter

As soon as we thought about how exactly to provide such a syntax in which identifiers will be closed by default, it became clear that this is not so difficult.

PostCSS came to the rescue - a great tool for writing your own CSS converters in the form of plug-ins. For example, the most popular Autoprefixer is initially PostCSS plugin, now used by many as a standalone tool.

Further, the author of the original article briefly describes his experimental library , carrying out the idea. Here is an example of its use . The author’s ideas were later accepted by the community and integrated into the webpack itself. The technology was called CSS Modules , which became part of the css-loader. The pilot project is no longer relevant. The final example of using CSS Modules here

Isolated css classes are just the beginning.

Hey, you fixed css, - tweet

The idea of ​​transferring control over the class names to an automatic build system has enormous potential. You no longer need a man-compiler that manually combines classes for optimization. The build system will do much better.

Common classes that style different components can be generated automatically. Such optimization can be just a tick in the compiler settings.

Starting to use closed CSS, you will understand that there is no way back. It is not so easy to refuse a technique that completely isolates css classes and works in all browsers.

Closed CSS greatly changes the conventional notions of how to organize and name styles in large projects. We are still at the very beginning. The era of closed CSS is just beginning.

Try it yourself to play with CSS Modules. As soon as you see them in action, I am sure that you will agree that this is not an exaggeration - the days of global CSS are coming to an end. The future is modular .



[ Publication - translation. The author of the article is Mark Dalgleish . Link to the original article ]

Source: https://habr.com/ru/post/276417/


All Articles