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
pitself 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.