New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Custom StyleSheet #2675
Comments
Hi there, nice to see you here - thank you for your great OSS work! I can't wait for throw types to happen 😉 Using If you prepare a PR for such a change with tests then I would happily merge it.
Out of my curiosity - could you describe this in more detail? I love learning about quirky use cases :P |
thanks! but it's likely not to happen because the TypeScript team doesn't approve that feature.
Our application is rendered in the Shadow Root. When a Dialog is opened, it needs to be rendered into another ShadowRoot (in order to have the correct DOM hierarchy to be the frontest DOM). Therefore, our application actually needs to render the same StyleSheet in multiple places. As you can see, this component sits inside a ShadowRoot. And this dialog is in the out-most DOM to show in the top-most. We wrote code in this way: const [open, setOpen] = useState(false)
return <>
// This is rendered as the screenshot 1
<Button onClick={() => setOpen(true)}><Icon /></Button>
// This dialog is rendered as the screenshot 2
{useShadowRootPortal(container => <ComposeDialog open={open} ModalProps={{ container }} />)}
</> So we need to clone styles between different ShadowRoots. My implementation of StyleSheet for our environment is available at ShadowRootStyleSheet.ts On Chrome, we choose to use Constructable StyleSheet to share CSS between different ShadowRoots, on other platforms, we choose to clone those styles manually when Currently, I provided a fake this.container.insertBefore = (child) => {
if (child instanceof HTMLStyleElement) {
child.appendChild = (child) => {
if (child instanceof Text) this.implementation.insertGlobal(child.wholeText)
return child
}
}
return child
}
Thanks! I need to consider if emotion changes to |
I have some other questions about emotion 👀👀
|
I've tried to patch emotion on my project to test if |
In a way - yes. We insert global styled before other styles from the "main" sheet. So yes, changing this could result in different CSS being applied on a page because of how cascade works. Note though that if you have multiple Global styles on a page then their order is not guaranteed - all of them should be inserted before the "main" sheet but it's not obvious which global styles will take precedence over other global styles etc.
We just over-cache stuff - we do not implement any kind of ref-counting etc to clean "unused" styles. Theoretically, those styles might become needed again and in such a case we won't have to reinsert them, we'll just reuse what has already been inserted. Also, note that removing rules has a cost for the browser - so it's not like we are facing an obvious choice here that removing styles is better for performance, it could actually be worse. This ain't 100% true for Global styles because those are removable - and it's also a partial reason why those are inserted into another sheet. This way we can just call
So patching this didn't help you? I think this kinda would be a nice feature to add nevertheless - so I might introduce such a change later on my own. |
Oh, I forgot global styles can be removed... So our patch currently doesn't work correct for this case, because calling
I patched local files and found it will make our code more complex. If it's possible to not create a new StyleSheet, but change the shape of StyleSheet, to add the following methods: insertGlobal(rule: string): void
flushGlobal(): void It will be cleaner on our sides. I can implement those two methods to get the correct behavior without patching |
This gets slightly more complicated because each Global needs its own flushable "slot". So if anything this would have to be implemented: // returns DisposeGlobal
insertGlobal(rule: string): () => void But then it gets harder to actually call So if possible I would really prefer to keep the originally proposed solution of: let sheet = new cache.sheet.constructor({
...
}) I understand that this might complicate stuff a little bit for you but on the other hand, this wouldn't really spill into your app code, the complexity would just be contained to the implemented cache wrapper. So I think it's a reasonable tradeoff here. Could you share a git patch ( |
Umm actually I didn't complete my try on this route because I stopped when I found I already modified too much things. What about this? Add a newGlobalSheet() {
return {
insert(x) { this.implementation.insertGlobal(x) }
flush() { this.implementation.flushGlobal }
}
} Then change This will make implementation on my side much simpler cause I don't need to distinguish if the |
Could you summarize where complexity was? I'd like to understand the problem space really well here before moving towards other solutions. I somewhat don't see why the latest proposal with const emotionCache = createEmotionCache({ key: keyA })
const styleSheet = new MySpecialStyleSheet(keyA, shadow)
emotionCache.sheet = styleSheet You can always assume that all calls to class MySpecialStyleSheet {
// ...
newGlobalSheet() { /* ... */ }
}
const emotionCache = createEmotionCache({ key: keyA })
const styleSheet = new MySpecialStyleSheet(keyA, shadow)
emotionCache.sheet = styleSheet Then you almost always should be able to write something here to make the |
And note that I'm not saying a definitive no to expanding the current implementation etc. For instance, I've always wanted to make those vanilla~ global styles flushable: emotion/packages/css/src/create-instance.js Line 105 in 4266aa0
I would just like to understand your PoV better first - figure out a solution for that and then perhaps move to figure out this other problem. Maybe they are strongly coupled and related and we must solve both at the same time but I don't see that yet. |
Why I propose the As you can see, newGlobalSheet() {
return {
insert(x) { this.implementation.insertGlobal(x) }
flush() { this.implementation.flushGlobal }
}
} in this example, If we change to Example: // @ts-ignore
this.container.sheet = this And then I can read I think any of those two solutions are cool but I prefer the |
Could you prepare pseudocode of what you have there? Then I perhaps could propose a way to do this using the I know that this might require some "hacks" but ultimately I don't think it will be that bad (happy to be proven wrong though). I would just like to first unblock you, even with a somewhat hacky solution, and only then take our time to improve the design that maybe would make it even simpler for you (I'm not committing to it just yet though, cause I'd like to really evaluate our possibilities here and I have a lot on my plate lately). |
thanks! it's no longer blocks us, our new implementation has already landed. it is based on the hack of If emotion chooses to class MySheet {
container = document.createElement('div')
constructor(options) {
if (options.container.sheet) {
return new MyGlobalSheet(this)
} else {
this.container.sheet = this
return new ConstructableSheet(this)
}
}
} |
If emotion choose class ConstructableSheet {
constructor(options) {
// ...
}
newGlobalSheet() {
return new GlobalSheet(this)
}
} This one is simpler on our side and has better semantics, but the |
Would you be open to preparing a PR for the |
emotion/packages/react/src/global.js
Line 94 in ce9873f
Hi! I found emotion in creating a new Sheet based on the old sheet. Is it possible to get rid of this behavior, or use the same constructor as the
cache.sheet
has?I'm handling a very special environment and I must implement my own
StyleSheet
class to insert rules. It's be like this:It works well besides the global styles. Global styles using the emotion's original implementation therefore I cannot control the insertRule behavior.
If I can provide my own StyleSheet implementation, or at least emotion can write this to use my StyleSheet:
it will be great! thanks!
The text was updated successfully, but these errors were encountered: