5

What's the easiest way to use a large css library (like bootstrap) in web components / shadowDOM app using LitElement?
Tried the following:

  1. Use the link tag within the component. Works, but creates FOUC (flash of unstyled content).
  2. Render everything to Light DOM (I'm using LitElement and they have a createRenderRoot() override. Works as well, but as the app gets more complex, maintaining component document isolation would be nice.

Looking for the simplest way to just use boostrap in this setting.

user3757849
  • 199
  • 2
  • 14

1 Answers1

6

LitElement's recommended way to add styles to components is via the styles property. Loading external .css files this way is not straightforward, but there are some solutions.

The import way

If your definition of "simplest way" comprises using transpilers or module bundlers then you can use non-js inlined imports to accomplish something like the following:

import bootstrap from './path/to/bootstrap.css';
// ...

class MyElement extends LitElement {

  static styles = bootstrap; // If your build system already converted
                             // the stylesheet to a CSSResult

  static styles = unsafeCss(bootstrap); // If bootstrap is plain text

}

There are many plugins dedicated to this: see for example babel-plugin-inline-import, rollup-plugin-lit-css, rollup-plugin-postcss-lit, webpack-lit-loader.

The wrapper way

If you want to keep things (almost) buildless you can write a simple postinstall script that generates a .js file that exports the lit-ified styles:

// bootstrap.css.js
import {css} from 'lit-element';

export const bootstrap = css`
<bootstrap here>
`;

// my-element.js
import {bootstrap} from './bootstrap.css.js';

class MyElement extends LitElement {
  static styles = bootstrap;
}

About Shadow DOM

If you want to use Shadow DOM you'll have to import the library in every component that needs to use it, even nested ones. This is not as onerous as it seems thanks to Constructable Stylesheets, used by Lit under the hood; think of it as a way for components to join style contexts more than a replication of identical stylesheets. Also, to keep things organized you can create a "base" component that imports bootstrap and extend it wherever needed:

import bootstrap from 'path/to/bootstrap.css';

export class BaseElement extends LitElement {
  static styles = bootstrap;
}

class MyElement extends BaseElement {
  render() {
    // Bootstrap is ready to use here!
    return html``;
  }
}

Lit documentation about style sharing: https://lit.dev/docs/components/styles/#sharing-styles

Umbo
  • 3,042
  • 18
  • 23
  • awesome - thank you! I'm already using webpack and also have a base class, anyway so the base class + bundler looks like a good way to go. Will try some bundlers and go from there. – user3757849 Feb 27 '21 at 15:21
  • Glad I could help! :) – Umbo Feb 27 '21 at 21:43
  • Awesome! It seems like there is one drawback -- if your whole site isn't built in webcomponents then either method is going to involve twice loading the css files -- once in your site html's style tag, and once in the webpack content. Nonetheless, way better than the "Flash of Unstyled Content" problem. – Michael Scott Asato Cuthbert Jun 16 '21 at 02:29
  • 1
    @MichaelScottCuthbert you're right. You could reuse the same constructed stylesheet of your components and apply it to `document` though, see [here](https://developers.google.com/web/updates/2019/02/constructable-stylesheets#using_constructed_stylesheets). – Umbo Jun 16 '21 at 13:20
  • while this worked great for bootstrap, it didn't work as well for my site's custom css which imported a webfont since `@import` isn't available in CSSResult. But it's given a lot of good directions to go in. – Michael Scott Asato Cuthbert Jun 16 '21 at 18:49
  • @Umbo's suggestion for applying to document is the way of the future, but at present adoptedStyleSheets is still not available on Firefox or Safari. :-( – Michael Scott Asato Cuthbert Jun 16 '21 at 19:21