<popup-info>
About using external CSS styles
Note that <link> elements do not block paint of the shadow root, so there may be a flash of unstyled content (FOUC) while the stylesheet loads.
/*
    Using Shadow DOM
    https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM
*/
// ⭐️ 1. custom web component
class PopUpInfo extends HTMLElement {
    // constructor
    constructor() {
        // tell HTMLElement to initialize itself.
        super();
        // Create a shadow root
        let shadow = this.attachShadow({mode: 'open'});
        // get attributes from this <popup-info>
        let info = this.getAttribute('data-text');
        let src = this.getAttribute('img') ?? 'https://mdn.github.io/web-components-examples/popup-info-box-web-component/img/alt.png';
        // set the shadow tree
        shadow.innerHTML = PopUpInfo.template(src, info);
        
    }
    // template HTML (⭐️ external css styles)
    static template(imgURL, infoText){
        return `
            <link href="popup.css" rel="stylesheet" />
            <span class="wrapper">
                <span class="icon" tabindex=0>
                    <img src="${imgURL}">
                </span>
                <span class="info">${infoText}</span>
            </span>
        `;
    }
} 
// ⭐️ 2. define custom tag <popup-info>
// ⭐️⭐️ custom element names must contain a "hyphen".
customElements.define('popup-info', PopUpInfo);
// ⭐️ 1. custom web component
class PopUpInfo extends HTMLElement {
    // constructor
    constructor() {
        // tell HTMLElement to initialize itself.
        super();
        // Create a shadow root
        let shadow = this.attachShadow({mode: 'open'});
        // get attributes from this <popup-info>
        let info = this.getAttribute('data-text');
        let src = this.getAttribute('img') ?? 'https://mdn.github.io/web-components-examples/popup-info-box-web-component/img/alt.png';
        // set the shadow tree
        shadow.innerHTML = PopUpInfo.template(src, info);
        
    }
    // template HTML (domString)
    static template(imgURL, infoText){
        return `
            <style>
            .wrapper {
                position: relative;
            }
            .info {
                font-size: 0.8rem;
                width: 200px;
                display: inline-block;
                border: 1px solid black;
                padding: 10px;
                background: hsla(60, 80%, 50%, 0.9);
                border-radius: 10px;
                opacity: 0;
                transition: 0.6s all;
                position: absolute;
                top: 20px;
                left: 10px;
                z-index: 3;
            }
            img {
                width: 1.2rem;
            }
            .icon:hover + .info, .icon:focus + .info {
                opacity: 1;
            }
            </style>
            <span class="wrapper">
                <span class="icon" tabindex=0>
                    <img src="${imgURL}">
                </span>
                <span class="info">${infoText}</span>
            </span>
        `;
    }
} 
// ⭐️ 2. define custom tag <popup-info>
// ⭐️⭐️ custom element names must contain a "hyphen".
customElements.define('popup-info', PopUpInfo);
.wrapper {
    position: relative;
}
.info {
    font-size: 0.8rem;
    width: 200px;
    display: inline-block;
    
    border: 1px solid black;
    padding: 10px;
    background: hsla(60, 80%, 50%, 0.9);
    border-radius: 10px;
    opacity: 0;
    transition: 0.6s all;
    position: absolute;
    top: 20px;
    left: 10px;
    z-index: 3;
}
img {
    width: 1.2rem;
}
.icon:hover + .info, .icon:focus + .info {
    opacity: 1;
}// ⭐️ 1. custom web component
class PopUpInfo extends HTMLElement {
    // constructor
    constructor() {
        // tell HTMLElement to initialize itself.
        super();
        // Create a shadow root
        let shadow = this.attachShadow({mode: 'open'});
        // create spans
        let wrapper = tag('span', {'class': 'wrapper'});
        let icon = tag('span', {'class': 'icon', 'tabindex': 0});
        let info = tag('span', {'class': 'info'});
        info.textContent = this.getAttribute('data-text');
        // img
        let src = this.getAttribute('img') ?? 'https://mdn.github.io/web-components-examples/popup-info-box-web-component/img/alt.png';
        let img = tag('img', {'src': src});
        icon.appendChild(img);
        // attach elements to shadow dom    // shadow root
        shadow.appendChild(this.style());   //   ├ style
        shadow.appendChild(wrapper);        //   └ wrapper
        wrapper.appendChild(icon);          //       ├ icon
        wrapper.appendChild(info);          //       │   └ img
    }                                       //       └ info
    // css style
    style() {
        // Create some CSS to apply to the shadow dom
        let style = document.createElement('style');
        style.textContent = `
        .wrapper {
            position: relative;
        }
        .info {
            font-size: 0.8rem;
            width: 200px;
            display: inline-block;
            border: 1px solid black;
            padding: 10px;
            background: hsla(60, 80%, 50%, 0.9);
            border-radius: 10px;
            opacity: 0;
            transition: 0.6s all;
            position: absolute;
            top: 20px;
            left: 10px;
            z-index: 3;
        }
        img {
            width: 1.2rem;
        }
        .icon:hover + .info, .icon:focus + .info {
            opacity: 1;
        }`;
        return style;
    }
} 
// ⭐️ 2. define custom tag <popup-info>
// ⭐️⭐️ custom element names must contain a "hyphen".
customElements.define('popup-info', PopUpInfo);
/* -------- helpers -------- */
function tag(name, attrs){
    // create element
    let elem = document.createElement(name);
    // set attributes
    for (const [key, value] of Object.entries(attrs)) {
        elem.setAttribute(key, value);
    }
    // return element
    return elem;
}<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. 
Autem, assumenda aut voluptas perspiciatis impedit alias. 
Fugit atque eos voluptatem suscipit quos nostrum! 
Libero tenetur deleniti minima! Atque laborum minima non.</p>
<!-- web component -->
<popup-info 
    img="https://mdn.github.io/web-components-examples/popup-info-box-web-component/img/alt.png" 
    data-text="Your card validation code (CVC) is an extra security feature — it is the last 3 or 4 numbers on the back of your card."
></popup-info>
<span>hover over this image.</span>
<h1>Hello</h1>Last updated
Was this helpful?