Search code examples
javascriptweb-componentbulma

Why isn't text styling in my web component using the external stylesheet I added to the shadowroot?


I'm using the Bulma css library from an external CDN and I'm attempting to build a navbar web component. For some reason the text for my menu items is showing up as Times New Roman instead of sans-serif. It should be inheriting the default font-family from Bulma, but appears to be using browser defaults.

Do I need to be appending each child item to the shadowRoot in order to inherit Burma's text stying, or something similar to that?

Here is the html (index.html):

<!DOCTYPE html>
<html lang="en">
<head>
    <title>hello</title>
    <meta charset="utf-8">
</head>
<body>   
    <my-element></my-element>

    <script src="./js/index.js" type="module"></script>

</body>
</html>

Here is my main javascript file (index.js):

import { MyElement } from './components/header.js';

if(!customElements.get('my-element')){
    customElements.define('my-element', MyElement)
}

Here is my web component code (header.js):

export class MyElement extends HTMLElement {
    constructor() {

        super()

        let count = Math.floor(Math.round() * 10) + 1
        let items = ["Home", "Config", "Admin"]

        let that = this
        let id = Math.random()
        this.id = id

        // and use it in the constructor after creating the shadow DOM
        
        this.attachShadow({ mode: 'open' })
        this.shadowRoot.innerHTML = `<link rel=stylesheet  href='https://cdn.jsdelivr.net/npm/bulma@0.9.2/css/bulma.min.css'>`


        // main header element
        let header = document.createElement('nav')
        header.classList.add('navbar')
        header.id = 'header'
        header.role = 'navigation'

        // brand container
        let brand = document.createElement('div')
        brand.classList.add('navbar-brand')

        let brandImg = document.createElement('img')
        brandImg.src = 'https://www.zilliondesigns.com/images/how-to/swoosh-concepts-02.png'
        brandImg.width = '100'
        
        // menu container
        let menu = document.createElement('div')
        menu.classList = 'navbar-menu'
        menu.id = 'navbarexample'

        let menuLeft = document.createElement('div')
        menuLeft.classList = 'navbar-start'

        // menu items
        items.forEach(item => {
            let child = document.createElement('a')
            child.classList='navbar-item'

            child.innerText = item
    
            menuLeft.appendChild(child)  
            child.addEventListener('click', e => {
                e.target
                child.remove()
            })
        })

        menu.appendChild(menuLeft)

        brand.appendChild(brandImg)
        header.appendChild(brand)
        header.appendChild(menu)

        this.shadowRoot.appendChild(header)



    }
}

Solution

  • The reason you're seeing the default user agent font is that Bulma is obviously not made with shadow roots in mind.

    It sets the font-family for the following elements:

    body,
    button,
    input,
    optgroup,
    select,
    textarea {
      font-family: BlinkMacSystemFont, -apple-system, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
    }
    

    Because your div.button.is-large is not a descendant to any of those elements, there is no Bulma font definition to be inherited. I highly assume that this will apply to other commonly inherited properties like color as well.

    To fix it, Just include the Bulma CSS on the main page as well:

    <link rel=stylesheet href="https://cdn.jsdelivr.net/npm/bulma@0.9.2/css/bulma.min.css">
    

    Inheritance-wise this will subject the elements in your shadow root to be "descendants" of the body element, and thus, inherit inherited styles again.

    Alternatively, I'd suggest you create a ComponentStyles.js class with a static property styles like this:

    export class ComponentStyles {
       static get styles() {
           return `:host { font-family: BlinkMacSystemFont, -apple-system, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; }`;
       }
    }
    

    and in your components, you import it and then use it in the constructor:

    import { ComponentStyles } from './path/to/ComponentStyles.js';
    
    // ...
    
    constructor() {
        // ...
        let style = document.createElement('style');
        style.textContent = ComponentStyles.styles;
        this.shadowRoot.appendChild(style);
        // ...
    }