Open seesharper opened 6 months ago
I'm wondering the same, no styles seems to be included in the shadow dom
First of all, this would entirely depend on how your bundler/build tool deals with CSS imports, and the constraints you have in your project. With that said, you can make it work, but at the moment it looks like you must roll your own solution.
In my situation, I am using esbuild
directly, and one of my constraints is that I must end up with a single JS file as output from the bundler. I found an excellent plugin for the Bun bundler called bun-lightningcss. I use esbuild
, but it was dead simple to modify it so that it works with esbuild
, since Bun's API is almost identical.
Without modifications, the bun-lightningcss
plugin does a couple things:
inject-styles
. It exports a function injectStyles
that adds all CSS code passed onto it into a <style>
tag appended to the document's head. It also adds an ID to this tag.lightningcss
, and a JS file is generated which calls injectStyles
with the transpiled CSS at runtime. It also does some other stuff like set up the exported object for CSS modules.After modifying the plugin so that it worked with esbuild
, all I had to do was to extend the custom element class returned by r2wc
so that it cloned the <style>
tag from the host document and appended it into the shadow DOM whenever the custom element was constructed. If you see the source code for r2wc
, you can see you can get it with this.container
:
class MyCustomElement extends r2wc(MyComponent) {
constructor() {
super();
this.container.append(document.getElementById('bun_lightningcss').cloneNode(true));
}
}
It works like a charm and fits my use case perfectly. But you might have to find a way to do this with the tools you use if the solution doesn't exist yet.
Almost same as https://github.com/bitovi/react-to-web-component/issues/172#issuecomment-2075776780, In case of mine, below example is worked fine.
vite
vite-plugin-css-injected-by-js
to inject css in bundle file.connectedCallback
instead of cunstructor
(in my case, constructor not working)
import { defineConfig } from "vite";
import cssInjectedByJsPlugin from "vite-plugin-css-injected-by-js";
export default defineConfig({
plugins: [
react(),
cssInjectedByJsPlugin({ styleId: "
// other config here });
2. Wrap r2wc
export const convertReact2WebComponent = (
Component: Parameters
class WebComponentWithStyle extends WebComponent {
connectedCallback() { // 2. Use connectedCallback instead of constructor (this can be changed by your usecase.)
const styleTag = document.getElementById("
return WebComponentWithStyle; };
3. Define Web component using wrapped r2wc
const webComponent = convertReact2WebComponent(Component, { props, shadow: "open", }); customElements.define(name, webComponent);
I hope this helps.
Similar to ^but scoped example:
import r2wc from "@r2wc/react-to-web-component";
import App from "./App";
class StyledHelloWC extends r2wc(App, {
props: { name: "string" },
shadow: "open",
}) {
connectedCallback() {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
super.connectedCallback();
// window.__styles is injected by vite-plugin-css-injected-by-js
if (window.__styles) {
const template = document.createElement("template");
template.innerHTML = `<style id="vite-plugin-css-injected-by-js">${window.__styles}</style>`;
this.shadowRoot?.appendChild(template.content.cloneNode(true));
}
}
}
customElements.define("hello-tailwind-wc", StyledHelloWC);
And in [vite.config.ts](https://github.com/wataruoguchi/poc-spa-gh-pages/blob/main/packages/react-tailwind-fragment/vite.config.ts)
I have
plugins: [
...defaultConfig.plugins,
cssInjectedByJsPlugin({
injectCode: (cssCode: string) => {
return `window.__styles = ${cssCode}`;
},
}),
],
When we pass
shadow : 'open'
orshadow : 'closed'
we would probably expect the styles to be included in the shadow root. Is there any way to accomplish this?React component
To create the web component
Usage