paypal / glamorous

DEPRECATED: 💄 Maintainable CSS with React

Home Page:https://glamorous.rocks

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Deprecate glamorous 💄 in favor of emotion 👩‍🎤

kentcdodds opened this issue · comments

For a long time now I've been considering deprecating glamorous (NOT glamorous-native) in favor of emotion.

Here's my reasoning:

  1. Emotion can do everything glamorous can do
  2. Emotion can do more than glamorous can do
  3. Emotion is faster than glamorous
  4. Emotion is smaller than glamorous
  5. It's good for the community to consolidate and rally around using and improving a single solution

I would not recommend people spend a bunch of time migrating to emotion. glamorous isn't bad, it's just not as good as emotion and I don't think it would be worth the effort to manually migrate. That said, I'm fairly confident that a codemod could be written to automatically migrate your glamorous code to emotion.

What a deprecation would mean:

  • I would add a notice to the README and website indicating that this project is no longer maintained and encouraging people to use emotion instead.

What a deprecation would NOT mean:

  • glamorous-native is still the best-in-class for styling native applications and is entirely separate from glamorous. It would NOT be deprecated. It is an entirely separate project with no shared dependencies anyway, so a deprecation of glamorous wouldn't impact glamorous-native directly.

What I want from you:

Please give this a 👍 if you agree or a 👎 if you disagree.

If you disagree, please list reasons why and whether you would be willing to assume the maintainer role.

Thanks friends!

Things are looking very positive for this. If someone would like to help a LOT of people here are a few things you could do:

  1. Write a migration guide. It could be a simple markdown file here.
  2. Write a codemod. If you don't have any experience with ASTs then this might be a bit of a challenge for you. I have some resources for you though:

A good starting point for the codemod could be the one I wrote for upgrading people's version of glamorous due to the theme breaking change we had a few months ago: https://github.com/paypal/glamorous/blob/master/other/codemods/theme-move.js

I don't think that I'll have time to do either of these things any time soon, but I'd love it if anyone would like to pitch in to do it :)

I'm 99% certain that we'll be doing this.

commented

I can think of one concern, this move can confuse people (like some already asked) and raise this question that what would happen to glamorous-native again and again. Adding a notice mentioning "This is actively maintained" or something like that in that project's readme would be helpful :)

nice! I recently migrated our entire codebase from glamorous to emotion. I'll see if I have some cycles to write up some of the things I did to migrate and any gotchas I ran into

One thing that can (probably) confuse people is TS support. Emotion's TS support is quite immature compared to Glamorous. It does almost nothing with CSS typing, does not have proper plugin type, ...

@Ailrun, that is a very valid concern and I'm sure it would be frustrating for folks like you and others who have poured a TON of effort into making glamorous as great as it is with regards to TypeScript. Hopefully by switching to emotion we can combine efforts with other folks to take the things we've learned from glamorous to improve the Typings in emotion.

@kentcdodds Hm... then, I should spend some time on emotion typing to move there safely. Thank you for kind comment.

Does anyone have a react+emotion demo repo/codepen or something I could test a few things with?

My primary use case with Glamorous has been css-object composition+ThemeProvider--nested arrays of objects or functions-returning-objects. I'd like to validate that emotion can work with some of those patterns.

Soon enough I'll test it out, but if there's already a codesandbox or something I could test things very quickly.

@kentcdodds You're making a very awesome decision to rally around a single solution. Love the direction here.

@kentcdodds Emotion does not seem to have any notion of prop filtering, and the associated API's rootEl, filterProps, forwardProps, like Glamorous does. Was this a difficult thing to maintain in Glamorous? Any thoughts on how to approach if migrating to Emotion? The library that I work on relies on it pretty heavily at present.


Update: Apparently Emotion supports a shouldForwardProp option and also has a @emotion/is-prop-valid package. Investigating further...


Update: Emotion has similar capabilities, but as usual, the devil is in the details. It does not appear that this will be a simple migration. Need time to investigate further, but unfortunately don't have it at present. Issues documented in mineral-ui/mineral-ui#692

Interesting. Could you file an issue on emotion @brentertz? Let's see if this is something they'd be willing to implement. Either that or we could implement a small layer on top of emotion that could do this.

@quicksnap

My primary use case with Glamorous has been css-object composition+ThemeProvider--nested arrays of objects or functions-returning-objects. I'd like to validate that emotion can work with some of those patterns.

Emotion should handle all that jazz just fine. We use it on production and receiving serializable theme objects through the wire + ofc we use some local functions etc to style based on props.

I'm curious about the implication for this kind of use:

  <glamorous.Div
    backgroundColor={theme.color('white')}
    paddingTop={theme.size(0.1)}
    paddingLeft={theme.size(0.2)}
    paddingRight={theme.size(0.2)}
    display="flex"
    jusitfyContent="center"
    alignItems="center"
  >
    This gets nicely styled using props
  </glamorous.Div>

In emotion this can be achieved by using custom helpers or libraries like styled-system

From #421

Object Syntax If moving to emotion you will need their babel-plugin-emotion

Is that true? In that case moving from glamorous to emotion is a no go for me and users of create-react-app who like to still use the object notation.

@bjoernricks that's not true, emotion handles object syntax without babel's help

@bjoernricks the babel plugin is only needed if you want a similar API to glamorous.

Without babel plugin:

import styled from 'react-emotion'

const Foo = styled('div')({
  position: 'relative'
})

With babel plugin:

import styled from 'react-emotion'

const Foo = styled.div({
  position: 'relative'
})

So whilst the plugin isn't required, it certainly makes the migration easier.


Update:

FYI, I also use create-react-app, and would encourage you to look at react-app-rewired. Sometimes it's really helpful to be able to add a plugin to CRA without ejecting.

Someone has built a plugin for emotion too 👍

Also there is a plan to support styled.div style without a babel plugin in upcoming emotion@10, but it's still recommended to use babel plugin nevertheless to downsize production bundles.

fyi: v2 of create-react-app supports babel-macros. This means that you can use emotion/macro without having to eject.
Macros seem to support all features that babel-plugin-emotion support except for the css-prop.

Two things that emotion doesn't do are exactly the things that I used glamorous for:

  • being able to inline styles like this (lets you not give anything a name if you don't reuse it):
<Div width={100} height={100}>
 <H1 backgroundColor="green">Hello</H1>
</Div>
  • if you used TypeScript, these props would be type-checked at compile time.

So, not really feeling 'emotion is better than glamorous'.
I will probably continue to use it.

@dragosbulugean I started migrating and found

<div
  css={{
    width: 100,
    height: 100,
  }}
>
  <h1
    css={{
      backgroundColor: 'green',
    }}
  >
    Hello
  </h1>
</div>

to be a suitable alternative. I even like it a bit more.

@VinSpee thanks, but where do you get the div and h1 from? not sure you are allowed to name them exactly like the included dom react components.

In addition, the css prop is actually more powerful because you can use media queries and such, so I haven't used the named css props for a long time. On top of that, when you use the built-in components with glamorous, you're putting that code on the slow path. It's fast enough to not be a problem most of the time, but it's definitely the slowest way to use glamorous. That's not the case with emotion. They've done some amazing work to make it super fast.

Can we use emotion with any type of css lint? In glamorous, as far as I know, we can use stylelint. But I cannot find any related tools for css linting working with emotion.

I don't think there'd be anything to preclude us from adding that functionality if it doesn't already exist. They're very similar libraries. Whatever tooling you're using for glamorous could probably be retrofitted to work with emotion fairly easily.

@VinSpee yes but create-react-app is not there yet :(. Plus I just checked that the css object doesn't get type-checked.
@kentcdodds I understand :(
Pretty unhappy, but this is the OSS world.

As I said @dragosbulugean:

If you disagree, please list reasons why and whether you would be willing to assume the maintainer role.

I love glamorous and I have already started migrating some stuff to it on our current project some months ago, but looks like now I will have to migrate to something different.

My only concerned and question is: Why emotion instead of styled-components?

I'm just bit concerned that emotion might have the same fate as glamorous did.

@JeffersonFilho
First, styled-component does not support object style syntax.
Second, styled-component is even slower than glamorous, as far as I know.

@Ailrun
Oh I see! Thanks for the info!

Actually, as of very recently, styled-components now actually supports objects, it's also made leaps and bounds with regard to performance, though it's still not as fast as emotion. I'm pretty certain that emotion is still more capable though, so I still recommend emotion over styled-components personally. I don't think that you'll go wrong either way though.

Again, you can't really go wrong by not migrating at all. It's not like glamorous suddenly got an infusion of bugs or performance problems. You can keep using and loving it for all the same reasons you use and love it today. I just wouldn't recommend starting a new project with it.

Eventually we can hopefully create a codemod to migrate your code to emotion automatically. The folks who maintain storybook were big users of glamorous and have manually migrated to emotion saying that the effort was "minimal." So you might try to take an hour of your time to manually migrate and see if you can do the same. I probably wouldn't spend more than an hour on it though.

@kentcdodds Oh, thank you for correcting me! I should check and send some PR to benchmark repos!

And side note for TS users,
I added typing for create-emotion, and sent a PR to fix typing for emotion.
See emotion-js/emotion#663 and emotion-js/emotion#667.
I'm currently working on typing create-emotion-styled, and after then, I will typing react-emotion and preact-emotion.

Type migration(?) will be ended soon!

Thank you @Ailrun! I think this is wonderful that we can combine forces and make our chosen solution even better!

With the overwhelming 👍 over 👎, I've added a message to the top of the README. I'll try to get a message on the website as well.

Glamorous has been and still is an amazing library! I love the maturity around this decision. Thanks for everything @kentcdodds and everyone else behind this library!

Glamorous has been and still is an amazing library! I love the maturity around this decision.

You might even say that this decision shows emotional maturity. ☜(゚ヮ゚☜)

I'll show myself out.

commented

Just converted a project with zero issues in 5 minutes, was surprised how easy it was. I'm also using the 'prop-styles' package and had no issues. Here's what i did for anyone else using Atom:

Open up project search and enable regex

  1. cmd + shift + F
  2. Enable regex search .*

Replace glamorous imports:

  1. Find in project: import glamorous from 'glamorous'
  2. Replace in project: import styled from 'react-emotion'

For any glamorous.element use:

  1. Find in project: glamorous\.(.*)\(
  2. Replace in project: styled('$1')(

For any glamorous(MyComponent) use:

  1. Find in project: glamorous\(
  2. Replace in project: styled\(

Might not be perfect for your needs, but hope it's able to point some people in the right direction.

Has anybody started a list of what's in glamorous that has no equivalent in emotion yet? It would be helpful for planning a migration (we have >100 components that were styled with glamorous). I can't see withProps there, for instance.

@Ailrun Ah, of course - I forgot about that. We previously tried to use that with glamorous but it didn't play nicely, so had to switch to glamorous' own withProps. Thanks!

Using defaultProps should accommodate for most use cases too.

defaultProps is sufficient, is you prefer, recompose's withProps also works

But I cannot find any related tools for css linting working with emotion.

Try this:
https://github.com/gucong3000/postcss-jsx

npm i stylelint postcss-jsx --save-dev
stylelint **/*.{js,css}

@gucong3000 Actually I'm currently working on stylelint plugin for emotion...

@Ailrun This is exactly what you are looking for.
postcss-jsx is what I developed specifically for stylelint & postcss.

Omg, this is such bad news. I much rather write CSS inside JS using the JS syntax

textAlign

not

text-align

I also much prefer not using these ```` around my code... I don't care if emotion is "faster", glamorous is perfectly fast enough for us!

Please don't kill this project! Is anyone volunteering to maintain glamorous?

@antoniobrandao you must not have looked closely. Emotion fully supports js objects as you described. It's the syntax I use and prefer too, so that was important to me!

I much rather write CSS inside JS using the JS syntax

To echo @VinSpee: https://emotion.sh/docs/object-styles

Thanks @VinSpee, didn't know and it's great to know! 👍

But it gives me more work to achieve the same result...

If I install just the glamorous NPM package I can write like this:

import glamorous from 'glamorous'
const H2 = glamorous.h2({
    fontSize: '44px'
})

To use it like

<H2>Some text<H2>

To be able to do this in emotion I have to use react-emotion, which in turn requires that I install babel-plugin-emotion.

Then I can do this:

import styled from 'react-emotion'
const H2 = styled.h2({
    fontSize: '44px'
})
// and use like
<H2>Some text<H2>

Yarn didn't install emotion automatically when I installed react-emotion by the way. Had to install it separately.

So in total I need to install:

emotion
react-emotion
babel-plugin-emotion
And I have to add emotion to the .babelrc file

Isn't it a lot to install / config for something I can achieve with a single glamorous NPM module installation?

@antoniobrandao The babel plugin is optional, as it appears you are aware. It enables the styled.div syntax so you don't have to use styled('div'). Using the latter is a simple find and replace across your project.

Having to install emotion and react-emotion is no different from having to install glamor and glamorous. react-emotion even exports everything that emotion exports so you have direct access to those exports without having to fuss with packages.

thanks for the reply @knpwrs, but that is my point, I want to write like this:

glamorousOrEmotion.h1({

Not like this:

glamorousOrEmotion('h1')({

the default glamorous way requires a . instead of 4 chars: ('')

react-emotion even exports everything that emotion exports so you have direct access to those exports without having to fuss with packages

I read about that, but surprisingly after installing react-emotion, I did have to install emotion separately, and to install and setup the babel plugin. I tried it first just after installing react-emotion and I got an error, saying I had to install emotion as well.

On the other hand, glamorous does install glamor automatically, which is nice. At the moment it provides me the shortest path to be able to use the simpler JS syntax.

@antoniobrandao react-emotion declares emotion as a peer dependency and glamorous declares glamor as a peer dependency. glamorous does not automatically install glamor. I just installed glamorous in an empty project and got the following warning:

npm WARN glamorous@4.13.1 requires a peer of glamor@>=2 but none is installed. You must install peer dependencies yourself. 

And the following node_modules tree (no node_modules under glamorous` either):

node_modules
├── brcast
├── csstype
├── fast-memoize
├── glamorous
├── html-element-attributes
├── html-tag-names
├── is-function
├── is-plain-object
├── isobject
├── react-html-attributes
└── svg-tag-names

11 directories, 0 files

I can't help the character difference. It leads to a smaller package size which is why glamorous ships a "tiny" version which is more similar to emotion: https://glamorous.rocks/advanced/#optimizing-bundle-size

@knpwrs sorry I must have remembered incorrectly. I just realised, I don't even use glamor. Removed glamor from my (large) project and with glamorous alone it still compiles and works, using the simpler dot syntax. Package size optimisation is nice, but not always that important, and it's also nice to have a less verbose syntax (for some people at least). It's really a matter of syntax flavour and simplicity. I really like the syntax that glamorous offers out of-the-box, even without glamor. Just wanted to put it out there - Not to argue the design choices made with emotion which are perfectly sensible. I already knew emotion and it looked great to me, just didn't migrate because of the different default syntax. It's nice to know emotion can do the same even if with a little more setup, so thanks for your clarifications.

It would be fairly trivial to publish and maintain another package that depends on emotion and react-emotion and exposes the type of API you're looking for. But that's not important to me specifically so I won't personally be working on it.

Keep also in mind that having all of those .div, .h1 etc costs u bytes shipped over the wire. It's actually preferable to use styled('div') etc - but that can ofc be compiled with babel plugin which is super nice, because you have great DX & less bytes shipped to production.

@kentcdodds One of the hard things to port is inserting class names.
In glamorous, we can use something like

const StyledButton = glamorous.button(
  'awesome-library-class',
  (props) => `${props.country}-style`,
  {
    //... other styles
  },
);

However, in emotion, we cannot use something like

const StyledButton = styled('button')(
  'awesome-library-class',
  (props) => `${props.country}-style`,
  {
    //... other styles
  },
);

but we should use something like

const StyledButton = styled('button', {
  target: [
    'awesome-library-class',
  ],
})({
  //... other styles
});

and there's no way to port (props) => `${props.country}-style` .
Or there is a way and I just don't know it?

Huh, that's odd. I thought that was possible. @tkh44, could you speak to this? Any reason that providing a string isn't possible as described above? Would a PR be accepted to support this? It's a pretty handy feature.

@Ailrun could u illustrate both with runnable example? Is awesome-library-class a problem here?

  1. https://github.com/emotion-js/emotion/blob/21c3308ee6764a04a267ff07e5890a6626603c15/packages/create-emotion-styled/src/index.js#L65
    • When users call styled(...)(/* second call parameters */), /* second call parameters */ are just pushed to styles
  2. https://github.com/emotion-js/emotion/blob/21c3308ee6764a04a267ff07e5890a6626603c15/packages/create-emotion-styled/src/index.js#L119-L122
    • styles passed to emotion.css
  3. https://github.com/emotion-js/emotion/blob/21c3308ee6764a04a267ff07e5890a6626603c15/packages/create-emotion/src/index.js#L285
    • and emotion.css creates a single selector (or rather, css class), not css classes, so it's of course impossible to add other css classes.
      (Of course emotion have target option, but it support only static classes.)

@Andarist Here it is. 'awesome-library-class' is just a random static class.
https://codesandbox.io/s/9q191nkxpp

Sorry, this one is the correct one.
https://codesandbox.io/s/yq1w2vkj1

Class names like 'blue', 'text-white', 'text-red' are defined in styles.css

Got it, aint sure what it would take to support it because emotion supports both tagged template literals & objects syntax this might be somewhat ambiguous - at least when talking about glamorous-like API.

Hi y'all! I have thoroughly enjoyed glamorous for the past almost-year or so. I'm sad to see it deprecated in favor of emotion.

Still, I've got a large codebase that depends on it that we've built for months that I needed to migrate over, and so I've written a codemod here for everyone who may have a similar situation.

Thanks again for your great work on this library, @kentcdodds! ❤️

This is fantastic! Thank you for your help! Would you be willing to accept contributions to improve it? For example, I think it'd be great to have an option to assume the babel-plugin-emotion will be used so that styled.div and the css prop work. What do you think?

For sure. All contributions welcome!

I can't see the capitalized html components available in glamorous in emotion's docs eg: <Div paddingTop="10px"/>. It's easy enough to fix but it means a simple find replace won't cut it if you're using these extensively.

My codemod will replace glamorous.Divs to <div>s. Can I update it to help your use case, @melbourne2991?

Upgraded a pretty huge side project of mine (approx a year worth of dev on it) within an hour. Pretty decent! Huge thanks to @TejasQ for the codemod. ⭐️


Side note: emotion is really fast! Noticed a distinct improvement in perf already. Great decision to move towards it. 👍

@ctrlplusb awesome to hear it! Glad I could help! I hope your project flourishes!

I just migrated quite a big project using a lot of glamorous features to emotion. I'm almost done, and it took me about a day of work.

Notable things the codemod does not support (yet):

  • wont work with glamorous.macro (should be easy to fix)
  • won't work if you do imports like import g from "glamorous" the codemod expects import glamorous from "glamorous"
  • you manually need to convert code like <glamorous.div marginTop={5}/> to <div css={{marginTop: 5}}/>
  • no automatic translation from filterProps/forwardProps to shouldForwardProps (the latter expects a function), most other options (e.g. withProps) are also not directly translatable
  • forwardProps: ["innerRef"] does not work with emotion
  • global styles and keyframes can't be automatically translated yet either
  • won't work with create-react-app out of the box, but react-app-rewire-emotion makes it fairly straight forward without the need to eject.

Wow! Thanks so much @danielberndt! Would you be interested to implement these features in the codemod together?

@TejasQ this would be quite a nice opportunity for me to dig into AST-transformations which I've never done before. :)
But I can't promise anything since my schedule is quite full at the moment.

@danielberndt No worries whatsoever. The easiest thing would probably be working with generic default import specifiers (number 2 on your list). Shall I reserve that for you?

I could even just submit PRs that you review and could use for learning.

What would you prefer?

Ah, how nice! Yes, if you have the time, feel free to go ahead and implement it yourself. I'd be very happy to learn from your changes 👍

I don't really have the time, but I'll make the time for you. <3

@danielberndt does the standard ref work ?

@antoniobrandao both innerRef and ref work the same way in glamorous and emotion. I was referring to a very niche feature that was introduced here:

If you want to prevent glamorous from appling the innerRef mechanism and instead just forward that prop to the underlying Component (e.g. use react-routers own <Link innerRef={handler}/> you could just do

const StyledLink = glamourous(Link, {forwardProps: ["innerRef"]})({...styles})

This feature is not supported by emotion and instead you need to do something like:

const MyLink = ({underlyingInnerRef, ...rest}) => <Link innerRef={underlyingInnerRef} {...rest}/>
const StyledLink = emotion(MyLink)({...styles})

// use it like this
<StyledLink underlyingInnerRef={handlerReceivingTheATag}/>

// or to be more specific about the differences:
<StyledLink 
  underlyingInnerRef={handlerReceivingTheATag}
  innerRef={handlerReceivingTheLinkComponent}
  ref={handlerReceivingTheStyledLinkComponent}
/>

How does this work in Emotion with object styles? Can't find it in the docs.

Glamorous way to make a element that is blue by default, be red when it has a certain class:

// glamorous way doesn't work in emotion
const SomeDiv = styled.div({
  color: 'blue',
  ['.someClass']: {
    color: 'red',
  }
})

@antoniobrandao:

const SomeDiv = styled.div({
  color: 'blue',
  ['&.someClass']: {
    color: 'red',
  }
})

Thanks @VinSpee 🙏

Thanks @VinSpee. FWIW you do not need the [ ] around the selector @antoniobrandao.

CMD+F "linkClass" here https://codesandbox.io/s/xrpjw8jnnq

Does anyone have guidelines for migrating from glamorous to styled-components? Having a lot of trouble getting it to work properly.

@syberen is there a reason you've chosen to migrate from glamorous to styled-components instead of to emotion? 🤔

@TejasQ We're researching both options, but styled-components does seem to have a larger community and I've gotten some reports of buggy behavior from emotion from fellow developers, although I don't know the details.

@TejasQ We're researching both options, but styled-components does seem to have a larger community and I've gotten some reports of buggy behavior from emotion from fellow developers, although I don't know the details.

Not sure where did that come from, but we barely receive any bug reports for emotion and even if there are bugs reported they are fixed pretty quickly.

Hello people!
I've taken some time to rewrite a big chunk of the codemod.
It's now in a much more solid state, and tackles most of my points above.

I'm most excited about all the transforms around the <glamorous.Div> syntax. Here some examples:

import g from "glamorous";

<g.Div marginTop={5}/>
// transformed to ↓
<div css={{marginTop: 5}}/>

<g.Div marginTop={5} css={{marginTop: 10}} marginBottom="5" onClick={handler}/>
// transformed to ↓
<div onClick={handler} css={{
  marginTop: 10,
  marginTop: 5,
  marginBottom: "5"
}} />

<g.Img width={100}/>
// transformed to ↓
<img width={100} />

<g.Span width={100}/>
// transformed to ↓
<span css={{width: 100}} />

<g.Span css={redStyles} marginLeft={5}/>
// transformed to ↓
<span css={{ ...redStyles, marginLeft: 5}} />

There's also an withBabelPlugin option, allowing you to decide what kind of emotion setup you want to support.

Thanks a lot to @TejasQ for the helpful code reviews! The code is in quite a readable state, should you be curious about babel-transforms :)

That's fantastic @danielberndt! Thank you and @TejasQ for all your work on that 👏 👏 👏 👏 👏 👏

I just published glamorous@5 and deprecated it so people installing latest will now get a deprecation message. (This means that existing projects using 4.x will not see a deprecation message, only people installing glamorous for the first time). I will also be archiving this repository in the next few days. https://glamorous.rocks will remain untouched for the foreseeable future (though the repo https://github.com/kentcdodds/glamorous-website will be archived as well).

image

I would like to give a special thank you to everyone who helped make this project what it was and inspired other projects to be built and enhanced pushing the css-in-js coding style forward. It was an awesome run. Thanks everyone who helped make the transition as smooth as possible.

I love you all <3

Love you too Kent 💞