mui / material-ui

MaterialΒ UI: Ready-to-use foundational React components, free forever. It includes Material UI, which implements Google's Material Design.
https://mui.com/material-ui/
MIT License
91.86k stars 31.57k forks source link

[RFC] v5 styling solution πŸ’… #22342

Closed mnajdova closed 3 years ago

mnajdova commented 3 years ago

This RFC is a proposal for changing the styling solution of Material-UI in v5.

TL:DR; the core team proposes we go with emotion

What's the problem?

What are the requirements?

Whatever styling engine we choose to go with we have to consider the following factors:

It would be nice if it can support the following:

What are our options?

Comparison

Performance

Here are benchmarks with dynamic styles of several popular libraries (note the Material-UI v4 only use static styles which have good performance):

PR for reference: https://github.com/mnajdova/react-native-web/pull/1

Based on the performance, I think that we should eliminate: JSS (currently wrapped in @material-ui/styles), styletron, and fela. That would leave us with:

Dynamic props

Based on the open issues, it seems that Aphrodite doesn't support dynamic props: https://github.com/Khan/aphrodite/issues/141 which in my opinion means that we should drop that one from our options too, leaving us with:

npm

While styled-components and emotion are both libraries are pretty popular, react-styletron at the time or writing is much behind with around 12500 downloads per week (this in my opinion is a strong reason why we should eliminate it, as if we decide to go with it, the community will again need to have two different styling engine in their apps).

Here is the list rang by the number of Weekly downloads at the time of writing:

Note that storybook has a dependency on emotion. It significantly skews the stats.

Support concurrent mode

SSR

Stars

Trafic on the documentation

SimilarWeb estimated sessions/month:

Users feedback

Based on the survey, 53.8% percent are using the Material-UI styles (JSS), which is not a surprise as it is the engine coming from Material-UI. However, we can see that 20.4% percent are already using styled-components, which is a big number considering that we don't have direct support for it. Emotion is used by around 1.9% percent of the developers currently based on the survey.

Having these numbers we want to push with better support for styled-components, so this is something we should consider.

Browser support

Bundle size

What's the best option?

Default engine

Even if we decide to support multiple engines, we would still need to advocate for one by default and have one documented in the demos.

styled-components

Pros:

Cons:

emotion

Pros:

Cons:

Support multiple

We may try to support multiple CSS-in-JS solutions, by providing our in house adapters for them. Some things that we need to consider is that, that we may have duplicate work on the styles, as the syntax is different between them (at least jss compared to styled-components/emotion). We will reuse the theme object no matter what solution we will pick up.

The less involved support for this may come from the usage of the styled, as people may do some webpack config to decide which one to use - (this is just something to consider).

Additional comments

Deterministic classnames on the components that can be targeted for custom styles

Regarding how the classes look and how developers may target them, I want to show a comparison of what we currently have and how the problem can be solved with the new approach.

As an example, I will take the Slider component. Here is currently how the generated DOM look like:

Each of the classes has a very well descriptive semantic and people can use these classes for overriding the styles of the component.

On the other hand, emotion, styled-components or any other similar library will create some hash as a class name. For us to solve this and offer the developers the same functionality for targeting classes, each of the components will add classes that can be targeted by the developers based on the props.

This would mean that apart from the classes generated by emotion, each component will still have the classes that we had previously, like MuiSlider-root & MuiSlider-colorPrimary, the only difference would be that this classes will now be used purely as selectors, rather than defining the styles for the components. This could be implemented like a hook - useSliderClasses

Conclusion

No matter which solution we would choose, we would use the styled API, which is supported by the two of them. This will allow us down the road to have easier support for styled + unstyled components (probably with webpack aliases, like for using preact).

After we investigated the two options we had in the end, the core team proposes we go with emotion. Some key elements:

A small migration cost between styled-components and emotion

Developers already using styled-components should be able to use emotion with almost no effort.

There are different ways for adding overrides other than wrapper components

The support of cx + css from emotion can be beneficial for developers to use it as an alternative for adding style overrides if they don't want to create wrapper components.

Concurrent mode is for sure supported :+1:

Kudos to @ryancogswell for doing a deeper investigation on this topic. So far we did not find anything in @emotion's code that would give us concern that concurrent mode wouldn't work. We were also looking into createGlobalStyle from styled-components as a comparison to emotion's Global component. It is doing most of its work during render (inherently problematic for Strict/Concurrent Mode) and just using useEffect for removing styles in its cleanup function. createGlobalStyle needs a complete rewrite before it will be usable in concurrent mode -- it isn't OK for it to add styles during render if that render is never committed. It looks like someone has tried rewriting it with some further changes in the last month, so we will need to follow this progress.

How is the specificity handled

Emotion's docs recommend doing composition of CSS into a single class rather than trying to leverage styles from multiple class names. In v5, our existing global class names would be applied without any styles attached to them. The composition of emotion-styled components automatically combines the styles into a single class. This potentially gets rid of these stylesheet order issues at least internal to the styles defined by Material-UI, because every component's styles are driven by a single class name :+1:. So we would have the global class names (for developers to target in various ways for customizations) and then a single generated (by emotion) class name per element that would consolidate all the CSS sources flowing into it. Specificity is then handled by emotion based on the order of composition. All compositions using emotion (whether render-time or definition-time composition) results in a single class on the element. styled-components does NOT work this way concerning render-time composition (definition-time composition does get combined into a single class). The same composition in styled-components results in multiple classes applied to the same element and the specificity does not work as I would have intended.

Alternatives


What do you think about it?

mbrookes commented 3 years ago

Just to a couple of corrections:

The React community, at large, has voted against using JSS at scale.

I would suggest instead that the React community didn't vote for JSS. Perhaps it wasn't "marketed" as well as other solutions?

We didn't bet on the right horse.

We bet on the only horse – it was a one-horse race. None of the other solutions available at the time ticked all the boxes.

MathiasKandelborg commented 3 years ago

Emotion sounds great! I love getting TS support, e.g., autocomplete and all the benefits of typing - with CSS-in-JS - when building UI or styling components, will that still be possible? How is this going to affect that, if at all? TS support is apparently better, amazing!

The last bit got me! I'd love helping by doing this behind a beta flag or develop on some features:

All compositions using emotion (whether render-time or definition-time composition) results in a single class on the element. Styled-components do NOT work this way concerning render-time composition (definition-time composition does get combined into a single class).

I also noted that the theme object is going to stay the same, the - in my opinion - the absolute best way to theme an application! I have nothing else to say :astonished:

Thanks for the great work on M-UI. I love the direction the project is going.

Moving to a more standardized way of styling is the way to go. I know the team and community will work out the kinks, and v5 - by the sounds of it - is going to be even more awesome! :rocket:

shilangyu commented 3 years ago

As someone who has used both styled-components and emotion I can confirm transitioning between them is easy and painless.

+ emotion is more typescript friendly

Andarist commented 3 years ago

Speaking as Emotion's maintainer - this sounds great πŸš€

We should also be able to release a new major soon-ish which won't be any revolution, just some cleanups, overall improvements, hooks under the hood and TS types improvements (better inference and performance).

Oh, and rewritten parser! that eliminates some edge bugs in Emotion and Styled-Components (as currently, they are both using the same parser). It is both smaller and faster.

sag1v commented 3 years ago

What about breaking changes, which option introduce more breaking changes (if any)?

re-thc commented 3 years ago

Not sure if it makes a difference but were the benchmarks done with emotion and/or style-component's babel plugins? They help to optimize things further.

ee0pdt commented 3 years ago

It was my understanding that MUI had previously indicated it was going with styled so this is a surprise. I think emotion is a great library, but with more people using styled currently it's important you find ways to give them good options if they don't want to migrate to emotion

MathiasKandelborg commented 3 years ago

@ee0pdt Emotion is very, very much like styled. I think that's part of the whole choice for Emotion over styled-components. There are clear benefits, and migration debt is little to none.

And there's a whole section about supporting both by giving a choice to developers. That could be a way to go, but then again, standardizing would probably help future us more. The full concurrency and no wrapper components is a deal-breaker for me. I get that others might want something styled provides, and that should be considered. I'd rather push toward standardization

re-thc commented 3 years ago

Why was styletron-react ruled out? It leaves out a whole metric that wasn't considered, which is memory consumption. The default styletron engine (and fela) is atomic. Whilst a bit slower it saves a lot of memory. Having seen a lot of react pages do nothing and go >1GB after a while it's a bit of a concern. The browser freezes after that.

With an atomic framework performance improves over time globally, across components as each atomic "class" is cached. Likely not reflected in the test either.

cr101 commented 3 years ago

Happy with either as long as it supports SSR

sakulstra commented 3 years ago

I just withdrew my vote from the original styled components issue :sweat_smile: - first learned to know emotion through storybook, but It will mean that all components styles need to be created using the styled API, which means for developers they will always have wrapper components if they need to re-style. got me to switch.

mnajdova commented 3 years ago

Thanks everyone for the quick feedback. Here are answers to some of the comments/questions.

What about breaking changes, which option introduce more breaking changes (if any)?

@sag1v using styled-components vs emotion does not introduce any more or less breaking changes that we will need to handle. The overall breaking changes would be around how the overrides inside the theme would look like:

// previosly
root: {
  contained: {
    '&$disabled': { // <-- this part will need to be transformed
      color: 'red',
    },
  },
  containedPrimary: {
    color: 'blue',
  },
}

// after
root: {
  contained: {
     '&.Mui-disabled': {
      color: 'red',
    },
  },
}

However as the styles syntax between emotion & styled-components is identical, it won't make any difference.

Not sure if it makes a difference but were the benchmarks done with emotion and/or style-component's babel plugins? They help to optimize things further.

@hc-codersatlas nope, but the perfs are those are anyway between the top few, so I don't believe it would make any difference. Good call tough!

Why was styletron-react ruled out? It leaves out a whole metric that wasn't considered, which is memory consumption. The default styletron engine (and fela) is atomic. Whilst a bit slower it saves a lot of memory. Having seen a lot of react pages do nothing and go >1GB after a while it's a bit of a concern. The browser freezes after that.

With an atomic framework performance improves over time globally, across components as each atomic "class" is cached. Likely not reflected in the test either.

My comments around why styletron-react was ruled out may be a bit misleading sorry about that, will update the PR description rightaway. The perf are good, but the biggest concern I have with styletron is the community: https://www.npmtrends.com/styletron-react-vs-@emotion/core-vs-styled-components While both emotion and styled-components are over 2000000 downloads in the last 6 months, styletron is around 15000.

Also atomic css may cause issues with overrides, as each classname contains only one styler rule.

I have got a question if we decide using emotion we want to add below code on top of all files? /* @jsx jsx / This is JSX pragma, by default JSX pragma is React.createElement

You need to add it if you are using the css property in emotion. For the styled API as well as the regular className API you don't need it.

Andarist commented 3 years ago

It is possible to skip adding JSX pragma, but it requires extra Babel setup and it comes with worse support from the tooling - for example, TS is not able to type-check your CSS prop as accurately as it does when using JSX pragma. More information can be found here: https://github.com/emotion-js/emotion/pull/1941/files#diff-9abe25e5d2b00958d4b9849f5f20c139R5

re-thc commented 3 years ago

@mnajdova thanks. I was just hoping memory usage is covered more than vouching for styletron in particular. As to downloads or community - "only" Uber uses styletron :) so no worries. Voted for emotion in the first place.

Would be cool if there was a babel plugin or similar that can transform static styles to real css classes. There's already a similar library called compiled. Most styles realistically are static.

layershifter commented 3 years ago

To be useful Fela will require a set of plugins like fela-plugin-rtl, fela-plugin-prefixer which will make performance even worse (https://github.com/microsoft/fluentui/pull/12289) 🐒 And then you will probably end up with Emotion (https://github.com/microsoft/fluentui/pull/13547) as sometimes it can be twice faster than Fela πŸ“¦

yordis commented 3 years ago

My only concern is having to use css thingy from Emotion. That is a big red-flag based on my experience. I had to remove such thing from a large codebase and wasn't fun. Why? Because it is an abstraction that brings more problems than the one that solves in the long term.

Most of the time, we try to use styled function, but we are quite happy with makeStyles function for generating some classes in some cases.

Hopefully, there is no breaking change for those two APIs, and the I am not forced to use css macro.

ryancogswell commented 3 years ago

My only concern is having to use css thingy from Emotion.

@yordis You definitely won't be forced to use the css prop. I suspect there will be some degree of change for usages of makeStyles and withStyles. Hopefully the amount of change necessary can be mostly limited to a codemod on the imports. I don't think the approach used in makeStyles or withStyles will be practical to support using emotion, so I would expect any ongoing support of those APIs to be through a separate package (so that "@material-ui/core" no longer has a JSS dependency) which would probably receive only minimal maintenance after v5 is released (the details around what happens to makeStyles and withStyles aren't nailed down yet, so this is just my speculation about the implications of moving forward with emotion).

seanconnollydev commented 3 years ago

πŸ‘ the choice to use Emotion. Avoiding the styled API of styled-components is one reason my team chose Emotion (which also supports a similar styled API in addition to the css prop). We presently use Emotion for Material UI style customization and it works pretty well. Built in support would just be icing on the cake.

petermikitsh commented 3 years ago

Regarding these facts:

Many developers use styled-components to override Material-UI's styles. End-users find themselves with two CSS-in-JS libraries in their bundle

Support concurrent mode styled-components: Partial

If styled-components had full support for concurrent mode, would it be a more sensible choice, given most developers are overriding MUI with styled-components (excluding JSS)? The point about emotion being smaller in bundle size is moot if 2 css-in-js solutions need to be included. And I would presume most practical applications of MUI involve overriding its styles.

sko-kr commented 3 years ago

Where I and my team use Emotion as a main way of styling components, I came across some inefficiencies present in emotion library. And I wonder what you guys think about these inefficiencies explained below.

Consider below Emotion StyledComponent;

const StyledComponent = styled.div`
  ${({color}) => color && `color: ${color}`};
  display: flex;
  justify-content: center;
  align-items: center;
  background: teal;
  font-size: 20px;
  padding-top: 8px;
  padding-bottom: 8px;
  margin-top: 12px;
  margin-bottom: 12px;
  border: 1px solid grey;
`

When color prop changes new css class is generated with all css props (display: flex, justify-content, ..., border: 1px soild grey) copied over. Which would result in css classes with the exact same css props for each color prop, see below; image

Another inefficiency we have found is when a new component is derived from above StyledComponent it will create a new class with all the css props copied over from base StyledComponent. Consider below;

const DerivedComponent = styled(StyledComponent)`
  font-family: monospace;
`

It creates another css class that only adds font-family: monospace to the above css class generated from StyledComponent. That is, It creates a css that has all the props copied over from StyledComponent as can be seen below;

image

Now if above StyledComponent and DerivedComponent are used together we (initially) have two css classes which have duplicate css props, (differing only in font-family). As can be seen below;

image

As you can imagine a number of css classes that have duplicate css props of each other can grow quite quickly.

I found that with Emotion, as component styles are composed together, we end up with css classes with many duplicate css props.

I am not sure this duplicate of css props in each css class has any noticeable impact on apps, but I wonder if this is taken into account on choosing to use emotion.

I am not questioning the performance of Emotion in creating and applying CSSStyle at run time. Emotion is one of the fastest on applying CSSstyles as evident in perf tests. I am just concerned resulting CSSstyle is bloated. And as Emotion can be SSR(ed) which means styles are inlined in HTML, we just might get unnecessarily bloated (with css style tags) HTML file. Which in turn results in lot more tags to parse with unnecessary css props by browsers.

mnajdova commented 3 years ago

If styled-components had full support for concurrent mode, would it be a more sensible choice, given most developers are overriding MUI with styled-components (excluding JSS)? The point about emotion being smaller in bundle size is moot if 2 css-in-js solutions need to be included. And I would presume most practical applications of MUI involve overriding its styles.

@petermikitsh the reasons why we concluded on emotion are actually the four points in the conclusion

Having the first point in mind, if developers really want to avoid having two css-in-js solutions in the bundle, the migration cost is really small for moving to emotion + it supports different API other than the styled.

mnajdova commented 3 years ago

@ko-toss thanks for writing this up these are all good points. The fact that emotion generates one className with all styles, makes it the better engine for resolving overrides. The problem we may have with generated multiple classNames is that the last class written will win, which may become problematic in the future.

On another project, I was using atomic css, where from memory consumption is much better because all css rules are written only once, but doing predictable overrides there is pretty hard, as each className is one atomic rule, and than again which one wins, depends on the order of which they are written, if you do not decide to previously process all styles and merge them correctly, which may impact perf in the end.

On the other hand, I believe that using any css-in-js solution, people will not just randomly create infinite combination of styles, they are still pretty structured based on the props value.

However, again these are good points, that we will have in mind, thanks a lot for sharing them πŸ‘

smmoosavi commented 3 years ago
  • else?

what about stylex idea compatibility (like style9)

shilangyu commented 3 years ago
  • else?

style9 or any other CSS engine that is compatible with stylex idea

This seems to require a build time setup which would discourage many people from using it, especially beginners.

cztomsik commented 3 years ago

(this is rather FYI, emotion is a good choice)

https://github.com/cristianbote/goober (1kB, perf little worse than emotion)

I have no experience with that yet but I want to try it one day. image image

oliviertassinari commented 3 years ago

@cztomsik Similar to https://github.com/kuldeepkeshwar/filbert-js but has no JavaScript syntax support (only CSS template string)

jantimon commented 3 years ago

Here are some tests done with google lighthouse on time-to-interaction time:

https://jantimon.github.io/css-framework-performance/

css-lighthouse-scores
simnalamburt commented 3 years ago

FYI, I've done some detailed benchmark of styled-components v5, emotion v10, and emotion v11, with/without babel plugin, with vanilla API, css props API, and styled API. Hope this help the discussion!

https://github.com/simnalamburt/css-in-js-benchmark

jgoux commented 3 years ago

Did you consider the new wave of css-in-js libraries that rely heavily on atomic css and typescript support?

simnalamburt commented 3 years ago

Did you consider the new wave of css-in-js libraries that rely heavily on atomic css and typescript support?

It's not on the benchmark, but currently otion is 2~4 times slower than emotion. I think otion indeed have pretty big potential and believe there is a room for the optimization, but otion is not really ready for the production yet.

I didn't test the stitches yet, though. πŸ˜ƒ

TheHolyWaffle commented 3 years ago

What about an actual zero-runtime library? I haven't seen anyone mention linaria.

mnajdova commented 3 years ago

I stumbled across linaria at some point and I really like it. My only worry with is that the dynamics props styles solely depend on css variables, and there isn't any support for IE 11 based on https://github.com/callstack/linaria/issues/445 Also compared to styled-components and emotion the community is much smaller at this moment.

sko-kr commented 3 years ago

@TheHolyWaffle Linaria is awesome. Iff you set it up properly, I believe it is the best of both css-in-js (in terms of dev experience), and pure css (can't beat pure css performance). It even optimizes(dedupes) and reuses css rules. But linaria requires build and bundling step which would be hard for beginners.

kuldeepkeshwar commented 3 years ago

I would love to see ports for other css-in-js libraries with similar API surface e.g filbert-js /goober

oliviertassinari commented 3 years ago

@kuldeepkeshwar We will let you know once we look into the adapters for the styled API :)

MathiasKandelborg commented 3 years ago

How does https://compiledcssinjs.com/ fit into all of this? It seems to be an incredibly interesting approach; Compiled also runs RFCs for the project, which proves to be great for open source and collaboration. wink wink

I think the future is very, very bright for styling the web, and I hope Material-UI will be an integral part of the go-to solution for styling any app.

The explanation of how Compiled works got to me:

This kind of transformation allows us to deliver your component to any consumer without needing them to configure/setup their tooling. Just import and go. This is powerful, and more importantly, the same as current CSS in JS libraries work - with one catch.

CSS can't be generated at runtime.

This single constraint opens a lot of doors for us. Build time optimizations. Runtime guarantees. Performance bottlenecks gone.

On a different side note, I'd like to point out that popularity doesn't mean much for a good project. I love MUI and the work that has gone into it thus far; I also think it is fantastic it has become a premium product. But choosing a 'popular' name for the sake of popularity isn't a reasonable argument. I've seen popularity referenced multiple times, and I very much dislike even considering if x amount of people use x technology - MUI is (in my books) focused on performance, DX, and other things, just not popularity.

MUI didn't always have 60k stars, it got them because it chose the best tech (or close to), not because it chose popular tech.

If choosing based on popular vote is in regards to being a more widely approachable project, that's business concerns, not possible performance enhancements. A project lives with or without users. It dies with bad choices. I think there's so many sayings around this and reading "it isn't popular enough, therefore it is a bad choice" rings a lot of loud bells.

People use a right product because it is good, not because it uses popular technology; MUI was niche once but became famous even though it had CSS-in-JS, which didn't win the popular vote btw. It just has some amazing properties and made the right choices that weren't based in current community but the actual DX and performance.

That sidenote being noted, I am on the side of the popular vote myself; so if anything, I am also sabotaging myself. I don't have any personal gain to have from choosing a way less popular product, I have the opinion that popularity should not be considered at all when talking about revolutions and changes. Please reconsider some of these options based on what they actually are, not what people think they are based on the current popularity of the option.

To actually finish off, I am grateful for every thought and any amount of time going into MUI. I have made some amazing (sadly private) solutions following all standards etc. the past couple years, which would have taken months or years to make singlehandedly! I can't describe my appreciation nearly enough for it to shine through on paper πŸ™‡β€β™‚οΈ πŸ™ πŸ™‡β€β™‚οΈ

I'm curious if Compiled even is an option and how it would work with adapters and such. I think the Compiled approach:

Compiled compiles your CSS in JS at build time by statically analyzing your code and then transforming it into Compiled Components. Everything we need to use the component is included along side it in the JavaScript bundle.

is a path to think about, given the whole compiling 'css at runtime' constraint.

Andarist commented 3 years ago

I'm saying this as Emotion maintainer - Compiled is great. Or rather - it might be in the future, this is very exciting stuff but it's still quite early in the experimentation. I highly doubt that MUI can go with it at this point in time.

shilangyu commented 3 years ago

Correct me if I'm wrong but compiled implies having a config which means it would be obligatory to have a config file for MUI even without using any custom styles.

I would hate to be forced to create a config just to use MUI. On a side note: wouldn't that be hard to use in opinionated bootstrappers like Create React App?

MathiasKandelborg commented 3 years ago

@Andarist I agree entirely. I would suggest starting a collaboration or at least considering participating in the development of the library. I'm inquisitive about where it could lead in the future! :eyes: I think something like compiled - as you're saying - in the future is going to be great. It would be awesome to have more great minds get together to make something remarkable.

@shilangyu I am not sure what you are implying, since I might be missing something. So I'll just say that the frontpage of compiled has this to say about it:

Migrate to a zero config reality

The APIs we love are all here for the ride - CSS prop and class names component too! Our consumers don't even need to change how they consume our components, continuing the zero config story they don't need to configure their bundler, nor do they need to setup any specific things for server side rendering. It just works.

Just the beginning

With zero config out-of-the-box today, we're not forgetting what tomorrow could look like. With the possibility for optional CSS extraction, transforming the CSS to an Atomic form, and even being able to use the CSS data for analysis across our code base, we're thinking up an exciting tomorrow.

sko-kr commented 3 years ago

@MathiasKandelborg I skimmed through https://compiledcssinjs.com/ . Isn't it still runtime css-in-js ? It creates css classes at build time but it applies that style(creates style tag with build time generated css classes) with <CC>...</CC> tag at runtime. If it is as fast as just using pure css then it really is the future(as it uses css variables). Thanks for sharing I wonder how faster it is compared to Emotion.

eps1lon commented 3 years ago

What about an actual zero-runtime library? I haven't seen anyone mention linaria.

What we didn't include in the requirements is that any solution has to be zero-config from the perspective of Material-UI consumers. If I understood zero-runtime solutions then they generate some CSS at compile time. Don't I have to setup my bundler to include it properly?

So while zero-runtime is probably the fastest solution it also requires extra attention. Having a zero-config solution that can be configured to run with zero-runtime would be ideal I guess.

Andarist commented 3 years ago

So while zero-runtime is probably the fastest solution it also requires extra attention. Having a zero-config solution that can be configured to run with zero-runtime would be ideal I guess.

Cant really say about current state of Compiled but i was talking about it several times with the maintainer and this roughly is the plan - the idea is to keep support for Emotion & Styled Components APIs, so optimizing code written those should just be a matter of changing the imports and including a transform plugin or a webpack loader. It, for sure, wont handle all code that can be possibly written (JS is wild), but it shohld be able to handle sensibly written code. If it wont be able to compile something the. It will simply throw - forcing one to ditch using it or rewriting a particular part of the code to aid static analysis.

So to sum up - if u go with 0config Emotion (or Styled Components) then it should be possible to adapt Compiled as an optional optimization in the future (if the project will manage to deliver what it promises)

MathiasKandelborg commented 3 years ago

@ko-toss I think it compiles into the styled component at build time. At runtime, the styles from the component are then moved to their rightful place.

As they say on the webpage:

Everything we need to use the component is included along side it in the JavaScript bundle.

We take your initial code in all its glory:

import { styled } from '@compiled/css-in-js';

export const ColoredText = styled.span`
  color: #ff5630;
`;

And then transform it into a Compiled Component:

...
...

Which then at runtime will move the styles to the head of the document.

This kind of transformation allows us to deliver your component to any consumer, without needing them to configure/setup their tooling. Just import and go. This is powerful, and more importantly exactly the same as current CSS in JS libraries work - with one catch.

CSS can't be generated at runtime.


Having a zero-config solution that can be configured to run with zero-runtime would be ideal I guess.

I think you hit the nail on the head. It would feel utopian to suddenly just do that dreaming fondly.

There are some ideas to explore and maybe some collaboration to be had. Some of the code and concepts are a bit foreign to me, so I'm not in the position to go in many details. Here are some of the things I'm excited about:

In regards to IE11 support, how are you looking at stats? I'm sure it's a perfectly viable thing to do. Edge is now based on chromium, and most businesses should be making the switch when MS finally stops support for IE when each OS IE11 was installed on reaches the end of their support cycle. It is indeed a long cycle, but I think maintainers also have a part in pushing changes, and keeping support for something that is essentially deprecated seems to enable people to wait until the 'shift' actually happens.

It would be nice to have the option not to support IE11. It isn't industry standard anymore, and will be deprecated. It is a matter of time, and default support from amazing things like MUI probably holds back the shift.

eps1lon commented 3 years ago

It would be nice to have the option not to support IE11.

See https://github.com/mui-org/material-ui/issues/14420 for that.

We don't plan to drop support for IE completely. The default version likely won't target IE 11 in v5 but we can't choose a solution that won't work at all in IE 11. Or rather it should be a solution that we can swap out at build time and produces the same output.

armand1m commented 3 years ago

This makes me happy.

kelly-tock commented 3 years ago

is there a code mod for converting existing jss to styled/emotion I wonder?

satazor commented 3 years ago

Hello everyone. I'm taking the opportunity to shim into this discussion.

In the current version, Material UI makes heavy use of withStyles HOC without any dynamic styles (style being a function that depend on props), which internally uses makeStyles. The performance of makeStyles (without dynamic props) is quite remarkable and Material UI could even be faster if it was using it directly, instead of withStyles, which creates an unnecessary wrapper.

I have created a benchmark forked of this benchmark, and I deployed it to Vercel, so that all code is compiled with production flags on. The benchmark renders cards using different CSS in JSS libraries. Here are the links:

For 100 cards:

For 500 cards:

For 2500 cards:

Overall, emotion and styled-components are very similar in performance. However, makeStyles seems to be 2-4x faster overall on mount and rerender 6-8x faster for updates.

The difference is significant enough, especially when we are rendering on low-power devices, such as our phones.. and there are a lot of crappy phones out there.

All of this to say that I'm worried about material migrating and using emotion by default, which will decrease the rendering performance of Material UI and sites using it by a 3-5x factor. (this is not actually true, it depends on the component; the more complex it is, the less difference there will be).

Some questions and food for thought:

MathiasKandelborg commented 3 years ago

I can live with a small performance loss.

Not a 300% performance loss, not at any cost. πŸ˜…

mnajdova commented 3 years ago

@satazor thanks for exploring this. We did a heavy perf testing before starting this effort, see PR https://github.com/mui-org/material-ui/pull/22173 for more details (we did it on the ListItem component) and the perf difference was at most 10% for rendering x1000 instances, in production mode.

https://deploy-preview-22173--material-ui.netlify.app/performance/list-raw/

https://deploy-preview-22173--material-ui.netlify.app/performance/list-mui/

https://deploy-preview-22173--material-ui.netlify.app/performance/list-styled/

https://deploy-preview-22173--material-ui.netlify.app/performance/list-styled-wrapper/

Based on this, we decided to ignore this difference, because of the benefits we would get (dynamic props out of the box, styled API that was already used by a big percent of the developers already etc - the whole summary can be find in the PR description :))

Not sure what is happening on your benchmarks, but 3-5x seems too much to me, it makes me wonder why would anybody use emotion/styled-compoenents if this was the case.. We can try to see where is the difference between the two benchmarks, in case we are missing something. Also, doing benchmarks on a real MUI component would be much better in my opinion, so we would get more realistic numbers, so let me know if you want to explore more on this side. The PR I linked is a good starting point.

satazor commented 3 years ago

Thanks for the reply @mnajdova. You are right that testing on a Mui component would be more realistic. What's probably happening is that the Mui code for the List components is the predominant slowness factor, and the difference between them (~30ms) is the actual rendering time difference associated with styles. I will take the code of that PR and add it to the benchmark, to see the results.

Will this matter in the end? Probably not, but it depends on the app. The performance difference between current Mui components and styled ones will increase as the rendering code itself is simpler. As an example, I'm expecting to see increased differences on the Icon or Typography components, but decreased differences for Cards. So, it really depends on the app and the amount of components of each type the app is using.