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
Css3Pie in MVC, Where to Place the Pie.Htc File
Which Is Better to Use in CSS, Percentage or Pixels
Media Queries for Tablet Min-Resolution and Max-Resolution
How to Load CSS Rules Dynamically in Webkit (Safari/Chrome)
Ie11 Making Background Image Text Blurry
How to Do Browser Specific Conditional CSS Inside a *.CSS File
Create CSS for Internet Explorer Only
CSS Set Width to Fill % of Remaining Area
Instagram New Logo CSS Background
How to Change Default Bootstrap Fluid Grid 12 Column Gutter Width
Google Chrome, Flash and Z-Index Wrong Behaviour
:Last-Child Pseudo Class Selector in CSS and Internet Explorer
How to Do Text-Overflow: Ellipsis on Two Lines
Concatenate String in Less in Loop
Maintain Aspect Ratio of a Div According to Height
Change a Text Input's Value with CSS