Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?

Proposal: Importable Constructable Stylesheets

We're getting Constructable Stylesheets. This seems like an intuitive value to obtain when importing CSS from JavaScript, since it's the DOM's representation of a Stylesheet:

import stylesheet from './style.css';
console.log(stylesheet);  // CSSStyleSheet

No such system is in place to allow this to work (see whatwg/loader), however frontend build tooling has congregated around this approach as a mechanism for bringing CSS assets into the JavaScript module graph. There are many benefits to be obtained from moving CSS into this graph, however the most important is that imported CSS can be attributed to the consuming JS Module. This allows it to be bundled, optimized, and potentially dead-code-eliminated leveraging static analysis performed on the surrounding module graph.

However, given the existence of CSSStyleSheet as a 1:1 representation of a stylesheet, it's possible we now have a case for why CSS deserves special treatment.

import sheet from './style.css';

// global CSS:
document.adoptedStyleSheets = [sheet];

const node = document.createElement('div');
const shadow = node.attachShadow({ mode: 'open' });
// scoped CSS:
shadow.adoptedStyleSheets = [sheet];

// Updates! (propagates out to all affected trees)
sheet.insertRule('.foo { color: red; }');

// "hot CSS replacement":
module.hot.accept('style.css', req => {
  sheet.replace(req('style.css'));
});
// in your ServiceWorker: importScripts('polyfill.js')
addEventListener('fetch', e => {
if (e.request.destination === 'script' && e.request.url.endsWith('.css')) {
let res;
e.respondWith(
fetch(e.request)
.then(r => (res = r).text())
.then(css => {
const headers = new Headers(res.headers);
headers.set('content-type', 'application/javascript');
headers.delete('content-length');
return new Response(`
let s = new CSSStyleSheet();
s.replaceSync(${JSON.stringify(css)});
export default s;
`, { headers });
})
);
}
});
@developit

This comment has been minimized.

Copy link
Owner Author

developit commented Jan 31, 2019

@littledan

This comment has been minimized.

Copy link

littledan commented Mar 6, 2019

Impressive! I'm excited for CSS imports. Nit: Is this form of the CSSStyleSheet constructor, of being called with a string argument, standards-track? I just see an options bag as the argument in the draft spec, and calls without arguments in the explainer.

@developit

This comment has been minimized.

Copy link
Owner Author

developit commented Apr 18, 2019

@littledan - oops, that was a mistake! Fixed.

@Jack-Works

This comment has been minimized.

Copy link

Jack-Works commented Sep 7, 2019

https://github.com/Jack-Works/loader-with-esmodule-example I made an example (not follow the spec so strict)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.