Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Combining ::slotted with pseudo-elements

I'm trying to figure out what's the deal with combining pseudo-elements with the ::slotted selector, looks like it works with some but not with others and I can't find any documentation listing the selectors it works with

Here is a demonstration of the problem, notice how some pseudo-selectors take effect while others don't

class TestElement extends HTMLElement {
    constructor() {
        super();
        this.attachShadow({mode: 'open'});
        let template = document.querySelector("template");
        this.shadowRoot.append(template.content.cloneNode(true));
    }
}

customElements.define("test-element", TestElement);
<template>
    <style>
        
        ::slotted(*)::first-line { /* doesn't works */
            color: red;
        }
        ::slotted(*):first-letter { /* doesn't works */
            color: red;
        }
        ::slotted(*) {
            max-height: 3em;
            overflow: auto;
        }
        ::slotted(*)::-webkit-scrollbar { /* doesn't works */
            width: 3px;
        }
        ::slotted(*)::-webkit-scrollbar-track { /* doesn't works */
            background-color: red;
        }
        ::slotted(*)::selection { /* doesn't works */
            color: red;
        }
        ::slotted(*)::placeholder { /* works */
            color: red;
        }
        ::slotted(*)::marker { /* works */
            color: red;
        }
    </style>
    <slot></slot>
</template>

<test-element>
    <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Sapiente
    doloribus ullam fugit ipsum laborum velit architecto, provident dolore
    at, aperiam quaerat officiis aliquid magni sed expedita totam dolor
    doloremque odit.
    Lorem ipsum dolor sit amet consectetur adipisicing elit. Sapiente
    doloribus ullam fugit ipsum laborum velit architecto, provident dolore
    at, aperiam quaerat officiis aliquid magni sed expedita totam dolor
    doloremque odit.
    Lorem ipsum dolor sit amet consectetur adipisicing elit. Sapiente
    doloribus ullam fugit ipsum laborum velit architecto, provident dolore
    at, aperiam quaerat officiis aliquid magni sed expedita totam dolor
    doloremque odit.</p>
</test-element>

<test-element>
    <input placeholder="Placeholder">
</test-element>

<test-element>
    <li>Li</li>
</test-element>
like image 645
Mendy Avatar asked Jul 03 '26 20:07

Mendy


1 Answers

A component's shadow DOM is only allowed to style the "box" of the element you slot in, but not the content inside that box.

The ::slotted() pseudo-class gives you a hook to apply styles to the top-level elements that are passed into a <slot>. However, this power is limited. It can only apply styles that affect the element as a whole (like background-color, display, border, etc.).

Where it gets limited is with pseudo-elements that style the internal structure or content of an element:

  • Doesn't Work (::first-line, ::first-letter, ::selection, ::-webkit-scrollbar): These pseudo-elements style things inside the slotted element. ::first-line targets the text, ::selection targets a user interaction state on the text, and ::-webkit-scrollbar targets a UI element rendered as part of the element's content box. The Shadow DOM is not allowed to reach "into" the slotted element and mess with its internal details.

  • Works (::placeholder, ::marker): These are exceptions defined in the CSS specs. They are considered part of the originating element's direct presentation, almost like a built-in attribute. The browser's rendering engine exposes them in a way that allows them to be styled across the shadow boundary.

The correct way to style the internal content of a slotted element is from the outside, in the same document context where the element is defined (i.e., the light DOM).

You can target the elements by using the custom element's tag name as a CSS selector in your global stylesheet.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Slotted Pseudo-Elements</title>
    <style>
        /*
         * STYLES FOR SLOTTED CONTENT GO HERE (in the Light DOM)
         * These styles target the internal rendering of elements slotted
         * into a <test-element>.
        */
        test-element p::first-line {
            color: blue;
            font-weight: bold;
        }

        test-element p::selection {
            background-color: yellow;
            color: black;
        }
        
        test-element p::-webkit-scrollbar {
            width: 5px;
        }
        
        test-element p::-webkit-scrollbar-track {
            background-color: lightblue;
        }
    </style>
</head>
<body>

<template id="test-template">
    <style>
        /* Component's internal styles */
        :host {
            display: block;
            border: 1px solid #ccc;
            margin-bottom: 1em;
            padding: 1em;
        }

        /* 
         * STYLES THAT WORK FROM INSIDE THE SHADOW DOM
         * These target the slotted element itself or special pseudo-elements.
        */

        ::slotted(p) {
            max-height: 4em;
            overflow: auto;
            background-color: #f9f9f9;
        }
        
        ::slotted(input)::placeholder { /* This works */
            color: green;
            font-style: italic;
        }

        ::slotted(li)::marker { /* This works */
            color: green;
            font-size: 1.5em;
        }


    </style>
    <slot></slot>
</template>

<h3>Example with a Paragraph (Scroll to see scrollbar)</h3>
<test-element>
    <p>This is the first line, which should be blue and bold. The rest of this text is for demonstrating the scrollbar and selection styles. Lorem ipsum dolor sit amet consectetur adipisicing elit. Sapiente
    doloribus ullam fugit ipsum laborum velit architecto, provident dolore
    at, aperiam quaerat officiis aliquid magni sed expedita totam dolor
    doloremque odit. Select this text to see the yellow background.
    </p>
</test-element>

<h3>Example with an Input</h3>
<test-element>
    <input placeholder="This placeholder is green">
</test-element>

<h3>Example with a List Item</h3>
<test-element>
    <ul>
        <li>This list item has a large green marker.</li>
    </ul>
</test-element>

<script>
    class TestElement extends HTMLElement {
        constructor() {
            super();
            this.attachShadow({mode: 'open'});
            let template = document.getElementById("test-template");
            this.shadowRoot.append(template.content.cloneNode(true));
        }
    }
    customElements.define("test-element", TestElement);
</script>

</body>
</html>
like image 87
EmJy Avatar answered Jul 06 '26 12:07

EmJy



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!