Let's say I have an app where paragraphs are red by default, but I want to use a custom element (let's call it <blue-stuff>
) to style certain paragraphs as blue and bold. In the snippet below, I attempt to do this with a web component and shadow dom below, relying on the ::slotted
pseudo-element:
customElements.define('blue-stuff', class extends HTMLElement {
constructor() {
super()
.attachShadow({ mode: 'open' })
.appendChild(document.importNode(document.getElementById('blue-template').content, true))
}
})
p {
color: red;
}
<template id="blue-template">
<style>
.blue ::slotted(p) {
color: blue;
font-weight: bold;
}
</style>
<div class="blue">
<slot></slot>
</div>
</template>
<p>Hello I am red!</p>
<blue-stuff>
<p>Hello, I am supposed to be blue and bold!</p>
</blue-stuff>
What surprises me is the paragraph that's supposed to be blue is in fact red, meaning it's prioritizing the simple p
selector over the .blue ::slotted(p)
selector. Normally, specificity would solve this problem but it appears in this case, styling in the "light" dom gets preferred over the shadow dom.
The question: In my example, is it possible for the paragraph in <blue-stuff>
to be styled as blue without the use of !important
?
I've thought of so far:
- Give each paragraph not in the component a class, call it
paragraph
, and select that instead. A bit hard-handed, and not something I'd like to do, as I'm parsing these paragraphs from markdown. - Apply the red color to whatever wraps the paragraphs, so the
p
itself no longer has the rule. While this works for inherited properties like color, it doesn't work for properties like margin. - Apply the styling in the light DOM, by selecting
blue-stuff p { ... }
. It works, but now it feels like the component is not self-sufficient.
I've worked on this in Firefox.