Skip to content

Cascading Style Sheet module scripts #759

@justinfagnani

Description

@justinfagnani

In addition to HTML Modules, the ability to load CSS into a component definition is an important capability that we're currently lacking. Loading CSS is probably a more important use case judging by the popularity of CSS loaders in JavaScript bundlers.

Currently, styles are usually either defined inline with HTML templating or JSX or loaded with various JS Bundler CSS Loaders. The CSS loaders often have global side-effects like appending a <style> tag to the document, which does not generally work well with the style scoping of Shadow DOM.

I propose that we add Cascading Style Sheet module scripts to the platform, allowing CSS to be imported directly by JavaScript:

import styles from './styles.css';

Exports

The semantics for Cascading Style Sheet module scripts can be very simple, and combined with Constructable Stylesheets allow the importer to determine how the CSS should be applied to the document.

To start with, the only export of a Cascading Style Sheet module script would be a default export of the CSSStyleSheet object. This can then simply be added to document.styles or shadowRoot.styles:

import styles from './styles.css';

class MyElement extends HTMLElement {
  constructor() {
    this.attachShadow({mode: open});
    this.shadowRoot.adoptedStyleSheets = [styles];
  }
}

Additional Features

Other userland CSS module systems often have more features, like the ability to import or export symbols that are defined in the module. ie:

import (LitElement, html} from 'lit-element';
import styles from './styles.css';

class MyElement extends LitElement {
  constructor() {
    this.attachShadow({mode: open});
    this.shadowRoot.adoptedStyleSheets = [styles];
  }

  render() {
    return html`<div class=${styles.classes.exampleClass}></div>`;
  }
}

These features may be very useful, but they can be considered for addition to the CSSOM itself so that they're exposed on CSSStyleSheet and available to both Cascading Style Sheet module scripts and styles loaded via <style> and <link> tags, or constructed from a string.

Polyfilling

It's not easy to polyfill a new module type, but build-time tools can create JavaScript modules that adhere to the Cascading Style Sheet module script interface.

.exampleClass {
  color: red;
}

Can be compiled to:

// create a container and scope to hold a style sheet:
const container = document.createElement('div');
const shadowRoot = container.attachShadow({mode: 'open'});

// create a <style> element to add css text to
let styleElement = document.createElement('style');

// add the styles
styleElement.textContent = String.raw`
.exampleClass {
  color: red;
}
`;

// add the <style> to the document so it creates a StyleSheet
shadowRoot.appendChild(styleElement);

const stylesheet = styleElement.sheet;
export default stylesheet;

edit: updated to the actually Constructible StyleSheet API.
edit: updated to refer to the feature by "Cascading Style Sheet module script"

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions