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

insertInto and ShadowDom #256

Closed
cdbkr opened this issue Jul 10, 2017 · 18 comments
Closed

insertInto and ShadowDom #256

cdbkr opened this issue Jul 10, 2017 · 18 comments

Comments

@cdbkr
Copy link

cdbkr commented Jul 10, 2017

Regarding #135 , "insertInto" seems to me incomplete or obsolete (or missing extra features) at the moment when combining it with ShadowDom.
In the ReadME there is the following example:

{
  loader: 'style-loader'
  options: {
    insertInto: '#host::shadow>#root'
  }
}

My concern is that the pseudo selector "::shadow" is going (probably it is) to be deprecated along with "/deep/".

Are you aware of any other way to replicate the example above using css selectors?

Maybe, an improved/extended way would be a direct access to the element's shadowRoot - declaring or inspecting the element's 'nature' - , of course supporting also simple elements such as the one in the tests https://github.com/webpack-contrib/style-loader/blob/master/test/basicTest.js#L98 (a simple div with "target" as class).

@LukasBombach
Copy link

LukasBombach commented Aug 8, 2017

I could use this too. It seems ::shadow is deprecated now. However, this could be shimmed. We could check if a ::shadow selector is present and just do

querySelector('#selectorUnitlShadow').shadowRoot

or

querySelector('#selectorUnitlShadow').shadowRoot.querySelector('.possibleDeeperQueries')

respectively.

Would the maintainers care for such a PR?

@LukasBombach
Copy link

This works: master...LukasBombach:allow-insertInto-shadow-dom

I'm gonna test this a little bit tomorrow, write unit tests and might open a PR if you want to.

@morewry
Copy link

morewry commented Aug 9, 2017

I just copied this from the README #host::shadow>#root and got Uncaught Error: Couldn't find a style target. This probably means that the value for the 'insertInto' parameter is invalid.

