-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Problems surrounding SSR injection of <style> and unreliability of :first-child selectors #1178
Comments
Considering that Emotion knows if it's being server-rendered, it could even replace |
I don't think we can simply move the styles to after the elements as that would trigger the flash of unstyled content until the styles are parsed. I agree this is a pain in the rear, but I can't think of immediately obvious solutions. Does anyone have experience with other css-in-js libraries, how are they solving this problem, and is it actually solvable. Rewriting of selectors sounds like a possible solution but it also sounds dangerous. |
Another possible solution would be to wrap the components into an additional |
Do you have time to create a simple example of how this would look like? |
I mean:
the |
Hmm I don't think that would help, the
This solves the problem of the |
Glad this spurred some discussion, and thanks for chiming in. Replacing with Using
This came up in outside conversation, so I started looking for a reference to confirm. Do you know of one? If you're right (I suspect you are), I suppose that makes the first problem in this issue moot. |
It needs to work in both modes, the problem at hand here is FOUC. The SSR styles are being moved at runtime to head - as emotion in browser works the same, it doesnt need to know much if the html was SSRed or not. Because of this the rules would have to rewritten both on server and on the client so they produce the same hash for caching purposes. I think those rules CAN be rewritten reliably - it's just not super easy and need some effort into writing the smart-ish parser and writing comprehensive tests to ensure we doesnt break anything. |
I was actually wrong, apologies for providing false information. It looks like there is no FOUC with the inline style tags, at least not in Chrome and Firefox. I was not able to find any conclusive info on it, though. |
Have you tried slower connections? Maybe one of the options to avoid FOUC currently would be also to put this: emotion/packages/cache/src/index.js Lines 69 to 84 in 2971dba
into inline script tag 🤔 |
Sorry if I was not clear, I meant that I was not able to produce a FOUC with the style tags placed after the HTML they are targetting. |
Example reproduction: https://s.codepen.io/OriginalEXE/debug/vvqbrb/vPAKKzQzPgVA I have checked the rendering in chrome and the very first frame already includes the styled paragraph. |
Oh, the issue here is different though - FOUC in this case is caused purely by the fact that we render style tags together with elements and this messes up mentioned pseudo classes ( |
I understand that. But the thread author initially proposed a solution of moving the style tags after the elements, hence solving the issue of the |
Oh, I've misunderstood you then. If we can't confirm FOUC for this - I would be in favour of switching the order of those, technically this would be a breaking change though ;s |
If we can find a solution that doesn't make any selectors unreliable, that'd be great! Thank you for the reproduction, @OriginalEXE! I'll investigate other browsers to confirm behavior there, too. |
Yup, I think it would be a better solution, but it's definitely a breaking change. I am trying to dig up whether it would have any performance implications. |
Here is a simplified version: https://s.codepen.io/OriginalEXE/debug/yGdrrL/mVkbGpLpGPvM I was not able to reproduce FOUC this way either. I wonder however whether anything changes with a more complex DOM. Also, I believe not reproducing the FOUC should not be the only thing we consider before making any changes, performance implications should be tested as well. |
Hello everyone. Just weighing in with some comments and questions... I appreciate the simplicity and relative elegance of Emotion’s SSR strategy, but I believe that the trade-off of “breaking” many commonly used CSS pseudo selectors to not be worth it. Some pseudo selector usages can be changed to an alternate, but oftentimes there are others that get unwieldy fast or simply cannot be accommodated. In any of the cases, the updated implementations are unlikely to be the best choice or most easily understood. Further, the console error that suggests to try using nth-of-type is also not very helpful, particularly for those not even doing SSR. #1105. May I ask what was the impetus to change from the previous SSR implementation? Performance, reliability, usability, difficulty, maintenance? I did not use it and therefore only have so much perspective on it. It seems that if it did not suffer from these issues, that perhaps it was a superior solution, albeit perhaps less elegant. Personally speaking, I would much rather have to deal with a bit of extra SSR configuration code as it is often a one time cost and limited in scope. Is it possible to opt-out of the new SSR implementation for those cannot accommodate the concessions? I see that it is documented that the old SSR API’s can be used with Emotion 10, however with the caveat that is should only be done for compatibility and migration purposes. I can’t imagine that you would want to support multiple API’s long term though. It also would not address #1105. A few comments on some of the other proposed ideas...
|
I don't think rewriting selectors is a good idea because it's confusing when debugging and also complex and error-prone. The server-rendering of Emotion 10 unfortunately also messes up the following selector:
I agree with @brentertz that sacrificing common CSS selectors for easier setup is not worth it. At least not for us. |
Yep. @maximilianschmitt Just launched a new site and we're using Emotion 10 with a utility class for "lobotomized owl" (the
The second selector is basically saying "Take the second child of .owl which is next to a style and lets just remove top margin now because when emotion js kicks in and removes style, the second element will become the first". This fixes the shifting. Just wanted to put this solution here for others that stumble upon this |
To stop the FOSC (Flash-Of-SSR'd-Content) when using the labotomized owl, I found this selector to be useful: *:not(style[data-emotion-css]) + * {
margin-top: 1em;
} Edit, but there's also a case where the |
It looks like most of the issues can be avoided by doing the following for a React app: import createEmotionServer from "create-emotion-server";
import { CacheProvider } from "@emotion/core";
import createCache from "@emotion/cache";
let cache = createCache();
let { extractCritical } = createEmotionServer(cache);
let { html, css, ids } = extractCritical(
renderToString(
<CacheProvider value={cache}>
<App />
</CacheProvider>
)
);
let styleTag = `<style data-emotion-css="${ids.join(" ")}">${css}</style>`; (Thanks @mitchellhamilton for the suggestion) |
@jesstelford That's what I will do also. Tried to rewrite a few selectors, but it's a pain. So a combination of I can't imagine basic CSS being sacrificed for the sake of magic SSR. People doing serious SSR are usually not the least skilled and people using NextJS for SSR are being pushed to styled-jsx by default anyway. |
Cheers @jesstelford! Probably a dumb question as I'm fairly new to tinkering with SSR (I'm currently stuck on this issue too), but am I right in thinking that If this is the case, would it be worth my time raising a PR to update the documentation? I've been quite stuck trying to resolve this with the current docs |
A great podcast outlining some problems with those selectors (among other things) - http://www.fullstackradio.com/134 |
I assume that the advanced approach (https://emotion.sh/docs/ssr#advanced-approach) wont work with setups for nextjs ? as they dont expose the |
awesome thanks @azizhk |
* Improved docs around SSR apis Fixes emotion-js#1178 * Some changes * Add the client cache bit back
I experienced this warning today for the first time But the warning is very misleading, and it's not explicit that it comes from Emotion either. Using the term Could it be possible to:
Couldn't Emotion rewrite those CSS rules, e.g: If not possible to rewrite, could a solution be given within the warning (or its link)? |
Yes, those are possible. I would be open to merging PRs improving any of those points. As to rewriting CSS rules - it's hard to cover all possible use cases and we probably wouldn't like dive into this unless someone provides a quality PR for this (then we could start considering it). What we try to avoid here is potential FOUC and thus a simple replacement like you have proposed is not enough. This could work on the assumption that |
Is there an existing webpage where this issue is explained thoroughly with developer experience in mind that could be printed alongside the warning? Or is it something that needs to be created? Does using |
It's vaguely mentioned here but a dedicated page or a new section should be created that would go into more details.
It probably triggers it right now as it's currently just a very simple check. |
- It's an upstream bug in emotion (one which I encountered before, as well, when migrating from theme-ui) - Long term the solution is MUI's responsibility, if that is what's managing the root EmotionCache - Short term, the fix is `display: none` on a CSS selector for the any `style` tag with attribute `data-emotion` starting with `css` Partially related thread (I haven't looked for the root cause yet): emotion-js/emotion#1178 CU-#1c2283e CU-#1c1z13c
- It's an upstream bug in emotion (one which I encountered before, as well, when migrating from theme-ui) - Long term the solution is MUI's responsibility, if that is what's managing the root EmotionCache - Short term, the fix is `display: none` on a CSS selector for the any `style` tag with attribute `data-emotion` starting with `css` Partially related thread (I haven't looked for the root cause yet): emotion-js/emotion#1178 CU-#1c2283e CU-#1c1z13c
Hey everyone, I just wanted to share a way to recreate the
This will select the first of any element that is not style and has adjacent siblings. This selector can also have an element or class like this
|
Problem description:
In emotion v10+, a new warning is thrown if you use
:first-child
,:nth-child
, or:nth-last-child
selectors in a styled component or css prop value. This is because, when using SSR, thestyle
element is injected immediately above (prepended) the associated component. (More details.)There are a number of problems with this approach:
:first-child
to:first-of-type
, only works if all sibling elements are of the same type. This is probably fairly common for components like lists, grids, etc... But it's far from a guaranteed solution. It could also discourage the use of semantic markup by driving devs to "just use divs" for everything, since it's a simpler "fix".* + [anything]
, is unreliable, but not listed.:only-child
is unreliable, but not listed.:nth-last-child
is still reliable when prepending thestyle
element and should not be listed.Suggested solution:
Let's tackle each problem, in turn:
"First" vs. "last" selectors (or prepending vs. appending the
style
element)If emotion injected the
style
element after (appended) the associated component, then the following selectors would be unreliable::last-child
,:nth-last-child
,:only-child
.Compare that to the currently unreliable selectors:
* + [anything]
,:first-child
,:nth-child
,:only-child
.By changing to appending the
style
element, emotion would no longer block use of the common adjacent sibling selector and the "last" (rather than "first") varieties of the selectors would be affected, which I would argue is a better tradeoff more in line with actual usage of each [citation needed]."of-type" workaround
The warning's suggestion should be reworded to make clear that it may not work in all scenarios.
Incorrect unsafe list of selectors
:only-child
needs to be added (changing this to:only-of-type
is a good workaround, though!style
element remains prepended* + [anything]
selector pattern needs to be flagged:nth-last-child
needs to be removedstyle
element is appendedMute the unsafe selectors warning
At the very least, emotion should allow consumers to do so.
Alternative solutions:
Another suggested workaround is to somehow replace
:first-child
selectors with:first-child:not(style), style:first-child + *
in the styles output. While a clever bit of CSS selecting, this suggestion has two main problems I can think of: 1) it won't work for non-:first-child
selectors (though maybe we could come up with other rewrites for the others) and 2) replacing:first-child
in a selector is not trivial, as it is affected by whether or not its used on the immediate element being styled or a descendent element.The maintainers of emotion and its community could also simply decide that making these selectors unreliable is not worth the tradeoff of SSR "just working" and abandon the approach of injecting colocated
style
elements entirely. I have no idea how palatable such a change would be, but wanted to enumerate all the options.This issue is intended to open discussion around these problems. If we can decide on forward actions, I'm happy to help implement wherever I can.
The text was updated successfully, but these errors were encountered: