CSS for an encapsulated web
Tags: css, encapsulation, components, webdev
Published: 2017-10-11 21:53:17

---
title: CSS for an encapsulated web
published: true
description: How to (d)evolve from convoluted coding conventions to simpler rule definitions
tags: css, encapsulation, components, webdev
---

> *Or, how to (d)evolve from convoluted coding conventions to simpler rule definitions.*

CSS is 20 years old, hooray!

And we're still struggling with the same old problems of CSS at scale. Boo.

Things are changing, though. To be fair, the spec for a real change came a while ago already, but this kind of things take their time to become standard and widespread.


## The problem

The biggest concern about Cascading Style Sheets is... the cascade. In short, a plethora of rules that could all influence each other, also because they've all a *global* scope. It soon became evident that things like these don't work very well:

Code                             | Effect
---------------------------------|--------------
`.content { ... }`               | "Wait, I didn't add this border radius here, what's happening?" "Oh. Oh, yeah, I've already defined a `content` class somewhere else, sorry."
`#login .header { ... }`         | "Why is this all unstyled?" "Yeah, well, you have to keep that `login` ID" "But this isn't the login page!" "Uh, well, I guess..."
`article > section > p { ... }`  | "This isn't the right style for the paragraph, why?" "See, I had to wrap the `section` in a `div` here..."
`z-index: 99999999999;`          | "How many 9's should I put here again?"
`color: #fff !important;`        | "Great, now I can't change the color here!"
`-webkit-text-fill-color: #000;` | "Seriously, dude?"

Name clashing. Specificty races. Unimportant declaration marked as `!important` out of frustration. Styles that spill everywhere. Even differences among the browsers didn't help. At all.

## The first solutions

As web pages and applications grew in complexity, some best practices emerged:

* no more ID's in selectors;
* no "brute-force" values (as that `z-index`);
* no more `!important` or exotic CSS properties;
* no strict bindings between CSS and the DOM structure;
* keep the specifity as low as possible, ideally 0.1.0;
* use namespaces for your classes.

It still wasn't enough, so CSS - the simplest of all web languages - got its coding style guides too. Conventions like OOCSS, SMACSS, Atomic CSS and BEM were born. The last is arguably the most successful one, so I'll refer to it during the article, although the same concepts apply more or less for the others.

## Scoping the style

Let's say we have our [card prototype](https://codepen.io/MaxArt2501/pen/yowYwq):

