While developing custom web components with Vue 3 for use in various web applications, I encountered an issue related to importing fonts and Google material icons due to the shadow-root. Currently, my workaround involves adding the stylesheet link tag to the application header during the registration of the web components. Although this method works, I am concerned that it undermines the encapsulation concept of web components and could potentially impact the styles of the application utilizing my custom web components.
Below is my current implementation:
import { defineCustomElement as VueDefineCustomElement, h, getCurrentInstance } from 'vue';
export function register() {
// Icons
loadStylesheet(
'https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="d79085969397e5e7f9f9e3ef">[email protected]</a>,100..700,0..1,-50..200'
);
// Fonts
loadStylesheet('https://fonts.googleapis.com/css2?family=Lato:wght@400;500;600;700&display=swap');
// Components
customElements.define('comp', defineCustomElement(comp1));
...
}
export const defineCustomElement = (component) => {
...
return VueDefineCustomElement({
setup() {
... // Data injection
return () => h(component);
},
});
};
// Function to add the stylesheets to the application's head tag
function loadStylesheet(href) {
let existingNode = null;
for (let i = 0; i < document.styleSheets.length; i++) {
if (document.styleSheets[i].href && document.styleSheets[i].href.indexOf(href) > -1) {
existingNode = document.styleSheets[i].ownerNode;
break;
}
}
if (existingNode) {
return;
}
let linkTag = document.createElement('link');
linkTag.rel = 'stylesheet';
linkTag.href = href;
document.getElementsByTagName('head')[0].appendChild(linkTag);
}
In the root component, I have done the following:
@import url('../index.css');
@import url('https://fonts.googleapis.com/css2?family=Lato:wght@400;700&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="93d4c1d2d7d3a1a3bdbda7ab">[email protected]</a>,100..700,0..1,-50..200');
:host {
font-family: Lato, Arial, Helvetica, sans-serif;
}
I have considered downloading the font and using SVGs instead of icons, but I'm worried that this approach might increase the bundle size.