Summary
There is no public API to make styled / css / createGlobalStyles inject their generated <style> into a document other than the main document.head. styled-components solves this with <StyleSheetManager target={node}>. solid-styled-components has no equivalent, which breaks any multi-document rendering:
- Electron child
BrowserWindow rendered via <Portal mount={childWin.document.body}> — the child document never receives the goober sheet, so all styled components render unstyled there. iframe content portals.- Shadow DOM roots.
- Pop-out windows opened with
window.open.
Why this is almost already supported
The internals already thread a target through to goober:
// src/index.js — makeStyledfunctionmakeStyled(tag){let_ctx=this||{};return(...args)=>{constStyled=props=>{
...
letclassName=css.apply({target: _ctx.target,o: append,p: withTheme,g: _ctx.g},args);
...goober's css/glob honor this.target and write the sheet into that node's root (document.head or a shadow/other-document root). But styled is exported as:
exportconststyled=newProxy(makeStyled,{get(target,tag){returntarget(tag);}// makeStyled called with `this === undefined`});so _ctx.target is always undefined → everything lands in the main document.head. There is no way for a consumer to set it.
Reproduction
import{render,Portal}from"solid-js/web";import{styled}from"solid-styled-components";constBox=styled.div`background: tomato; width: 100px; height: 100px;`;// open a second window/documentconstwin=window.open("about:blank")!;render(()=>(<Portalmount={win.document.body}><Box/>{/* renders, but the .go<hash> rule is injected into the MAIN document's <style>, never into win.document → unstyled */}</Portal>),document.getElementById("root")!);The <div class="go…"> appears in win.document, but the matching CSS rule is only in the opener document's <head>, so the box is unstyled in the child window.
keyframes and glob/createGlobalStyles have the same limitation (they also inject into the default sheet).
Proposed API
Mirror styled-components:
import{StyleSheetManager}from"solid-styled-components";<Portalmount={win.document.body}><StyleSheetManagertarget={win.document.head}><App/></StyleSheetManager></Portal>All styled / css.class / createGlobalStyles rendered inside the provider inject into target instead of document.head.
Sketch
A context carrying the target, read inside Styled (and the createGlobalStyles component), passed through to the existing css.apply({ target, … }) call:
constStyleTargetContext=createContext();exportfunctionStyleSheetManager(props){returncreateComponent(StyleTargetContext.Provider,{value: props.target,getchildren(){returnprops.children;}});}// inside Styled():consttarget=useContext(StyleTargetContext)??_ctx.target;letclassName=css.apply({ target,o: append,p: withTheme,g: _ctx.g},args);Because goober already routes by target, this is a small, backward-compatible change (no behavior change when no provider is present).
Workaround today
We currently mirror every <style>/<link> from the opener document.head into the child window's head with a MutationObserver, and additionally shim keyframes so @keyframes bodies aren't dropped. It works but is fragile and duplicates the whole sheet per window.
Offer
Happy to send a PR implementing StyleSheetManager (context-threaded target) plus keyframes/glob target support, with tests. Filing this first to confirm the API shape (<StyleSheetManager target> vs a setup-level option) you'd prefer.
Summary
There is no public API to make
styled/css/createGlobalStylesinject their generated<style>into a document other than the maindocument.head.styled-componentssolves this with<StyleSheetManager target={node}>.solid-styled-componentshas no equivalent, which breaks any multi-document rendering:BrowserWindowrendered via<Portal mount={childWin.document.body}>— the child document never receives the goober sheet, so allstyledcomponents render unstyled there.iframecontent portals.window.open.Why this is almost already supported
The internals already thread a
targetthrough to goober:goober's
css/globhonorthis.targetand write the sheet into that node's root (document.heador a shadow/other-document root). Butstyledis exported as:so
_ctx.targetis alwaysundefined→ everything lands in the maindocument.head. There is no way for a consumer to set it.Reproduction
The
<div class="go…">appears inwin.document, but the matching CSS rule is only in the opener document's<head>, so the box is unstyled in the child window.keyframesandglob/createGlobalStyleshave the same limitation (they also inject into the default sheet).Proposed API
Mirror
styled-components:All
styled/css.class/createGlobalStylesrendered inside the provider inject intotargetinstead ofdocument.head.Sketch
A context carrying the target, read inside
Styled(and thecreateGlobalStylescomponent), passed through to the existingcss.apply({ target, … })call:Because goober already routes by
target, this is a small, backward-compatible change (no behavior change when no provider is present).Workaround today
We currently mirror every
<style>/<link>from the openerdocument.headinto the child window's head with aMutationObserver, and additionally shimkeyframesso@keyframesbodies aren't dropped. It works but is fragile and duplicates the whole sheet per window.Offer
Happy to send a PR implementing
StyleSheetManager(context-threadedtarget) pluskeyframes/globtarget support, with tests. Filing this first to confirm the API shape (<StyleSheetManager target>vs asetup-level option) you'd prefer.