I take it this means it's a CSS selector and you have to write a really specific selector for a shadow root. However, this selector doesn't function in recent versions of Chrome (no longer shows a deprecation warning, even, which may mean it's been removed).

A very specific selector...since IDs are unique and used by all the examples, there's an implication that you can only put the styles inside one shadow root? Very little about this appears tenable to me for a web component. Since you don't control what IDs or classes a component consumer puts on the hosting element, this would require every consumer to do a webpack config that supports how you as the author intend your component to be built, because you can't (so far as I can tell) effectively distribute your component with the styles already built in. And how are you supposed to place the styles for a web component composed from other web components in the right places? There are multiple shadow roots encapsulated inside other shadow roots.

@LukasBombach
Copy link

LukasBombach commented Aug 9, 2017

@morewry Regarding the CSS selector for finding your web component. When you setup your project you are in control of the HTML, so you can write any specific CSS selector that fits your HTML, you don't have to account for users using your web component differently. In the end you compile everything and ship your component with the styles included independent of what selector you used during development. I hope I understood your remark right.

Regarding nested custom elements. With the code I provided in the post before yours, you could do

{
    insertInto: 'my-component::shadow > #whatever > my-nested-component::shadow'
}

@savelichalex
Copy link
Contributor

savelichalex commented Dec 1, 2017

Looks like ::shadow not working now. What you guys think if I create PR that allow to use function in insertInto options? Smth like this

{
  insertInto: () => document.querySelector('.foo')
}

Instead of this: https://github.com/webpack-contrib/style-loader/blob/master/lib/addStyles.js#L46-L48

@herodrigues
Copy link

@savelichalex ::shadow is deprecated. It's from Shadow DOM v0 spec.

A solution would be to check if the target element has a shadow root.

@savelichalex
Copy link
Contributor

@herodrigues Yeah, you absolutely right 😄 And this is why I want to select target with custom function. Maybe I misspelling a little, check out this

{
  insertInto: () => document.querySelector('.foo').shadowRoot
}

And now in this place loader will be append new styles

@cdbkr
Copy link
Author

cdbkr commented Dec 1, 2017

@savelichalex that makes sense. it might even be memoized for performance reasons.

@savelichalex
Copy link
Contributor

Add PR for this #279

@michael-ciniawsky michael-ciniawsky added this to the 0.20.0 milestone Dec 4, 2017
@austinwalter
Copy link

austinwalter commented Jan 9, 2018

Is there a way to use insertInto if the HTML element it would insert into isn't created until the application is launched? I'm using this with a chrome extension content script and so I don't have access to the document immediately. Because of this the style-loader is trying to insert the styles before I actually get to create the element!

@michael-ciniawsky
Copy link
Member

Closed by #279, released in v0.20.0 🎉

@DiesIrae
Copy link

DiesIrae commented Aug 9, 2018

Neat! But I'm blocked the same way @austinwalter is: I create the shadow root programmatically at the beginning of my script :

const shadowRoot = document.body.attachShadow({mode: 'open'});
const root = shadowRoot.appendChild(element)

but it gets inserted after style-loader execution of insertInto, so I get the error: Couldn't find a style target. This probably means that the value for the 'insertInto' parameter is invalid.. Do you see a workaround?

@herodrigues
Copy link

herodrigues commented Aug 9, 2018

@austinwalter @DiesIrae I'm not using style-loader anymore because of this issue. I'm also developing a Chrome extension.
In this example, I use raw-loader plugin in my webpack config file.

import shadowCSS from 'css/shadow';

root = document.createElement('div');
root = container.attachShadow({ mode: 'open' });

const style = document.createElement('style');
style.type = 'text/css';
style.appendChild(document.createTextNode(shadowCSS));

root.appendChild(style);
document.body.appendChild(root);

Of course this code goes after the DOM is loaded.

@sdeleeuw
Copy link

@herodrigues
Thanks! This worked for me as well.

@simonLouvet
Copy link

simonLouvet commented Oct 29, 2018

@herodrigues
Thanks! work with to-string-loader for me.

@anshul2209
Copy link

@austinwalter @DiesIrae , were you guys able to find a work around ? I am currently struggling with this.

@doktordirk
Copy link

i used the url-loader:
my-component.html

<template>
  <link href="./my-component.scss" rel="stylesheet">
  <button class="btn"><slot>replace me</slot></button>
</template>

my-component.scss

.btn {
  background-color: var(--btn-background-color, red);
}

webpack.config.js

...
module: {
    rules: [
     {
        test: /\.scss$/i,
        issuer: [{ test: /\.html$/i }],
        use: [{
          loader: 'url-loader',
          options: {
            mimetype: 'text/css',
          },
        }, 'sass-loader']
      },
      { 
        test: /\.html$/i, 
        loader: 'html-loader', 
        options: {
          attrs: ['img:src', 'link:href'],
        }, 
      },
...

@simpleshadow
Copy link

simpleshadow commented Apr 17, 2020

Now in 2020, was having this issue with style-loader. Switched to-string-loader (as suggested by @simonLouvet) and got it working.

Also tried raw-loader but wasn't working with postcss-loader.

Building a Chrome extension where style encapsulation is necessary for an overlayed react app injected via content script. Shadow DOM has been the only successful way of isolating styles for our use case.

Tips

Container component looks like this:

import tawilwindCss from 'styles/tailwind.css'

export const Container = () => {
  return (
    <>
      <style>{tawilwindCss}</style>
      <div className="fixed inset-0 bg-overlay w-screen h-screen text-white flex items-center justify-center font-sans">
        <div className="bg-dark rounded p-4">
            {/* ... */}
        </div>
      </div>
    </>
  )
}

Render the component as follows:

export const renderApp = () => {
  const el = document.createElement('div')
  el.id = 'container'
  el.style.height = '100vh'
  el.style.width = '100vw'
  el.style.position = 'fixed'
  el.style.top = '0px'
  el.style.left = '0px'
  el.style.zIndex = '2147483647'
  el.style.pointerEvents = 'none'

  const shadowRoot = el.attachShadow({ mode: 'open' })
  shadowRoot && retargetEvents(shadowRoot), (shadowRoot.innerHTML = `<div id="app"></div>`)

  const renderInShadowDOM = () => {
    document.body.append(el)

    import('../components/container').then(({ Container }) =>
      render(createElement(Container), shadowRoot.getElementById('app'))
    )
  }

  if (document.readyState === 'complete' || document.readyState === 'interactive') {
    renderInShadowDOM()
  } else {
    window.addEventListener('DOMContentLoaded', renderInShadowDOM)
  }
}

Probably will want to reset styles via:

#app {
  all: initial;
  * {
    all: unset;
  }
}

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

No branches or pull requests