A Quick Look at Constructable Stylesheets
Tags: css, javascript, webdev
Published: 2019-04-05 21:29:37

---
title: A Quick Look at Constructable Stylesheets
published: true
tags: css, javascript, webdev
description: Constructable Stylesheets are coming (actually they're already here) and they're going to change "the game". So I decided to write about it.
---

"Constructable Stylesheets". This might be the first time you're hearing of this and you must be thinking "what the flux is that?", and that's fine, that was my reaction when I first heard of it too.

## What are Constructable Stylesheets?

Simply put, Constructable Stylesheets are a way to create and distribute reusable styles when working with the **Shadow DOM**.

## What is the Shadow DOM?

To understand how Constructable Stylesheets work, we need to understand what the Shadow DOM is and to do that we need to understand what the **DOM** is.

The **DOM** which stands for **Document Object Model** is a representation of an HTML document, it is used in Javascript to modify a page's content and is also utilized by browsers to figure out what to render on a page.

The **Shadow DOM** is a DOM within "**The DOM**". It is a completely separate DOM tree from "**The DOM**" with its own elements and styling. It was created for the purpose of encapsulation and most applications of the Shadow DOM revolve around creating complex components/elements in such a way that the styling of these components/elements is unaffected by other style rules in "**The DOM**".
A good example of this is Ionic 4 UI components.

To better understand how the DOM and Shadow DOM works, here is an article [What is the Shadow DOM](https://bitsofco.de/what-is-the-shadow-dom "What is the Shadow DOM") by Ire Aderinokun.

## Why Constructable Stylesheets?

"Why do we need a new way of creating stylesheets?" you might ask. I asked the same question too. As we all might know, we have always created/been able to create stylesheets on the fly using Javascript like this:

```javascript
const style = document.createElement('style');
```

and obtain a reference to the underlying **CssStyleSheet** instance by accessing the sheet property.![Accessing the sheet property](https://thepracticaldev.s3.amazonaws.com/i/fy30yf3tttzn6kxggbfv.gif)

This method works quite alright, but it has a few downsides, some of which are:

- It can result in duplicate CSS code and thus cause CSS bloat.

##### What is CSS Bloat?

  CSS bloat is unnecessarily repeated CSS code and while it directly doesn't affect your performance it indirectly affects your performance as having redundant selectors and rules increases your bundle size and makes your page heavier to load and slow to render.

- It can lead to FOUC.

##### What is FOUC?

  FOUC - Flash of Unstyled Content is a scenario where the content web page loads un-styled briefly then shortly after appears styled. This occurs when the browser renders the page before completely loading all the required assets.
  FOUC can be caused by having duplicate CSS code (CSS bloat) which in turn causes a larger and heavier bundle which is slow to render.

The aforementioned problems are easily solved by using Constructable Stylesheets.

## How to use Constructable Stylesheets

#### Creating a Stylesheet

To create a stylesheet according to the Constructable Stylesheets specification, we do by invoking the CSSStyleSheet() constructor.

```javascript
const sheet = new CSSStyleSheet();
```

The resulting object, in this case, **sheet** has two methods that we can use to add and update stylesheet rules without the risk of FOUC. These methods both take a single argument which is a string of style rules.

These methods are:

- replace(): This method allows the use of external references i.e. **@import** in addition to CSS rules and it returns a promise that resolves once any imports are loaded.

  ```javascript
  sheet.replace('@import url("app.css"); p { color: #a1a1a1 }').then(sheet => {
      console.log('Imports added and styles added');
  }).catch(error => {
      console.error('Error adding styles: ', error)
  });
  ```

- replaceSync(): This method doesn't allow **@import**, only CSS Rules.

  ```javascript
  // this works
  sheet.replaceSync('p { color: #a1a1a1 }');
  
  // this throws an exception
  try {
      sheet.replaceSync('@import url("app.css"); p { color: #a1a1a1 }');
  } catch(error) => {
      console.error(error);
  }
  ```

#### Using a Constructed Stylesheet

After creating a stylesheet, we'd like to use it, of course. We use created stylesheets by using the **adoptedStyleSheets** property which Documents and Shadow DOMs possess.
This property lets us explicitly apply the styles we have defined in our Constructed Stylesheet to a DOM subtree by setting the value of this **adoptedStyleSheets**  property to an array of stylesheets. 

```javascript
// applying the earlier created stylesheet to a document
document.adoptedStyleSheets = [sheet];

// creating an element and applying stylesheet to its shadow root
const el = document.createElement('div');
const shadowRoot = el.attachShadow({ mode: open });
shadowRoot.adoptedStyleSheets = [sheet];
```

We can also create new stylesheets and add them to the adoptedStyleSheets property. 
Now normally, since the property is an array, using mutations like **push()** would be the way to go. However, in this case, it's not so. 

This is because the adoptedStyleSheets property array is frozen and therefore in-place mutations like **push()** won't work.

##### When is an array said to be frozen?

A frozen array is an array which has been frozen as an object via the Object.freeze() method. The Object.freeze() method "freezes" an object which prevents new properties from being added to it, prevents the values of existing properties from being changed and also prevents the object's prototype from being changed.

##### What is an in-place mutation?

The term "**in-place**" is used to describe an algorithm that transforms the input given to it without using any additional data structure. While an algorithm that utilizes an additional data structure in transforming input is said to be **out-of-place**/**not-in-place**.

Consider the following methods, both to reverse the order of an array:

P.S: This is just for explanation purposes.

```javascript
const reverseOutOfPlace = (input) => {
  const output = [];
  input.forEach((element, index) => {
    output[index] = input[input.length - (index + 1)];
  })
  return output;
}

const reverseInPlace = (input) => {
  const len = input.length;
  for(let i = 0; i <= Math.floor((len-2)/2); i++) {
    const temp = input[i];
    input[i] = input[len - 1 - i];
    input[len - 1 - i] = temp;
  }
  return input;
}
```

They both reverse the order of a given array, however, the **reverseOutOfPlace** method uses an additional array **output** to reverse the input while the **reverseInPlace** method doesn't use any additional arrays, as such the former is **out-of-place** while the latter is **in-place**.

Array [mutation] methods like **pop** and **push** are **in-place** because they don't use any additional arrays while others like **concat** and **map** are **out-of-place** because they use additional arrays in transforming the input array.



Since the adoptedStyleSheets property array is frozen and the values of its properties cannot be changed, the best way to add new stylesheets to the array is using **concat()** or the **spread** operator

```javascript
const newSheet = new CSSStyleSheet();
newSheet.replaceSync('p { color: #eaeaea }');

// using concat
shadowRoot.adoptedStyleSheets = shadowRoot.adoptedStyleSheets.concat(newSheet);

// using the spread operator
shadowRoot.adoptedStyleSheets = [...shadowRoot.adoptedStyleSheets, newSheet]
```



## What can I do with Constructable Stylesheets

Constructable Stylesheets has wide possibilities of usages, below are some of them:

- Create shared CSS styles on the fly and apply them to the document or multiple Shadow roots without CSS bloat.
- When a shared CSSStyleSheet has been applied to elements, any updates to it reflect on all the elements that it has been applied to. This can be used to implement hot replacement of styles within Shadow DOMs.![Hot replacement of styles](https://thepracticaldev.s3.amazonaws.com/i/x1iluonyee98ewcp9iy4.gif)

- Change CSS custom properties on the fly for specific DOM subtrees.
- Create a central theme that is used by/applied to several components.
- As a direct interface to the browser's parser to preload stylesheets.

[Here is a pen](https://codepen.io/Overrideveloper/pen/zXBqOG) I made that shows most of what is in this post.

For more information on Constructable StyleSheets, check out these posts: [Constructable Stylesheets: Seamless reusable styles](https://developers.google.com/web/updates/2019/02/constructable-stylesheets "Constructible Stylesheets: Seamless reusable styles")  and [Constructable Stylesheet Objects](https://wicg.github.io/construct-stylesheets "Constructable Stylesheet Objects").

P.S: At the time of writing this article, Constructable StyleSheets has only shipped to Chrome, so the aforementioned pen will work on only chromium-based browsers.