Search code examples
javascriptweb-componentshadow-domcustom-elementhtml5-template

How to let imported css font / icons have effects on elements in the shadow dom?


Say if I want to create a custom element using shadow dom. Some elements in the template have class names specified in the linked css file. Now I want to let the css rules have effects on the elements. But I can't achieve that because of the shadow dom style boundary.

<link rel="stylesheet" type="text/css"
href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">
<template id="blog-header">
<header>
    <h1>DreamLine</h1>
    <nav>
        <ul>
            <li><a href="#0">Tour</a></li>
            <li><a href="#0">Blog</a></li>
            <li><a href="#0">Contact</a></li>
            <li><a href="#0">Error</a></li>
            <li><a href="#0"><i class="fa fa-search"></i>Search</a></li>
        </ul>
    </nav>
</header>
</template>
<script type="text/javascript">
var importDoc = document.currentScript.ownerDocument;
var proto = Object.create(HTMLElement.prototype, {
    createdCallback: {
        value: function () {
            var t = importDoc.querySelector("#blog-header");
            var clone = document.importNode(t.content, true);
            this.createShadowRoot().appendChild(clone);
        }
    }
});
document.registerElement("blog-header", {
    prototype: proto
});
</script>

You see, fa-search is a class defined in the font-awesome css file, how can I style the <i> element?


Solution

  • To use an imported font (e.g., FontAwesome) in a Shadow DOM, you should:

    1° Declare the Font

    First, include the <link rel="stylesheet"> element in the main document. It will declare a @font-face CSS rule that will make the font available for all the text in the document.

    2° Import the Stylesheet

    Then, import the same file with an @import url CSS rule in the <template> node to make the .fa-* classes selectors available from the Shadow DOM :

    <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.1/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
    
    <template id="blog-header">
      <style>
        @import url("https://use.fontawesome.com/releases/v5.7.1/css/all.css")
      </style>
      <header>
        <h1>DreamLine</h1>
        //...
      </header>
    </template>
    

    var importDoc = document.currentScript.ownerDocument;
    var proto = Object.create(HTMLElement.prototype, {
    	createdCallback: {
    		value: function() {
    			var t = importDoc.querySelector("#blog-header");
    			var clone = document.importNode(t.content, true);
    			this.createShadowRoot().appendChild(clone);
    		}
    	}
    });
    document.registerElement("blog-header", {prototype: proto});
    /* 
    @font-face {
        font-family: "FontAwesome";
        src: url("https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome-webfont.woff2?v=4.5.0") format('woff2');
      }
    */
    <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.1/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
    
    <template id="blog-header">
      <style>
        @import url("https://use.fontawesome.com/releases/v5.7.1/css/all.css")
      </style>
    	<header>
    		<h1>DreamLine</h1>
    		<nav>
    			<ul>
    				<li><a href="#0">Tour</a></li>
    				<li><a href="#0">Blog</a></li>
    				<li><a href="#0">Contact</a></li>
    				<li><a href="#0">Error</a></li>
    				<li><a href="#0"><i class="fa fa-search"></i>Search</a></li>
    			</ul>
    		</nav>
    	</header>
    </template>
    
    <blog-header></blog-header>

    Update 2019

    Now you can use <link rel="stylesheet"> instead of @import url() inside a Shadow DOM.