Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow importing code to determine where styles are inserted #328

Closed
grommett opened this issue Jun 17, 2018 · 8 comments · Fixed by #535
Closed

Allow importing code to determine where styles are inserted #328

grommett opened this issue Jun 17, 2018 · 8 comments · Fixed by #535

Comments

@grommett
Copy link

grommett commented Jun 17, 2018

This is a feature request.

Problem

As far as I can tell, the style loader only allows for styles to be inserted into one location, either the <head> or where specified in the inserInto string or function.

This approach does not work with composed/nested custom elements, as each can have their own shadow dom. If, for example, insertInto specifies the root custom element the styles are not picked up in children shadow doms.

Feature Request

Allow importing code to determine where styles are inserted.

Possible Solution

  1. Add an option for local insertion. Let's call it localInsert, which defaults to false so that code executes as it currently does.
  2. When localInsert is true, the update function is not invoked and we attach a method (localInsertTo ?) to exports, which allows for the importing file(s) to determine where the styles are inserted.

Example

In the example below each custom element will have their imported styles inserted into their respective shadow doms by calling the localInsertTo.

Webpack

...
options: {
  localInsert: true
}
...

Parent

import css from './parent.css';

export class Parent extends HTMLElement {
  constructor() {
    super();
    this.shadow = this.attachShadow({ mode: 'open' });
    this.shadow.innerHTML = `
    <div>Parent 
      <x-child></x-child>
    </div>`;
    css.localInsertTo(this.shadow);
  }
}

customElements.define('x-app', Parent);

Child

import css from './child.css';

export class Child extends HTMLElement {
  constructor() {
    super();
    this.shadow = this.attachShadow({ mode: 'open' });
    this.shadow.innerHTML = `<div>child</div>`;
    css.localInsertTo(this.shadow);
  }
}

customElements.define('x-child', Child);
@grommett grommett changed the title Allow for localInsertTo option so that each file can determine where styles are inserted Allow importing code to determine where styles are inserted Jun 18, 2018
@bcanseco
Copy link

bcanseco commented Jul 30, 2018

Having a similar issue, this would be an awesome feature!

@grommett
Copy link
Author

@bcanseco
If you're interested I played with this idea and actually got it working. I'm not sure if it meets your use case, but have a look at the code in the repo I was playing with it in.

Loader files are here: https://github.com/grommett/web-components/tree/master/html-loader-css-modules/loaders

Usage(webpack.config) here:
https://github.com/grommett/web-components/blob/master/html-loader-css-modules/webpack.config.js

bcanseco added a commit to bcanseco/style-loader that referenced this issue Jul 30, 2018
This lets users reference their own string selector inside the
insertInto function. Necessary until webpack-contrib#328 is resolved.
@alexander-akait
Copy link
Member

Sorry for big delay, PR welcome

@alexander-akait
Copy link
Member

We have useable api, i think we need improve this, adding more option for style.use() or maybe implement new api, it should be easy, so PR welcome, anyway will be implemented for 2.0.0 version

@alexander-akait
Copy link
Member

alexander-akait commented Jul 30, 2019

For better compatibility with css modules from w3 spec (not to be confused with css-modules from css-loader), we need export css styleSheet as String and do:

import styles from './custom-square.css';

class CustomSquare extends HTMLElement {
  // Specify observed attributes so that
  // attributeChangedCallback will work
  static get observedAttributes() {
    return ['l'];
  }

  constructor() {
    // Always call super first in constructor
    super();

    this.attachShadow({ mode: 'open' });
  }
  connectedCallback() {
    const div = document.createElement('div');

    this.shadowRoot.appendChild(div);

    const bgPurple = new CSSStyleSheet();

    bgPurple.replace(styles);

    this.shadowRoot.adoptedStyleSheets = [bgPurple];
  }
}

customElements.define('custom-square', CustomSquare);

@mike-shtil
Copy link

For better compatibility with css modules from w3 spec (not to be confused with css-modules from css-loader), we need export css styleSheet as String and do:

import styles from './custom-square.css';

class CustomSquare extends HTMLElement {
  // Specify observed attributes so that
  // attributeChangedCallback will work
  static get observedAttributes() {
    return ['l'];
  }

  constructor() {
    // Always call super first in constructor
    super();

    this.attachShadow({ mode: 'open' });
  }
  connectedCallback() {
    const div = document.createElement('div');

    this.shadowRoot.appendChild(div);

    const bgPurple = new CSSStyleSheet();

    bgPurple.replace(styles);

    this.shadowRoot.adoptedStyleSheets = [bgPurple];
  }
}

customElements.define('custom-square', CustomSquare);

This looks like an elegant approach, however import styles from './custom-square.css' would only return a map of key:classname, not the compiled CSS string. Am I missing something?

@alexander-akait
Copy link
Member

@mike-shtil it is only return css nothing more, for better w3c compatibility with css modules spec (do not confuse with css modules from css-loader)

OlegWock added a commit to OlegWock/style-loader that referenced this issue Sep 12, 2021
Allow to pass additional parameter to `style.use(anything)` which will be passed to `insert` function. This allows to insert each tag in different places and especially useful for Shadow DOM

Resolves webpack-contrib#328
OlegWock added a commit to OlegWock/style-loader that referenced this issue Sep 12, 2021
Allow to pass additional parameter to `style.use(anything)` which will be passed to `insert` function. This allows to insert each tag in different places and especially useful for Shadow DOM

Resolves webpack-contrib#328
@alexander-akait
Copy link
Member

alexander-akait commented Sep 14, 2021

There are two solution for shadow DOM:

Original style-loader styleTag and singletonStyleTag is not designed for this case, in theory it is possible using hacks, but better avoid it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants