Why Doesn't Font Awesome Work in My Shadow Dom

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

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>

What is #shadow-root, and why does it put display none on my font awesome classes?

Do you have AdBlock extension installed? That might hide your elements.

How to load @font-face in dynamically loaded styles of Web Component with shadow DOM

It turned out that browsers don't support loading @font-face CSS rules in shadow DOMs as of September 2020. The Chrome team seems to be working on this, though.

The approach I took was to look for @font-face rules in the dynamically created stylesheet, modify them a bit to get relative paths working and then add them to the <head> of the page with a script. If you add the @font-face rules to the head and the shadow DOM, the fonts will be loaded and applied.

Here's the code:

for (const style of styles) { // styles is an array of urls
const stylesheet = document.createElement('link');
stylesheet.setAttribute('rel', 'stylesheet');
stylesheet.setAttribute('href', style);
stylesheet.setAttribute('type', 'text/css');
stylesheet.onload = (event) => {
for (
let i = 0;
i < event.currentTarget.sheet.cssRules.length;
i++
) {
if (event.currentTarget.sheet.cssRules[i].type == 5) { // type 5 is @font-face
const split = style.split('/');
const stylePath = split
.slice(0, split.length - 1)
.join('/');
let cssText =
event.currentTarget.sheet.cssRules[i].cssText;
cssText = cssText.replace(
// relative paths
/url\s*\(\s*[\'"]?(?!((\/)|((?:https?:)?\/\/)|(?:data\:?:)))([^\'"\)]+)[\'"]?\s*\)/g,
`url("${stylePath}/$4")`
);

const st = document.createElement('style');
st.appendChild(document.createTextNode(cssText));
document
.getElementsByTagName('head')[0]
.appendChild(st);
}
}
};
this.root.appendChild(stylesheet);
}

It works fine on Edge 85 (Chromium based).

Angular with FontAwesome: cannot style path

Angular offers by default encapsulation (emulation) of styles. It means each component is independent, and you won't have any conflict between 2 components in the same page. (if they use the same class name for instance).

https://angular.io/guide/component-styles

From Angular official documentation :

View encapsulation

As discussed earlier, component CSS styles are encapsulated into the component's view and don't affect the rest of the application.

To control how this encapsulation happens on a per component basis, you can set the view encapsulation mode in the component metadata. Choose from the following modes:

  • ShadowDom view encapsulation uses the browser's native shadow DOM implementation (see Shadow DOM on the MDN site) to attach a shadow DOM to the component's host element, and then puts the component view inside that shadow DOM. The component's styles are included within the shadow DOM.

  • Native view encapsulation uses a now deprecated version of the browser's native shadow DOM implementation - learn about the changes.

  • Emulated view encapsulation (the default) emulates the behavior of shadow DOM by preprocessing (and renaming) the CSS code to effectively scope the CSS to the component's view. For details, see Appendix 1.

  • None means that Angular does no view encapsulation. Angular adds the CSS to the global styles. The scoping rules, isolations, and protections discussed earlier don't apply. This is essentially the same as pasting the component's styles into the HTML.

To style an element included inside a component, (and if this feature not provided by the component library it self), then you have 2 options :

1) Add your style to global styles.css

There is not any encapsulation for styles defined inside global styles.css.

In your component:

<fa-icon class="my-global-icon" [icon]="faFacebook"></fa-icon>

In your styles.css or styles.scss:

fa-icon.my-global-icon path {
fill: red;
}

2) Disable encapsulation emulation

By default, encapsulation is active (emulated) on styles defined inside a component.
To disable it, you should set encapsulation = ViewEncapsulation.None

@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ],
encapsulation: ViewEncapsulation.None
})
export class AppComponent {
...

In this case, you will be able to style an sub element included in direct child of your component. But be careful, you could encounter other problems with style conflict. It's your job now to manage them.

Hope it will help.

Why should @font-face be declared at global scope, outside of shadow DOM?

It is still an open draft as of May 2021. The entire discussion can be found here: https://github.com/w3c/csswg-drafts/issues/1995



Related Topics



Leave a reply



Submit