Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to mount styles inside shadow root using cssinjs/jss

I'm trying to use https://material-ui.com/ components inside shadow dom, and need a way to inject those styles inside shadow dom. by default material-ui, which uses jss under the hood injects styles in the head of the page.

Is that even possible? Can anyone come with an example?

like image 668
bboydflo Avatar asked Jan 29 '19 21:01

bboydflo


People also ask

How do you style elements inside shadow DOM?

The element to which shadow DOM is attached is known as the host. To style the host, use the :host selector. Inheritable properties of the host element will inherit down the shadow tree, where they apply to the shadow children.

What is root in shadow DOM?

The ShadowRoot interface of the Shadow DOM API is the root node of a DOM subtree that is rendered separately from a document's main DOM tree. You can retrieve a reference to an element's shadow root using its Element.

What is shadow root angular?

Shadow root: The root node of the shadow tree. Real Example: example of a "video" element,All you see in the DOM is the "video" element, but it contains a series of buttons and other controls inside its shadow DOM.

What are shadow roots HTML?

The shadow root is a document fragment that is attached to the host element and it has a host property that identifies its host element. The act of attaching a shadow root is how the element gets its shadow DOM.


2 Answers

This is what my web component looks like, it is a web component that renders a react app that contains material-ui styles.

import * as React from 'react';
import { render } from 'react-dom';
import { StylesProvider, jssPreset } from '@material-ui/styles';
import { create } from 'jss';

import { App } from '@myApp/core';

class MyWebComponent extends HTMLElement {
  connectedCallback() {
    const shadowRoot = this.attachShadow({ mode: 'open' });
    const mountPoint = document.createElement('span');
    const reactRoot = shadowRoot.appendChild(mountPoint);
    const jss = create({
      ...jssPreset(),
      insertionPoint: reactRoot
    });

    render(
      <StylesProvider jss={jss}>
        <App />
      </StylesProvider>,
      mountPoint
    );
  }
}
customElements.define('my-web-commponent', MyWebComponent);

Setting the insertionPoint on jss to the actual react root inside the shadow root will tell jss to insert those styles inside that shadow root.

like image 148
Shawn Mclean Avatar answered Jan 03 '23 23:01

Shawn Mclean


Using https://github.com/Wildhoney/ReactShadow to create shadow dom (you could also do it by hand as shown in previous answer), I created a small WrapperComponent that encapsulates the logic.

import root from 'react-shadow';
import {jssPreset, StylesProvider} from "@material-ui/styles";
import {create} from 'jss';
import React, {useState} from "react"

const WrappedJssComponent = ({children}) => {
  const [jss, setJss] = useState(null);
  
  function setRefAndCreateJss(headRef) {
    if (headRef && !jss) {
      const createdJssWithRef = create({...jssPreset(), insertionPoint: headRef})
      setJss(createdJssWithRef)
    }
  }
  
  return (
    <root.div>
      <head>
        <style ref={setRefAndCreateJss}></style>
      </head>
      {jss &&
      <StylesProvider jss={jss}>
        {children}
      </StylesProvider>
      }
    
    </root.div>
  )
}

export default WrappedJssComponent

Then you just need to Wrap your app, or the part of your app you want to shadow inside <WrappedJssComponenent><YourComponent></YourComponent></WrappedJssComponenent>.

Be careful, some of the material-UI component won't work as usual (I had some trouble with

  • ClickAwayListener, maybe because it uses the parent dom, did not investigate more than that to be honest.
  • Popper, and everything that will try to use document.body as container will not have access to jss defined in shadow node. You should give an element inside the shadow dom as container.
like image 38
Robin Leclerc Avatar answered Jan 04 '23 00:01

Robin Leclerc