Without modifiers, props or utilities #
In Making components with CSS nesting, we looked at different ways to make a keyword list component with and without links. If we want to make that even more reusable, there are also multiple ways to make variants.
With BEM, we have the modifier class. With JS components, we can set a prop and build logic that outputs the correct variant. And with utilities, we can add a class or five on top of what we have. CSS nesting gives us another option, context variants.
Context awareness with CSS nesting #
Let’s say we have a local sub-navigation similar to our keywords component. For simplicity’s sake, only size and font size are different, which means we can turn the existing component into one that can be used for both types by letting context handle the variations.
First, we change the name from .keywords to .multilist, for lack of a better word, and remove the flex properties from ul and li because they will be different in each context.
Keywords context
We set the keywords area as a div with room for a heading and other elements besides the list.
<div class="keywords">
[heading, etc …]
<ul class="multilist">
[…]
</ul>
</div>
Then, in the keywords CSS, keeping it simple for the example, we set the flex wrap and size.
.keywords {
.multilist {
flex-flow: row wrap;
& li {
flex: 0 1 auto;
}
}
}
We don’t have to use the list’s class as a selector, we could use the element, but I find referencing the name makes it easier to see the connection in case we think about modifying or deleting the original component.
Sub-navigation context
For the sub-navigation, we change the font size and stack the list items. The HTML is only slightly different from the keywords context. Again, skipping code not relevant for the example.
<nav class="subnav">
<ul class="multilist">
[…]
</ul>
</nav>
.subnav {
.multilist {
font-size: 1.17rem;
flex-direction: column;
}
}
We set the font size on the ancestor to utilise possible em units for spacing in the descendants.
Calling CSS with arguments #
Defining part of a class in another place and file might seem messy, but we’re not re-defining the component, we’re using it with context values. In terms of other programming languages, I like to think of it as calling a function with different parameters and not redeclaring a variable. We don’t have to add anything else to the HTML or write more classes. And if we delete one of the variants, the component doesn’t require any cleanup; we won’t be left with variant code that we’re unsure is in use.
Another example is CSS grid’s grid-template-areas. If we have a component or partial template, we can set the grid-area by nesting its selector inside the contextual ancestor; no need to make an additional container with a contextual classes.
Three contexts of CSS #
The form of nesting we have looked at fits well with approaching CSS as a way to work on style and layout in three contexts:
- We style at the element level, which is scoped like when we work on the actual component.
- Style is also affected through the cascade, which is the larger context. Even though we work on elements, it can and should have consequences for all those instances included in the selector. In our case, the list and link elements would also have some global base CSS outside the component.
- We program layout in a combined context, mainly on the parent element, but with full regard to the child elements, where we can make individual adjustments if needed.
Selector is not element #
The bigger picture and the cascade are what most struggle with, and some libraries seem oblivious to. I believe it’s because we are seemingly working on singular elements, leading to approaches that default to strict scoping where HTML and CSS are treated as different levels of the same structure. But they are not; we are constantly working on a bigger scope in CSS. Even if the selector is unique and what we write is not affecting anything else, there is almost always code further up that affects our element. Nesting can make more of us aware of these connections.
You can fully write CSS without nesting, it’s fairly new and not considered necessary knowledge to produce good interfaces. At the time of writing, I don’t recommend shipping nesting code. While it’s getting closer to complete browser support, it requires transpiling for a while still. I use PostCSS with the postcss-nesting plugin.