Override styles in a shadow-root element

Ionic V4 select down icon color change example

document.querySelector('#my-select').shadowRoot.querySelector('.select-icon-inner').setAttribute('style', 'opacity:1');


ionViewDidEnter() {
    document.querySelector('#my-select').shadowRoot.querySelector('.select-icon-inner').setAttribute('style', 'opacity:1');
  }

If you want overwrite the default generated shadowRoot style then have to call js function after page loaded fully.


I'd like to second an answer given by @Renato in one of the comments, since it points out the good, IMHO, way to solve the problem of customizing a WebComponent from the hosting application.

@Supersharp is right in the fact, that the external CSS rules are not propagating into thee Shadow Root, that's by design.

CSS variables are a good direction, but from my personal experience are a bit of an overkill for a value of a singular usage, AND yes, they MUST be supported be the WebComponent up-front.

Propagating the properties through the :host via inheritance (exactly as @Renato mentioned) is, IMHO, the perfectly right pattern aligned with the API design:

  • Custom element's (:host's) CSS rules are by design overridable by the outer rules
  • :host's children, the inner content of the Shadow DOM, MAY inherit the CSS rules of the :host, either by default or by explicit rule - and this is too, by design

I'd say, that where applicable, this approach would better be taken before considering CSS stylesheet injection, and also does not suffer from the limitation of open mode only.

Of course, this approach won't help when:

  • Inner elements are not inheriting relevant rules from the :host
  • The structure of a WebComponent is quite complex, so that single :host simply can't help them all

Yet, again from my own experience, simple components with desirably overridable CSS rules may benefit much from the non-intrusive pattern of propagating rules via :host.


Because of the isolation of styles, which is a feature of Shadow DOM, you cannot define a global CSS rule that will be applied in the Shadow DOM scope.

It could be possible with CSS variables but they should be implemented explicitly in the shadowed component (which is not the case with this 3rd party library).

A workaround is to inject the line of style in the shadow DOM directly.

//host is the element that holds the shadow root:
var style = document.createElement( 'style' )
style.innerHTML = '.the-class-name { property-name: my-value; }'
host.shadowRoot.appendChild( style )

NB: it will work only if the Shadow DOM mode is set to 'open'.


2019 update for Chrome 73+ and Opera 60+

Now it is possible to instantiate a CSSStyleSheet object directly and to affect it to a Shadow DOM or a document:

var sheet = new CSSStyleSheet
sheet.replaceSync( `.color { color: pink }`)
host.shadowRoot.adoptedStyleSheets = [ sheet ] 

Extending on the previous answers.

Outside styles always win over styles defined in the Shadow DOM, i.e. when you add a global style rule that reference the component you are styling. See: https://developers.google.com/web/fundamentals/web-components/shadowdom#stylefromoutside

Otherwise this will depend on if the elements shadow DOM was embedded with a styleSheet, or if it adopted a style-sheet using adoptedStyleSheets.

If the element was embedded in the element you can add or insert a rule to the existing style-sheet using addRule or insertRule. This also work for style-sheets added with adopedStyleSheets.

As mentioned in the previous answer, you can append a new style-sheet to the list of adopted style-sheets instead. This also work when the shadowRoot contains a embedded styleSheet, since adoptedStyleSheets takes precedence, and styleSheetList is a read-only property.

assert(myElement.shadowRoot.styleSheets.length != 0);
myElement.shadowRoot.styleSheets[0].addRule(':host', 'display: none;');

assert(myElement.shadowRoot.adoptedStyleSheets.length != 0);
`myElement.shadowRoot.adoptedStyleSheets[0].addRule(':host', 'display: none;');`

const sheet = new CSSStyleSheet();
sheet.replaceSync(`:host { display: none; }`);

const elemStyleSheets = myElement.shadowRoot.adoptedStyleSheets;

// Append your style to the existing style sheet.
myElement.shadowRoot.adoptedStyleSheets = [...elemStyleSheets, sheet];

// Or if just overwriting a style set in the embedded `styleSheet`
myElement.shadowRoot.adoptedStyleSheets = [sheet];