Component stylesheet not getting added to shadow root

I am trying to integrate shadowDom into an existing au2 project and am facing a couple of issues. I started of with following the documentation here.

Adding this to my main.js file

import bootstrap from .....
...
.register(StyleConfiguration.shadowDOM({
    sharedStyles: [bootstrap]
  }))

Adding in this to my webpack.config file

{
  test: /[/\\]src[/\\].+\.html$/i,
  use: {
    loader: '@aurelia/webpack-loader',
    options: {
      defaultShadowOptions: { mode: 'open' }
    }
  },
  exclude: /node_modules/
}

Looking at new project created using npx makes aurelia and choosing the shadowDom option. These entries need to also be added to the webpack.config file

{
      test: /\.css$/i,
      // For style loaded in src/main.js, it's not loaded by style-loader.
      // It's for shared styles for shadow-dom only.
      issuer: /[/\\]src[/\\]main\.(js|ts)$/,
      use: [cssLoader, postcssLoader]
},
{
      test: /\.scss$/i,
      // For style loaded in src/main.js, it's not loaded by style-loader.
      // It's for shared styles for shadow-dom only.
      issuer: /[/\\]src[/\\]main\.(js|ts)$/,
      use: [cssLoader, postcssLoader, sassLoader]
 },
 {
      test: /\.css$/i,
      // For style loaded in other js/ts files, it's loaded by style-loader.
      // They are directly injected to HTML head.
      issuer: /(?<![/\\]src[/\\]main)\.(js|ts)$/,
      use: ['style-loader', cssLoader, postcssLoader]
 },
 {
      test: /\.scss$/i,
      // For style loaded in other js/ts files, it's loaded by style-loader.
      // They are directly injected to HTML head.
      issuer: /(?<![/\\]src[/\\]main)\.(js|ts)$/,
      use: ['style-loader', cssLoader, postcssLoader, sassLoader]
 },
 {
      test: /\.css$/i,
      // For style loaded in html files, Aurelia will handle it.
      issuer: /\.html$/,
      use: [cssLoader, postcssLoader]
 },
 {
      test: /\.scss$/i,
      // For style loaded in html files, Aurelia will handle it.
      issuer: /\.html$/,
      use: [cssLoader, postcssLoader, sassLoader]
},

Now, when I try and run my project, I get an error

Failed to execute 'attachShadow' on 'Element': This element does not support attachShadow

Fair enough. I don’t really want to add it to all of the components, just the the my-app component. So, I changed my webpack.config file to this:

{
  test: /[/\\]src[/\\].+\.html$/i,
  use: {
    loader: '@aurelia/webpack-loader',
    options: {
      // defaultShadowOptions: { mode: 'open' }
    }
  },
  exclude: /node_modules/
}

By commenting out defaultShadowOptions: { mode: 'open' }, no shadow root is being added to any component.

Now in my my-app.js file I add in this:

@useShadowDOM({ mode: 'open' })

Looking at the Dom tree in the browser inspector, I can see that a shadow root has been added and the most of the bootstrap styles have been applied.

However, non of the styles in my-app.scss have been added?

Maybe I am just misunderstanding how this works?

I managed to solve this. Posting incase anyone else has the issue.

First change webpack.config to this:

...
module: {
  rules: [
  { test: /\.(png|svg|jpg|jpeg|gif)$/i, type: 'asset' },
  { test: /\.(woff|woff2|ttf|eot|svg|otf)(\?v=[0-9]\.[0-9]\.[0-9])?$/i, type: 'asset' },

  {
    test: /\.css$/i,
    // For style loaded in src/main.js, it's not loaded by style-loader.
    // It's for shared styles for shadow-dom only.
    issuer: /[/\\]src[/\\]main\.(js|ts)$/,
    use: [cssLoader, postcssLoader]
  },
  {
    test: /\.scss$/i,
    // For style loaded in src/main.js, it's not loaded by style-loader.
    // It's for shared styles for shadow-dom only.
    issuer: /[/\\]src[/\\]main\.(js|ts)$/,
    use: [cssLoader, postcssLoader, sassLoader]
  },
  {
    test: /\.css$/i,
    // For style loaded in other js/ts files, it's loaded by style-loader.
    // They are directly injected to HTML head.
    issuer: /(?<![/\\]src[/\\]main)\.(js|ts)$/,
    use: ['style-loader', cssLoader, postcssLoader]
  },
  {
    test: /\.scss$/i,
    // For style loaded in other js/ts files, it's loaded by style-loader.
    // They are directly injected to HTML head.
    issuer: /(?<![/\\]src[/\\]main)\.(js|ts)$/,
    use: ['style-loader', cssLoader, postcssLoader, sassLoader]
  },
  {
    test: /\.css$/i,
    // For style loaded in html files, Aurelia will handle it.
    issuer: /\.html$/,
    use: [cssLoader, postcssLoader]
  },
  {
    test: /\.scss$/i,
    // For style loaded in html files, Aurelia will handle it.
    issuer: /\.html$/,
    use: [cssLoader, postcssLoader, sassLoader]
  },

  { test: /\.js$/i, use: ['babel-loader', '@aurelia/webpack-loader'], exclude: /node_modules/ },
  {
    test: /[/\\]src[/\\].+\.html$/i,
    use: {
      loader: '@aurelia/webpack-loader',
      options: {
        // The other possible Shadow DOM mode is 'closed'.
        // If you turn on "closed" mode, there will be difficulty to perform e2e
        // tests (such as Playwright). Because shadowRoot is not accessible through
        // standard DOM APIs in "closed" mode.
        defaultShadowOptions: { mode: 'open' }
      }
    },
    exclude: /node_modules/
  }
  ]
}
...

Then in main.js add this

Aurelia
  .register(StyleConfiguration.shadowDOM({
    // optionally add the shared styles for all components
    sharedStyles: []
  }))

If you get an error: Failed to execute 'attachShadow' on 'Element'. THis could be due to the fact that shadowDom requires custom elements to follow this naming convention my-element.

This also creates a shadowRoot for all of your components/routes. If you do not want this, then you can add in this to the individual components.

import { useShadowDOM } from 'aurelia'

@useShadowDOM(null)

Note that in the aurelia documentation is says to use @useShadowDOM(false) but this did not work for me.

Hope this helps :slightly_smiling_face:

2 Likes