Skip to content
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

Ability to insert styles at a specific position in <head> #2037

Closed
jdb8 opened this issue Oct 8, 2020 · 18 comments · Fixed by #2521
Closed

Ability to insert styles at a specific position in <head> #2037

jdb8 opened this issue Oct 8, 2020 · 18 comments · Fixed by #2521

Comments

@jdb8
Copy link

jdb8 commented Oct 8, 2020

The problem

I see that @emotion/cache provides a container option, which seems like it's useful for having some control over the DOM element that emotion inserts its <style> nodes into. Unfortunately, I can't find a way to use this for my purpose, which is to control the position of the inserted nodes in the document head.

The reason I need this is to have more fine-grained control over rule ordering in a page that has existing styles (which are sadly often composed with the newer Emotion-generated classnames). I'd like Emotion (both on the server and client) to insert all of its <styles> (both those initially on the page as well as those dynamically inserted later on) at a specific position in the <head>.

Initially I thought I could use the container option, but that seems to have two problems here:

  • it deals only with nesting inside a container, not adjacent to, and...
  • it doesn't seem like there are any compatible container elements that accept <style> children in the <head>

Together, these two issues make it hard to find a way to do what I want here with any existing Emotion apis.

Proposed solution

Webpack's style-loader provides an insert option which can be given a function to be run in order to determine the position of any inserted styles. Something like this might work here too.

Alternative solutions

Another option might be to introduce a more flexible version of container - not a full function like style-loader, but an insertAdjacentTo, replaceCommentWith or something similar to allow for more control over the placement?

Additional context

Also, based on my problem statement - if there's any existing way to do what I want in Emotion 10, it goes without saying that I'd be very grateful to hear so :)

@Andarist
Copy link
Member

Andarist commented Oct 8, 2020

it doesn't seem like there are any compatible container elements that accept <style> children in the

That's not quite true - probably some validators might yell at you, but all browsers supports div elements within the head.

Also, based on my problem statement - if there's any existing way to do what I want in Emotion 10, it goes without saying that I'd be very grateful to hear so :)

It's not possible with Emotion 10 and we do not plan to introduce such an option. However, Emotion 11 - which is just around the corner - has a new prepend option that should fix your problem. Yes - it's a boolean so either your styles are appended or prepended to the container but I don't think much more control is needed over this.

@jdb8
Copy link
Author

jdb8 commented Oct 8, 2020

That's not quite true - probably some validators might yell at you, but all browsers supports div elements within the head.

I had tried with a <div> in the head but saw some weird-looking behaviour where Chrome would show all subsequent <head> elements as if they were in the <body> (when looking at the elements tab in dev tools) - it was unclear if this was just a quirk of devtools or would actually change behaviour for browsers loading the page (or other scripts making assumptions about where elements are located in the page). After posting this issue, I did start playing around with using a meta tag as a container (!) and sure enough it seems to work in the browsers I tested, without the same quirk (but feels even weirder given that meta is a void element in the spec 😱

I'll probably stick with the meta approach - at least until I run into any insurmountable issues - since it seems to work and lets me hydrate to that container, but it's good to hear that Emotion 11 will offer a bit more control. I suppose your suggestion with the Emotion 11 prepend boolean would be to stick with document.head but choose to prepend, which would ensure that all emotion tags would end up at the start of <head> - am I understanding that correctly?

@Andarist
Copy link
Member

Andarist commented Oct 8, 2020

I suppose your suggestion with the Emotion 11 prepend boolean would be to stick with document.head but choose to prepend, which would ensure that all emotion tags would end up at the start of - am I understanding that correctly?

Yes, pretty much that 😃

@gregjacobs
Copy link

I have another use case for this where we need Emotion's CSS classes to have lower specificity than a user's own CSS classes. We're building a library using Emotion where we want a user's CSS classes to be able to override the styles of our library components.

I noticed that the StyleSheet class (inside @emotion/sheet) has a before property, initially assigned to null, that could easily be provided as an option to the constructor instead.

One could provide head.firstElementChild as the before option in this case to prepend Emotion's <style> tag inside the <head>. This new option would also give the flexibility to place Emotion's <style> tag anywhere in the <head> too relative to another element. Thinking that the interface could be as simple as:

import { createEmotion } from '@emotion/create-emotion';

const emotion = createEmotion({
    container: document.head,
    insertBefore: document.head.firstElementChild
});

Is this a possible change to make in v10? If so, I'd be happy to submit a PR.

@Andarist
Copy link
Member

Is this a possible change to make in v10? If so, I'd be happy to submit a PR.

We do not plan to introduce this to v10. As mentioned - v11 has the prepend option that solves this problem and I plan to release v11 as stable on 11.11.2020, so 🔜

@gregjacobs
Copy link

@Andarist kk, thx, good to know that v11 is close then.

Quick question if you happen to know the answer: are there any performance issues in browsers caused by prepending a <style> element and rules in front of existing <style> or <link rel="stylesheet"> elements? (as opposed to appending the rules like the current behavior of Emotion?)

<html>
    <head>
        <style data-emotion="css"></style>        <!-- exists second -->
        <link rel="stylesheet" href="sheet.css">  <!-- exists first -->
    </head>
    <body>
        ...
    </body>
</html>

Do you know of any performance issues either during initial page load, or at a later time by dynamically prepending rules in front of maybe a few thousand existing rules?

@Andarist
Copy link
Member

I don't know about any perf issues regarding that but I haven't really ever thought about this. My intuition would be that there shouldn't be much of a difference between appending & prepending but I might be wrong.

@gregjacobs
Copy link

Ah ok, no worries, I'll try to do some testing and will let you know if I find anything

@gregjacobs
Copy link

@Andarist Did some testing and didn't find any difference in re-render performance in Chrome with prepending, at least in the case that styles are prepended after initial page load. Still need to do a test for initial page load with regular stylesheets + prepending, and in other browsers.

@gregjacobs
Copy link

Does the new prepend option in v11 cover this use case?

@Andarist
Copy link
Member

I believe it does - we dont intend to introduce more control over insertion point than what is already provided in v11

@oliviertassinari
Copy link

@Andarist is the motivation for rejecting the feature in #2037 (comment) lack of bandwidth or concern about the extra complexity involved?

@Andarist
Copy link
Member

Andarist commented Jun 1, 2021

@oliviertassinari mainly about the added complexity. We can reconsider this though - the new API (option?) would have to replace prepend (it would be deprecated) because maintaining multiple options for a very similar thing is not something that I'd like to do. If you care about this then please start a new issue with the initial proposal for the feature.

@oliviertassinari
Copy link

oliviertassinari commented Jun 1, 2021

@Andarist OK, We might POC around to explore the feasibility of bringing this with a PR in the future.

At this point, MUI v5 has about 1% of adoption of v4, so we will hear more about the need for this feature as more developers migrate. We have recently seen an interesting use case with Tailwind, where the TW CSS reset needs to apply before MUI but the TW classes after MUI. Sure we could say that, nop don't use TW with MUI but I don't think that it would be the best strategical direction. Better embrace the alternatives options and make the ones you propose more appealing.

@DominikSerafin
Copy link

DominikSerafin commented Dec 2, 2021

@Andarist @oliviertassinari, I'm migrating to MUI v5 and this became a bit of a problem for me. My styles insertion order should be like this:

  1. Custom reset styles (similar to MUI <CssBaseline>, but with some additional custom resets)
  2. MUI emotion styles
  3. Custom styles (that include overwrites for MUI styles)

Now, this is fairly straightforward during SSR, since I can control where the styles from emotion cache are inserted. However, during CSR there's no way to control where the styles are inserted inside the <head> (other than prepend option, which doesn't help here).

My workaround was to move styles into <body> and use <div> container for emotion styles, but I'd prefer to keep them in <head>.


Sure we could say that, nop don't use TW with MUI but I don't think that it would be the best strategical direction. Better embrace the alternatives options and make the ones you propose more appealing.

I'm not using Tailwind, but my use case is pretty similar, so I agree with this 💯%.

@Andarist
Copy link
Member

Andarist commented Dec 2, 2021

However, during CSR there's no way to control where the styles are inserted inside the (other than prepend option, which doesn't help here).

Since this got merged in and shipped this statement is no longer true. I'm not sure if this option has been exposed in MUI though - you'd have to look there for an answer to that.

@DominikSerafin
Copy link

DominikSerafin commented Dec 2, 2021

@Andarist, I'm using @emotion/cache@11.6.0 directly (as per MUI docs), and the insertionPoint option works great, thank you! Although, it seems docs don't have it documented (yet?).


There's also another somewhat related issue, which probably warrants a new issue in MUI repo (but I don't currently have time to properly submit it with minimal repro, etc.), but just FYI:

Some animations (seems to be from MUI LinearProgress) are still appended at the end of <head>, regardless if prepend, container or insertionPoint is configured.

image

And on the server render, these animations are at the beginning of constructStyleTagsFromChunks output (which seems OK):

image

@oliviertassinari

@Andarist
Copy link
Member

Andarist commented Dec 2, 2021

Interesting - would love to get a repro case for that, I could then investigate this quickly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants