This repository has been archived by the owner on Mar 14, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Moving the constructable stylesheets post to web.dev (#5968)
* Moving the constructable stylesheets post to web.dev * Update links to point to CSSOM draft spec * Update src/site/content/en/blog/constructable-stylesheets/index.md * Update src/site/content/en/blog/constructable-stylesheets/index.md * Apply suggestions from code review * Apply suggestions from code review Co-authored-by: Jason Miller <[email protected]>
- Loading branch information
1 parent
49f280e
commit 167a65b
Showing
1 changed file
with
181 additions
and
0 deletions.
There are no files selected for viewing
181 changes: 181 additions & 0 deletions
181
src/site/content/en/blog/constructable-stylesheets/index.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
--- | ||
layout: post | ||
title: Constructable Stylesheets | ||
subhead: Seamless reusable styles. | ||
authors: | ||
- developit | ||
date: 2019-02-08 | ||
updated: 2022-02-23 | ||
description: Constructable Stylesheets provide a seamless way to create and distribute styles to documents or shadow roots without worrying about FOUC. | ||
tags: | ||
- blog | ||
- css | ||
--- | ||
|
||
[Constructable Stylesheets](https://wicg.github.io/construct-stylesheets) are a | ||
way to create and distribute reusable styles when using [Shadow | ||
DOM](https://developers.google.com/web/fundamentals/web-components/shadowdom). | ||
|
||
{% Aside %} | ||
Constructable Stylesheets are available in Chromium | ||
(versions 73 and higher), and in Firefox from version 75 behind the | ||
`layout.css.constructable-stylesheets.enabled` flag. | ||
{% endAside %} | ||
|
||
It has always been possible to create stylesheets using JavaScript. However, the | ||
process has historically been to create a `<style>` element using | ||
`document.createElement('style')`, and then access its sheet property to obtain | ||
a reference to the underlying | ||
[CSSStyleSheet](https://developer.mozilla.org/docs/Web/API/CSSStyleSheet) | ||
instance. This method can produce duplicate CSS code and its attendant bloat, | ||
and the act of attaching leads to a flash of unstyled content whether there is | ||
bloat or not. The `CSSStyleSheet` interface is the root of a collection of CSS | ||
representation interfaces referred to as the | ||
[CSSOM](https://developer.mozilla.org/docs/Web/API/CSS_Object_Model), | ||
offering a programmatic way to manipulate stylesheets as well as eliminating the | ||
problems associated with the old method. | ||
|
||
{% Img | ||
src="image/ZDZVuXt6QqfXtxkpXcPGfnygYjd2/ZGONaG0tsbRHWzCZpAlo.png", | ||
alt="Diagram showing preparation and application of CSS.", | ||
width="800", height="610" %} | ||
|
||
Constructable Stylesheets make it possible to define and prepare shared CSS | ||
styles, and then apply those styles to multiple Shadow Roots or the Document | ||
easily and without duplication. Updates to a shared CSSStyleSheet are applied to | ||
all roots into which it has been adopted, and [adopting a | ||
stylesheet](https://wicg.github.io/construct-stylesheets/#using-constructed-stylesheets) | ||
is fast and synchronous once the sheet has been loaded. | ||
|
||
The association set up by Constructable Stylesheets lends itself well to a | ||
number of different applications. It can be used to provide a centralized theme | ||
used by many components: the theme can be a `CSSStyleSheet` instance passed to | ||
components, with updates to the theme propagating out to components | ||
automatically. It can be used to distribute [CSS Custom | ||
Property](https://developer.mozilla.org/docs/Web/CSS/--*) values to | ||
specific DOM subtrees without relying on the | ||
[cascade](https://developer.mozilla.org/docs/Web/CSS/Cascade). It can even | ||
be used as a direct interface to the browser's CSS parser, making it easy to | ||
preload stylesheets without injecting them into the DOM. | ||
|
||
## Constructing a stylesheet | ||
|
||
Rather than introducing a new API to accomplish this, the [Constructable | ||
StyleSheets](https://www.w3.org/TR/cssom-1/#dom-cssstylesheet-cssstylesheet) | ||
specification makes it possible to create stylesheets imperatively by invoking | ||
the `CSSStyleSheet()` constructor. The resulting CSSStyleSheet object has two | ||
new methods that make it safer to add and update stylesheet rules without | ||
triggering [Flash of Unstyled | ||
Content](https://en.wikipedia.org/wiki/Flash_of_unstyled_content) (FOUC). | ||
The | ||
[`replace()`](https://developer.mozilla.org/docs/Web/API/CSSStyleSheet/replace) | ||
and | ||
[`replaceSync()`](https://developer.mozilla.org/docs/Web/API/CSSStyleSheet/replaceSync) | ||
methods both replace the stylesheet with a string of CSS, and `replace()` | ||
returns a Promise. In both cases, external stylesheet references are not | ||
supported—any `@import` rules are ignored and will produce a warning. | ||
|
||
```js | ||
const sheet = new CSSStyleSheet(); | ||
|
||
// replace all styles synchronously: | ||
sheet.replaceSync('a { color: red; }'); | ||
|
||
// replace all styles: | ||
sheet.replace('a { color: blue; }') | ||
.then(() => { | ||
console.log('Styles replaced'); | ||
}) | ||
.catch(err => { | ||
console.error('Failed to replace styles:', err); | ||
}); | ||
|
||
// Any @import rules are ignored. | ||
// Both of these still apply the a{} style: | ||
sheet.replaceSync('@import url("styles.css"); a { color: red; }'); | ||
sheet.replace('@import url("styles.css"); a { color: red; }'); | ||
// Console warning: "@import rules are not allowed here..." | ||
``` | ||
|
||
{% Aside 'gotchas' %} | ||
In earlier versions of the specification, | ||
`replace()` allowed `@import` rules and returned | ||
a Promise that resolved when these were finished loading. This feature was | ||
[removed from the specification](https://github.com/WICG/construct-stylesheets/issues/119#issuecomment-642300024) | ||
and `@import` rules are | ||
ignored with a warning as of Chrome 84. | ||
{% endAside %} | ||
|
||
## Using constructed stylesheets | ||
|
||
The second new feature introduced by Constructable StyleSheets is an | ||
[adoptedStyleSheets](https://www.w3.org/TR/cssom-1/#extensions-to-the-document-or-shadow-root-interface) | ||
property available on [Shadow | ||
Roots](https://developer.mozilla.org/docs/Web/Web_Components/Using_shadow_DOM) | ||
and [Documents](https://developer.mozilla.org/en/docs/Web/API/Document). This | ||
lets us explicitly apply the styles defined by a `CSSStyleSheet` to a given DOM | ||
subtree. To do so, we set the property to an array of one or more stylesheets to | ||
apply to that element. | ||
|
||
```js | ||
// Create our shared stylesheet: | ||
const sheet = new CSSStyleSheet(); | ||
sheet.replaceSync('a { color: red; }'); | ||
|
||
// Apply the stylesheet to a document: | ||
document.adoptedStyleSheets = [sheet]; | ||
|
||
// Apply the stylesheet to a Shadow Root: | ||
const node = document.createElement('div'); | ||
const shadow = node.attachShadow({ mode: 'open' }); | ||
shadow.adoptedStyleSheets = [sheet]; | ||
``` | ||
|
||
Notice that we're overriding the value of `adoptedStyleSheets` instead of | ||
changing the array in place. This is required because the array is frozen; | ||
in-place mutations like `push()` throw an exception, so we have to assign a new | ||
array. To preserve any existing StyleSheets added via `adoptedStyleSheets`, we | ||
can use concat to create a new array that includes the existing sheets as well | ||
as additional ones to add: | ||
|
||
```js | ||
const sheet = new CSSStyleSheet(); | ||
sheet.replaceSync('a { color: red; }'); | ||
|
||
// Combine existing sheets with our new one: | ||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet]; | ||
``` | ||
|
||
## Putting it all together | ||
|
||
With Constructable StyleSheets, web developers now have an explicit solution for | ||
creating CSS StyleSheets and applying them to DOM trees. We have a new | ||
Promise-based API for loading StyleSheets from a string of CSS source that uses | ||
the browser's built-in parser and loading semantics. Finally, we have a | ||
mechanism for applying stylesheet updates to all usages of a StyleSheet, | ||
simplifying things like theme changes and color preferences. | ||
|
||
{% Video src="video/ZDZVuXt6QqfXtxkpXcPGfnygYjd2/0wKHfcRTB7tn9vWkrqcU.mp4", | ||
autoplay=true, | ||
playsinline=true %} | ||
|
||
**[View Demo](https://construct-stylesheets.glitch.me/)** | ||
|
||
## Looking ahead | ||
|
||
The initial version of Constructable Stylesheets shipped with the API | ||
described here, but there's work underway to make things easier to use. There's | ||
a [proposal](https://github.com/WICG/construct-stylesheets/issues/45) to extend | ||
the `adoptedStyleSheets` FrozenArray with dedicated methods for inserting and | ||
removing stylesheets, which would obviate the need for array cloning and avoid | ||
potential duplicate stylesheet references. | ||
|
||
## More information | ||
|
||
* [Chrome Platform Status](https://www.chromestatus.com/feature/5394843094220800) | ||
* [Example](https://construct-stylesheets.glitch.me/) | ||
* [API](https://wicg.github.io/construct-stylesheets/) | ||
* [Explainer](https://github.com/WICG/construct-stylesheets/blob/gh-pages/explainer.md) | ||
* [Intent to Implement](https://groups.google.com/a/chromium.org/d/topic/blink-dev/irhrlr6n5YQ/discussion) | ||
* [Intent to Ship](https://groups.google.com/a/chromium.org/d/topic/blink-dev/gL2EVBzO5og/discussion) | ||
* [Discourse](https://discourse.wicg.io/t/proposal-constructable-stylesheet-objects/2572) |