What are some CSS features that hurt large projects?
- global namespace
- dependency resolution
- search dead code
- lack of constants
- ambiguous result (cascade)
Let's take a look at how we now write CSS on a large project and how we would like to write it in an ideal world.
Take a simple example: a button and its state.
In the reality
Given that the namespace is global, it is necessary to introduce certain class naming conventions to avoid random intersections.
')
In BEM notation, it would look like this:
.button {...} .button_state_disabled {...} .button_state_error {...} .button_state_progress {...}
I think that many would agree that, when they first met BEM, cognitive dissonance was caused by the huge names of the classes, which are the result.
It would be natural to write:
.button {...} .button.disabled {...}
However, in a month, when everyone forgets about this place, the
.disabled class will appear in another file (which would mean something completely different), but here it suddenly breaks down -
the namespace is one .
One could write this:
.button {...} .button-disabled {...}
But then it turns out too much duplication of the code, because the buttons differ in only one style:
.button-disabled should contain everything the same as
.button , but, for example, a different background color.
Now this problem is solved with the use of mixins at the preprocessor level, because in CSS there is no such possibility.
In an ideal world
.button { display: inline-block; padding: 8px 2px; border-radius: 3px; } .button-disabled { composes: button; background-color: gray; }
All selectors are
local within a specific file .
This means that in the button.css file, I write:
.text {...}
And my colleague in the depths of a completely different component:
.text {…}
There are no intersections for
.text - there is no need for special classes for block elements.
The local namespace is also valid for animations declared via
@keyframes .
In the template you do not want to think about the composition of classes of the form
.button.button_state_disabled to get a certain state.
To avoid this, each class must contain everything necessary to draw each state of the component:
.button-disabled { composes: base from "./base.css"; }
The keyword
composes gives me the
mixin functionality.
And I can ask for styles from another file, which gives me CSS modularity.
Reality or fiction
Looks good. What is needed to implement such an interface? Obviously, it is necessary to establish a link between templates and CSS.
It all depends on which template is used. In modern frontend, almost all template engines are javascript applications, the task of which is to turn templates into html.
Imagine that we have a
simple template engine that can only interpolate strings:
<% var styles = require("./button.css") %> <button class="<%=styles.button%>"> </button>
All CSS is exported as an object, the keys of which are clear, semantic, class names for use in a template, and values are those class names that will be in the final markup (for example, unique hashes).
Now this can be done using a
webpack plugin or a
browserify plugin .
A more modern, real-world example is in the reactjs component pattern:
import { Component } from 'react'; import styles from './button.css'; export default class button extends Component { render() { let className = styles.button let text = " " if (this.state.loading) { className = styles.buttonDisabled } return <button className={className}>{text}</button> } }
What to read
It seems that the apparent movement began
with the report "CSS in JS"Article
CSS modules: welcome to the future . Before reading, open the source code, look at the compiled class names: beauty! :)
An organization on a githaba where
guys are storming the modularity theme in CSS. Here is the documentation: examples, concepts, and specific tools: postcss, browserify, and webpack plugins.
Report of Pavel Lovtsevich at the latest WSD (
slides )