Web components - Fonts and Material icons not working (@font-face)
Asked Answered
S

1

6

I am trying to create a web component using vue cli 3 by running following command

vue-cli-service build --target wc --name wc-demo 'src/App.vue'

I am facing issue with loading fonts and material icons inside web components.

I am importing roboto-font.scss and material-icon.scss inside style tag of App.vue which is entry for creating web component.

<style lang='scss'>
// @import url('https://fonts.googleapis.com/css?family=Roboto:400,700&display=swap');
@import "~@/assets/fonts/roboto-font/roboto-font.scss";
@import "~@/assets/fonts/material-icons/material-icons.scss";
</style>

Material-icon.scss

$font-dir: "assets/fonts/material-icons/web-font/";

@font-face {
  font-family: 'Material Icons';
  font-style: normal;
  font-weight: 400;
  // src: url('https://fonts.gstatic.com/s/materialicons/v48/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ.woff2') format("woff2");

  src: url("#{$font-dir}flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ.woff2") format("woff2"),
  url("#{$font-dir}flUhRq6tzZclQEJ-Vdg-IuiaDsNa.woff") format("woff");
}

.material-icons {
  font-family: 'Material Icons';
  font-weight: normal;
  font-style: normal;
  display: inline-block;
  line-height: 1;
  text-transform: none;
  letter-spacing: normal;
  word-wrap: normal;
  white-space: nowrap;
  direction: ltr;
}

Roboto-font.scss

$font-dir: "assets/fonts/roboto-font/web-font/";

@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 100;
  src: url("#{$font-dir}KFOkCnqEu92Fr1MmgVxIIzQ.woff") format("woff");
}

Everything works fine when running project using

yarn serve

. Fonts load fine in network tab, but after building web component and loading it in browser using “live server”, the fonts does not load in network tab.

Only way I could make fonts and icons work is by adding link tags to index.html file. But not within web component (within shadow Dom)

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>wc-demo</title>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <link
      href="https://fonts.googleapis.com/icon?family=Material+Icons"
      rel="stylesheet"
    />
    <link
      href="https://fonts.googleapis.com/css?family=Roboto&display=swap"
      rel="stylesheet"
    />
    <title>Document</title>
  </head>
  <body>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script src=“./wc-demo.js"></script>
    <wc-demo></wc-demo>
  </body>
</html>

Can anyone help me to understand how to include fonts within web component or using it in the html file head section is the only option ?

Also I have linked some similar issues that might help you to help many like me :-

https://bugs.chromium.org/p/chromium/issues/detail?id=336876

@font-face not working in polymer/web-components

https://robdodson.me/at-font-face-doesnt-work-in-shadow-dom/

Why doesn't Font Awesome work in my Shadow DOM?

Swig answered 28/1, 2020 at 6:58 Comment(0)
P
2

I am not sure if this question still relevant for the author, but:

I made quite a research regarding this topic and I found out that:

There is no way to import fonts from inside of the web component. Web components are self isolated. They have their own DOM tree. That's basically means that any styles included to head of parent document will not be available inside the web-component.

Only ways to use custom fonts inside the web-component are:

a) Disable shadow dom. Then you can fetch fonts in parent file (e.g. head tag) and use those fonts from inside the web component.

b) Include fonts definitions to both: parent and web-component (which is code duplication).

UPDATE

After some experiments it seems I found the solution. In the following example I am using StencilJS which is tool dedicated for web-components development.

Using javascript / typescript it is possible to fetch the styles node of the shadow root element. To access this element I am using following:

@Element() private element: HTMLElement;

From StencilJS docs:

The @Element() decorator is how to get access to the host element within the class instance. This returns an instance of an HTMLElement, so standard DOM methods/events can be used here.

And now I am able to access styles node and set (import) fronts there. Join the fonts import as a regular string to the style:

this.element.shadowRoot.querySelector('style').innerText += "@import url('https://fonts.googleapis.com/css2?family=Almendra+Display&family=Open+Sans:wght@200&display=swap');";
this.element.shadowRoot.querySelector('style').innerText += "p {font-family: 'Almendra Display', cursive;}";

That is the solution I used for the web-component built using Stencil. I strongly believe same can be achieved using React. And I think it can be done without any framework usage too.

Pimentel answered 11/1, 2022 at 12:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.