![A common picture card, with header, image, description a buttons](https://thepracticaldev.s3.amazonaws.com/i/gu9a9g8onedriz74gh4i.png)

And we back this design using this HTML structure:

```html
<div class="content-card">
  <header class="content-card__header">...</header>
  <picture class="content-card__picture">
    <img src="..." class="content-card__image">
  </picture>
  <section class="content-card__abstract">
    <p>...</p>
  </section>
  <footer class="content-card__footer">
    <button type="button" class="content-card__favorite">☆</button>
    <nav class="content-card__actions">
      <a href="#" class="content-card__action">Read later</a>
      <a href="#" class="content-card__action content-card__action--view">View</a>
    </nav>
  </footer>
</div>
    
```

Behold the code in all its glorious BEM verbosity!

Using BEM allowed us to name our classes clearly, without caring (much) about name clashing, specificity headaches and all the rest. But it came at the cost of lengthy class names, not always easy to read. Even using preprocessor rule nesting tricks like this one has its disavantages:

```scss
.content-card {
  ...
  &__action {
    ...
    &--view {
      ...
    }
  }
}
```

Now, if I want to track down the rule for the class `content-card__action`, I can't just copy the name and paste in the search box of my editor of choice. Not really a deal breaker but still annoying.


## Scoped styles

What we really need is to have a consistent - possibly native - system to apply our styles *locally*. BEM shows the way to do it but it's still a *manual* operation, which means it's prone to errors.

One of the first native approaches (back in 2012) to the problem came in the form of the [`scoped` attribute](https://css-tricks.com/saving-the-day-with-scoped-css/) of `<style>` elements:

```html
<div>
  <style scoped>
    div { width: 30em; }
    p { color: #333; }
  <style>
  <p>I look black but I'm actually nicer</p>
</div>
<p>I'm pitch black</p>
```

Thanks to that, the rules defined inside affect only on the `<style>`'s parent and all of its descendants. While it seemed cool and right on track, it didn't gain much momentum (Chrome removed its support on v36), because it didn't solve the other, big problem of *shielding* our styles from external influences.

In short, global and parent scoped style rules can still override our local ones.


## The (almost) definitive solution

When web development became aware that we need to split our interfaces into *components* and develop with this concept in mind, the path was set - although not easy to follow.

Web Components are a concept that actually predates scoped styles, as they've been introduced in 2011. But they had a long journey before a decently widespread adoption, and also API maturity. But together with the concept of [Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Shadow_DOM), it came the *encapsulated style sheet*.

This means that not only our component's style sheet with have a local effect, but also that external one will have *no effect to our component!*

The main implication of this is that we have *no need for a namespace* for our classes, as there's no risk of name clashing or style spilling anymore. In short, we can [reduce the HTML](https://codepen.io/MaxArt2501/pen/YxgwYV) for our picture card like this:

```html
<div class="content-card">
  <header class="header">...</header>
  <picture class="picture">
    <img src="..." class="image">
  </picture>
  <section class="abstract">
    <p>...</p>
  </section>
  <footer class="footer">
    <button type="button" class="favorite">☆</button>
    <nav class="actions">
      <a href="#" class="action">Read later</a>
      <a href="#" class="action action--view">View</a>
    </nav>
  </footer>
</div>
```

That's already much better! But we can do even more if we fully take advantage of the encapsulation. This means that we can also ditch the convention of using only selectors of specifity 0.1.0 for our rules.

Should we avoid using classes, then? Not really: we can *use them only when necessary*. Because not only our style sheets are now encapsulated, but the whole concept of *componentization* of our interface naturally brings the habit of writing *small things*: small JavaScript modules, small HTML and small CSS.

This implies that our semantically chosen HTML elements are possibily *the only ones* in the whole component: we'll presumably have only one `header`, one `footer`, or maybe multiple `li` but for just one type of element.

In the end we can [shrink our markup even further](https://codepen.io/MaxArt2501/pen/prYgKB):

```html
<content-card>
  <header>...</header>
  <picture>
    <img src="...">
  </picture>
  <section>
    <p>...</p>
  </section>
  <footer>
    <button type="button">☆</button>
    <nav>
      <a href="#">Read later</a>
      <a href="#" class="view">View</a>
    </nav>
  </footer>
</content-card>
```

Now this is even better, readable and clear. We even shaved some meaningful bytes from the payload. And if the semantic meaning (for the developers) has vanished, we can use ID's again, as they're encapsulated too.


## But I *want* to alter a component's appearance from the parent!

It's a legitimate need, no doubt. Unofrtunately this issue still lacks a definitve solution.

There has been a proposal to introduce a ["shadow-piercing" descendant combinator](https://github.com/w3c/webcomponents/wiki/Shadow-piercing-Combinators-in-the-Wild) in CSS (the `>>>` or `/deep/` combinator), but [has been deprecated](https://www.w3.org/wiki/Webapps/WebComponentsApril2015Meeting) since then as it's been considered as "too powerful" for the intent. Indeed, it would have reduced CSS encapsulation to mere scoped CSS.

The only way to change a custom element's appearance is by using CSS "variables" (or better ["custom properties"](https://developer.mozilla.org/en-US/docs/Web/CSS/--*)):

```css
/* styles.css */
:root {
  --base-button-color: rebeccapurple;
}

/* base-button's style */
button {
  background-color: var(--base-button-color, midnightblue);
}
```

This is a little bit inconvenient as it's deemed too "fine-grained", which also could lead to name clashing (again!) when it comes to naming said custom properties. This is why Tab Atkins proposed the `@apply` rule, that essentially meant *mixins in CSS*. While that sounded great, it didn't solve other problems in the matter and the proposal has been [abandoned by its champion](http://www.xanthir.com/b4o00) and we won't probably see it spec'ed.

The linked article mentions the `::part` and `::theme` pseudo-elements that could finally solve the problem, but there's still quite some road to walk. In the end, we have to stick to custom properties for now.


## How can I use encapsulated CSS though?

Web Components have a complex API that might put off some developers. That's why a library like [Polymer](https://www.polymer-project.org/) has been born. Even if Polymer or even Web Components are out of question for your development needs, there are frameworks that let you take advantage of encapsulation for styles.

When Web Components or even just Shadow DOM are ruled out, style encapsulation can be emulated. This usually means generating an attribute with a random name for each component, and attach it to *every* style rule and DOM element of the component. So, starting with the following markup and style sheet:

```html
<div>
  <h2>Hello!</h2>
  <p>I will get a random attribute even though I'm unstyled</p>
</div>
```
```css
h2 { font-size: 150%; }
```

They will be translated into something like this when processed:

```html
<!-- Inside the <head> -->
<style type="text/css">
  h2[data-v-4c74d97c] { font-size: 150%; }
</style>

<!-- In the page -->
<div data-v-4c74d97c>
  <h2 data-v-4c74d97c>Hello!</h2>
  <p data-v-4c74d97c>I will get a random attribute even though I'm unstyled</p>
</div>
```

Another component will get a different generated attribute, and that's how style rules get scoped.

Emulating style encapsulation is slower when it comes to applying the styles, but it won't probably affect your page significatively. So let's see how the most common front-end libraries handle style encapsulation.


### Vue

[Vue](https://vuejs.org/) *per se* is agnostic when it comes to CSS, but Vue as an ecosystem offers vue-loader, a loader for Webpack that leverages on a PostCSS plugin to achieve emulated encapsulation. The loader gets included using [vue-cli](https://github.com/vuejs/vue-cli) with a Webpack-based scaffolding template.

In order to encapsulate your styles, you have to define a component's style using the [`<style>` with the `scoped` attribute](https://vue-loader.vuejs.org/en/features/scoped-css.html). Now this is a bit inconvenient, as it's the same syntax from the old spec for element-scoped styles, *without* having the same effect.

In fact, descendant components will *not* be styled by the scoped style, opposed to the original proposal of `<style scoped>`, because they will miss the generated attribute defined for the ancestor. But still, encapsulation.

### Angular

[Angular](https://angular.io/) is more sophisticated when it comes to encapsulation, as it [let us decide](https://angular.io/guide/component-styles#view-encapsulation) among no encapsulation at all, emulated (which is the default one) and even taking advantage of Shadow DOM's native encapsulation. I guess this is possible also because Angular does *not* replace the host element from the page, *de facto* replicating what Web Components do.

### React/Preact

Although [React](https://reactjs.org/) and its lesser known (but not less powerful) alternatives like [Preact](https://preactjs.com/) don't offer native solutions for CSS, there's a plethora of libraries that allow to style our components, from CSS Modules to Styled Components, from Glamor to JSXStyle.

These libraries are all part of the great CSS-in-JS subject, and they all are based on the concept of encapsulated style, although mostly based on an emulated system.


## Conclusions

CSS comes from a long way. It's probably one of the simplest language around, it's simple to use but this doesn't mean it's simple to *manage*. With the advent of large-scale web applications, the issue became evident, but solutions didn't.

After long, struggling years, with style encapsulation we've managed to scale down the problem dramatically. Not only it seems a step in the right direction, but it *feels* like it. So it doesn't matter what library will you use for your next project, the advice is to choose one that allow style encapsulation.