Search code examples
javascripthtmlcssfont-awesomeweb-component

Add fontawesome to shadowroot of webcomponent


I've created a JS script that creates a webcomponent. That webcomponent used Font Awesome icons. When I include the Font Awesome library to the head of my HTML, the icons work without any problems. But I don't want them to be added to the HTML, I want them inside the shadowdom of my component.

My JS file has a render() function where all the HTML is. I've also added fetch of the font awesome css but that doesn't work. I've also tried to simply link to css over there, but that doesn't work either.

Below a minimal example of my code:

render() {
    fetch('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css')
  .then(response => response.text())
  .then(cssText => {
    // Inject the CSS text into the shadow DOM's <head> element
    const shadowHead = this.shadowRoot.querySelector('head');
    const styleElement = document.createElement('style');
    styleElement.textContent = cssText;
    shadowHead.appendChild(styleElement);
  });
    // Create the component's structure and styles
    this.shadowRoot.innerHTML = `
      <head>
        <style>
        * {
          font-family: Arial, sans-serif;
        }

        .title {
          color: #366BAB;
          font-size: 1rem;
        }

        </style>
      </head>
      
      <div>
        <h1 class="title">Lorem ipsum</h1>
      </div>
      `;
  }

Result:

The CSS is not added. For testing purposes, I've created a div with class .head and changed the querySelector to '.head', the CSS is added to that div but it isn't applied, the icon don't work this way.

New findings:

They icons are all shown as squares and when I inspect such a square, I see that the actual CSS is injected an correctly linked. See screenshot below

Fontawesome css issue


Solution

  • The first question you have to ask yourself: Does my Web Component really need shadowDOM?

    If so, you have to load FontAwesome in both the main Document and in shadowDOM

    <fa-icon is="check"></fa-icon>
    <fa-icon is="square-check"></fa-icon>
    <fa-icon is="circle-check"></fa-icon>
    <fa-icon></fa-icon>
    <script>
      let element = (tag,p={}) => Object.assign(document.createElement(tag),p);
    
      customElements.define('fa-icon', class extends HTMLElement {
        constructor() {
          let version = "[email protected]";
          let href = `https://cdn.jsdelivr.net/npm/@fortawesome/${version}/css/all.min.css`;
          let linkFontAwesome = () => element("link", {rel: "stylesheet", href });
          let hasFontAwesome = document.querySelector(`link[href="${href}"]`);
          if (!hasFontAwesome) document.head.append( linkFontAwesome() );
          super()
            .attachShadow({mode:'open'})
            .append(
              linkFontAwesome(),
              element("icon")
            );
        }
        connectedCallback() {
          this.style.display = "inline-block";
          this.icon = this.icon;
          this.onclick = (evt) => alert( this.icon );
        }
        get icon() {
          return this.getAttribute("is") || "gear";
        }
        set icon(icon){
          this.shadowRoot.querySelector("icon").innerHTML = `<i class="fa fa-${icon}"></i>`;
        }
      });
    </script>