facebook/create-react-app

Preprocess future-proof CSS features

giuseppeg opened this issue · 63 comments

Would you be open to add a few future-proof PostCSS plugins to transpile things like custom properties, calc, custom media queries?

This could be as simple as using the postcss-cssnext plugin or set up a lil preprocessor like we did for suitcss.

If there is interest I can probably help out.

There's an ongoing discussion about this in #78!

The TL;DR by @gaearon about cssnext is:

Whatever we add, it needs to be rock-stable. I’m worried about issue lists like this: https://github.com/postcss/postcss-import/issues.

That one is a more broad topic since it is about adding support for SASS/Less.
What I was asking here is if there is any interest in transpiling future-proof features like we already do in JavaScript with Babel.

Regarding cssnext, yep I agree that "there’s a lot of stuff there" – if we want to play it safe we could just add imports, variables (custom properties), calc, custom-media and autoprefixer.

Anyway I am fine with waiting and see where #78 goes.

We are open to add transpilation for standardized features if those tools don't have large issue lists. We don't want to recommend broken tools by default.

Since #78 has gone a little bit too broad, let’s use this issue for specific proposals about PostCSS plugins that are safe to enable, work great, correspond to stable specs, and give at least some of the features people want.

Stable

TBD

N.B. the plugins below don't support dynamic custom properties i.e. cp are compiled to static values.

Personally I also use postcss-autoreset and Stylelint but those require custom configuration.

MoOx commented

My 2 cents:

  • postcss-import "just" need a maintainer + revert to v7 (which was more stable than the current one).
  • I can handle a revert to postcss-import to v7 if that can be useful to a lot of people, should take a second.
  • postcss-easy-import use postcss-import v8, so forget this one imo.
  • postcss-cssnext does not includes postcss-import !
  • postcss ecosystem is not rock solid anyway (postcss-import is a proof, imo a core plugin, does not get any love from anybody - and I asked for help multiples times)
  • I can give access to some maintainers for a lot of postcss plugins I created (mostly all future proof syntax related plugins).

I am myself using CSS-in-JS (via RN-web) and avoid CSS when possible.

I'd love to see postcss-cssnext as it has autoprefixer and pretty rock solid and optional features baked in

I would strongly advise to take caution with CSS custom properties, and if something like postcss-custom-properties were to be included, it would be a good idea to set: preserve: true (not computed) in order to maintain the spec behavior as closely as possible.

Here's a trivial example of a potential issue:

:root {
  --foo: green;
}

header {
  --foo: red;
}

h1 {
  color: var(--foo);
}

What color is <h1>? Answer: it depends. It's 🍏 by default, but when it's in a <header>, it is 🔴, because custom properties cascade and inherit. Here's another fantastic example of that behavior: http://lea.verou.me/2016/09/autoprefixing-with-css-variables

Keep in mind: all major browsers (except IE and Edge, where it is in progress) support CSS Custom Properties.

ai commented

I like this feature: https://github.com/postcss/postcss-media-minmax

@media screen and (width >= 500px) and (width <= 1200px) {
  .bar {
    display: block;
  }
}

Polyfill is very stable. Spec is a official draft: https://drafts.csswg.org/mediaqueries/#mq-range-context

Feature is pretty useful, because it is always hard to work with min-/max- stuff instead of >=/<=.

ai commented

will-change polyfill is very useful, but it is not so stable (will not work if you defined backface-visibility on same tag, but with different class). So it is open question, should we add it.

https://github.com/postcss/postcss-will-change

ai commented

I think this plugin is mush-have: https://github.com/postcss/postcss-color-function

  1. It is very useful, color transformations are one of the most popular feature
  2. Spec is very stable
  3. Polyfill works without any problem (any lack of documentation)
ai commented

:any-link is useful, stable and have no problems in polyfill: https://github.com/jonathantneal/postcss-pseudo-class-any-link

ai commented

:matches is useful, stable and works well: https://github.com/postcss/postcss-selector-matches

Like me and @taion noted in #78, there are few that aren't/can't be to spec, or actively allow non-spec behavior. In particular the color() function is insiduously wrong, in that it doesn't distinguish between lightness(+10%) and lightness(+ 10%) (did you even notice the difference?) in the modifier functions. That whitespace should determine whether the change is absolute or relative, but it doesn't distinguish right now

taion commented

As @davidkpiano and @jquense note above, most of the more useful cssnext features have significant caveats.

  • Custom properties only polyfill the simple case where the property serves as a variable (with clunky syntax), and doesn't handle the new cases that the syntax actually allows
  • Color function does non-spec-compliant things around whitespace between modifier sign and modifier value
  • calc() polyfill only works if all units match
  • Nesting improperly allows Sass-like concatenative selectors: csstools/postcss-nesting#17

cssnext is very cool, but it's not like Babel in that the features it transpiles are transpiled in a manner that is close enough to spec compliant to "just work".

Some of the smaller polyfills in there might be nice, but the "big" ones have meaningful caveats attached.

taion commented

The capsule summary is that, for people not paying close attention to the actual spec, Babel loose mode is order of magnitudes safer than the majority of new features enabled by cssnext, and CRA isn't even using that: #198

the only thing that I really would like to see are css variables. They are already available in chrome and firefox -- so it's like a working and already implemented thing. But it does not work in IE and Edge that is why we need to post process it.

so my vote goes for https://github.com/MadLittleMods/postcss-css-variables

taion commented

They're not variables, they're custom properties – and as the repo you mentioned notes, they can't be correctly polyfilled, so you end up with support for either an odd arbitrary subset, or non-spec-compliant behavior that you're actually likely to run into (unlike with Babel).

It's not at all a reasonable default.

ai commented

@jquense could you please open a issue in postcss-color-function. Your example with space could be fixed and it is not fundamental problem of polyfill. What else color() issues do you have?

ai commented

@taion don’t forget that postcss-custom-properties warn user if it could not support written feature. So it is very safe. The only unexpected problem with postcss-custom-properties is working from several files:

/* a.css */
:root {
  --foo: red
}
a {
  color: var(--foo);
}

/* b.css */
:root {
  --foo: green
}

postcss-custom-properties will generates red link, but it should be green. I think it is very rare case (to have same custom property name with different files).

ai commented

@taion sure, postcss-calc is not to implement any calc(). But current calc() support is very useful too (it is same as in Sass math). And if user use multi-unit math in calc(), postcss-calc will keep calc() without changes — so this polyfill is very safe.

In my proposal I mentioned that all of the plugins under "TBD" don't support dynamic custom properties i.e. custom properties are compiled to static values.

Since this is a limitation that we cannot solve in this issue and considering that we may not even come to an agreement I want to suggest another idea:

How about we introduce a small postcss-based preprocessor that can be extended using a configuration file? We did this for suitcss and it works great.

See

In the CRA case the default plugins array would contain only autoprefixer.

A system like this gives people the flexibility they want and doesn't force us to make the call for 10.657 people (atm :-).

ai commented

@giuseppeg we will have a common PostCSS config soon https://github.com/michael-ciniawsky/postcss-load-plugins

I agree with you about postcss-custom-properties. But I think that we should enable more stable plugins with spec full support:

  • postcss-color-function
  • postcss-custom-selectors and postcss-custom-media
  • postcss-media-minmax
  • postcss-pseudo-class-any-link and postcss-selector-matches
  • postcss-color-gray, postcss-color-hex-alpha, postcss-color-hwb, postcss-color-rebeccapurple

Sorry, if the comment is not directly related to the conversation. what is the point of using postcss + plugins, over time proven solid preprocessors (I talk mostly about sass)?

Anyway I think it's better to not include any CSS related things into the project at all, as CSS subjects are always about holy war. Someone loves CSS-in-JS, other loves SASS, another person decided just give up on CSS at all and focusing on some more important part, as CSS itself is just works. IMO

ai commented

@vanesyan PostCSS is a framework to write CSS tools. You can re-implement preprocessors task on PostCSS (like PreCSS plugins pack), you can re-implement other non-preprocessors task (like cssnano to minify CSS), but also you can implement completely new tasks on PostCSS (Autoprefixer and CSS Modules is a great examples).

In this issue we discuss about using PostCSS for one of that PostCSS-only tasks — adding some plugins to polyfill future CSS specs (like Babel does for JS).

My main reason of using PostCSS not PostCSS-only task (like preprocessor or css minifier) is unification. It is very good when all your tools use same parser and shared once parsed AST tree. You will have better performance and cross-tools compatibility.

ai commented

@vanesyan I agree that this project should not contains any preprocessors, because we have too many of them (Sass, Less, Stylus, PreCSS). But this is why future-proof PosTCSS plugins is better. They implement spec, so it is not part of holy war, it is a future ;).

@ai postcss-load-plugins looks indeed similar to what we built and it'd be great to have such a feature in PostCSS core.

Regarding other plugins I think that it is not possible to transpile any feature that can manipulate custom properties and therefore we cannot enforce any of them.

@ai But any spec. by W3C so unstable while drafting stage. And for example @custom-media future implemented in postcss-custom-media module was completely removed from the spec. I'm not against of PostCSS (I love it and use it on my own ;)), but sadly there is no way to implement 100% following spec. polyfills for CSS, it's impossible by design.

ai commented

@giuseppeg other plugins is not relevant with custom properties. For example, postcss-media-minmax is very static, have nothing dynamic (you could not even use custom properties in at-rules).

ai commented

@vanesyan ouh, I missed that @custom-media was removed from spec :(

Sadly CSS isn't JavaScript and there's no industry standard in the wild for writing idiomatic CSS. Users use preprocessors that they love (know?) best. Because of that such projects shouldn't include CSS related things instead giving developers decide on this part. IMO

MoOx commented

Reminder: I am the author of many "future proof" PostCSS plugin and postcss-cssnext.

This was fun to do play with PostCSS. This allow to write future proof code that will hopefully end up to work (exactly like babel, even if sometimes you got stuff that won't end up being the spec - eg: decorators). If you use it wisely, you can really enjoy it, but there is currently a lot of know issues, and nobody is working on those (I won't list all here, but trust me, I know at least one for all plugins listed above as "stable")

Also there are 2 main differences between babel and PostCSS:

  • babel started as a "no conf" solution (6to5), and evolved to be a "small conf" (.babelrc are probably most of the time what CRA offers).
  • babel have a runtime. PostCSS don't have that.

PostCSS and cssnext are a very nice way to experiment thing. I used it for years and still do when plain CSS is required (eg: "server" side rendering for Phenomic, my static website generator).

But again, let's be clear: each postcss plugins I know have tons of issues. All of them.
Not a single PostCSS plugin is rock solid, or maybe some that do only "preprocessing" stuff.
Most plugins have a single maintainer (if we can use this word - I am the "non-maintainer" of postcss-import, since nobody responded to my call - and the person responsible for the v8 never looked back...). When I started I got big plan for postcss org (I asked for this "org"), but time proved me that CSS is, well, not really mature anyway. Current specs are sometimes funny (please don't look at the recent change for functions args - eg: rgba(r,g,b,a) will become rgba(r g b, a) or something like that) and does not look like Ecmascript standard at all, on so many level.

Using PostCSS for stuff like PreCSS is kind of stupid (if you just use this preset), cause you it just a rip of Sass, without the maturity and the community around.

Cherry picking plugins will be the worst solution: people won't stop asking for new ones.

From what I understand of CRA, "experiemental" is not an option.

This make sense: CRA authors don't really use it and don't want to maintain stuff they won't use.

According to the number of stars the project got in so little time, we can assume that this project is not just experimental: people probably already rely on it and trust the provider (facebook"incubator")).

IMO, if you want no conf, just use Sass or nothing. Note that I don't like Sass, but at least I can agree it's mature, compared to PostCSS.

Relying on PostCSS is a very bad idea on many levels. The power of PostCSS is "compose your own thing". This just does not fit with CRA spirit since there is not a single mature PostCSS "preset".

Last note: I see React as a way of not having to deal with HTML anymore. I think CRA should not bring any CSS preprocessors, since CSS is the same kind of low-level language like HTML. I really think that if CRA want to move forward with styling, it should wait for a more mature solution, or show to people CSS-in-JS solutions that don't require configuration (since server side rendering is not supported, and might never will.)

My 2 cents:

Using these build scripts is the first time I've tried importing CSS with JS, and I see why it makes sense in some ways - to rev assets mainly. But it doesn't really fit with the 'cascading' part of CSS and feels a bit like trying to push a square peg into a round hole IMO.

In CSS you actually want global/generic rules which get inherited or overridden. You actually want to set variables for things like colour and spacing in one file, and use them as much as possible in other files. You kinda want the opposite of a JS require-type module.

There's too many great techniques for CSS that a complete zero-conf build script would have to (and could) implement - auto-import, auto-fontface, auto-prefixing, inlining of small url() assets, sass - these are all great conveniences I have been using, and to leave them behind for edge (not Edge ;)) css4/cssnext seems like the wrong move.

@gaearon continue the twitter conversation.

My 2 cents.

SASS, LESS ... vs PostCSS

Meh, realistically I wouldn't put any preprocessor by default in the project because is so relative to people preferences.

But SASS, LESS ... vs cssnext

The only reason that I use PostCSS as a CSS [Insert whatever you called it here] is because cssnext plugin.

As Babel, cssnext is trying to allow us to use some future features of the CSS spec. Isn't some random feature that somebody thought, at least it is in the roadmap of the CSS specs.

From there, I would support the idea of add PostCSS with a strict list of features. Either when is cssnext because enable some future features or is some other feature that is based on the SPECS.

I understand that there is some constrains in the fallback, things like custom properties are allow only in :root selectors, but this shouldn't be an issue.

We as Engineers, have to understand the limitations and work around it or avoid it. This happened to me with class attributes from Babel, I introduced to the feature knowing that being in state 0 could means that they will remove it, but I took the responsibility of make that choice.

The point I am trying to make, don't kill a whole feature because could change in the future. You have the choice of either use it or not, but don't kill it. That's why I told @gaearon "All of them", meaning it's your responsibility to use cssnext features or not.

And again just reminding, cssnext is trying to give us some CSS specs today and the same way we support Babel for everything in 2016, we should start supporting such of good tool which is pretty much trying to do the same as Babel do, yes with constrains but at least is something close to the future.

P.S:
I would like to see the community of React to push and help cssnext concept. The only reason that Javascript is moving forward that faster is because years ago some person start a project called 6to5.

With problems, limitations and bugs but push forward a language and because crazy people decided to take the responsibility to use in production, defensive people watched how actually worked and the benefits that gave you and they started using as well.

This is the time for support something that is needed it for a long time.

taion commented

@yordis

The maintainer of a significant number of the PostCSS plugins used in cssnext just said a few comments upthread:

Not a single PostCSS plugin is rock solid

cssnext is nowhere near where Babel was in stability. And your understanding of history is quite lacking if you think 6to5 was the only transpiler around.

This is just not a reasonable tradeoff to make here. cssnext stability is somewhere worse than Babel stage-0, which this project also doesn't use.

taion commented

If you want to compare cssnext to Babel, it's like Babel stage 0, but less stable, less well-maintained, and with more caveats around spec compliance.

It's absolutely not appropriate for use in a "starter kit".

And I say this as a mostly happy user of cssnext.

I don't think we're going to find an answer here that satisfies everyone, since there's so many different needs and use-cases when it comes to styling and CSS. I agree with @MoOx that adding PostCSS plugins is risky and can lead to conflicts with the actual implemented spec in browsers, and something more mature like Sass (SCSS syntax) might be too opinionated, especially with the dependency on node-sass. And inline styles are right out, since there's a myriad of competing ways to implement them and the harsh reality is only a small percentage of React developers are fully invested into inline styles/CSS-in-JS solutions.

Is it best to just stick with the lowest common denominator, vanilla CSS with PostCSS for autoprefixer?

@taion what if we actually try to make it work. As a community we fix it and make it rock solid. It's just different prospective. Everything start with you (community, yourself, myself ...) That's why I am supporting the idea.

Or like @davidkpiano and even @MoOx said, just close the topic and don't use it, but then we just killed a potential tool.

Or even better, open the configurations so you plug in whatever preprocessor you want, and problem resolve

ai commented

@MoOx

Not a single PostCSS plugin is rock solid

Relying on PostCSS is a very bad idea on many levels

each postcss plugins I know have tons of issues. All of them.

Using PostCSS for stuff like PreCSS is kind of stupid

I agree with you, that CSS specs are not so stable to have future-proof plugins (Custom Media removing change my mind), but don’t be so toxic about speaking of all PostCSS.

Autoprefixer is rock solid and no big knowing issue and using it is a great idea on any level ;).

CSS Modules works great too and definitely don’t have “tons of issues”

PostCSS is not only “cssnext” stuff. There are many other tools, which works great. And create-react-app already use some of this tools (Autoprefixer and css-loader).

Other great tool that could be implemented in zero-config app is a Stylelint — it has non-codestyle rules to avoid mistakes.

MoOx commented

@ai My goal is not to be toxic, it's to be relevant to CRA requirements.
Sure Autoprefixer is rock solid (I had this one in mind when I said that some are probably fine). I didn't take this one into account since it's already built-in.
CSS-Modules is also very nice, but it's a "special" plugin, probably too opinionated, and you know it.
Stylelint is also not a classic PostCSS plugins too. I guess this one can be a good choice. But here we have a linter, not a preprocessor.

Obviously there is some good in PostCSS :)

Except Styelint with a small config (even the styelint-config-standard include stylistic rules) and Autoprefixer, I don't think something will be the right fit for CRA.

Should we use github's reactions to decide what to do about this issue?

I would suggest to use ±1 (thumb up/down) on this comment to decide if we want to:

  1. 👎 keep the status-quo and close this issue
  2. 👍 keep the status-quo but add the possibility to customize postcss via config file as described in my previous comment

cc @gaearon

MoOx commented

No config, means no config. I am pretty sure nobody want to add config just to support the (not really mature) PostCSS ecosystem :/

@MoOx I don't have strong opinions, I just think that we shouldn't discuss if PostCSS is good|bad|stable|unstable forever :)

fwiw 2) would be an add-on only feature i.e. if you want to configure postcss you have a way to do so otherwise continue to do what you were doing :)

fwiw2 I also think that in the long run css-in-js will win so shurg

ai commented

I agree that “no config” means no config”.

(not really mature) PostCSS ecosystem :/

@MoOx don’t be toxic. If cssnext plugins is not really mature, doesn’t mean that other plugins are not really mature. Stylelint, Autoprefixer, PostCSS Assets and CSS Modules show a great example of mature ecosystem.

It is especially painful to hear “not really mature” from your, because you a maintainer of cssnext, and esosystem problems in cssnext is on you. Don’t spread cssnext’s lack of maintaibility to other well maintained plugins.

Let's find a solution that works for everybody

No config means no config

Well, then don't force the package to lookup something and have predefined PostCSS plugins that only are well maintained and curated.

Config, Hurrah!

Just add a parameter that is a Javascript file, arguments could be many things open to people who know more than me, where you could whatever Javascript allow you to do 😄
This way pretty much we cover the use case of advance users like me, who don't want to lose the flexibility of adding some stuff.

@giuseppeg I support both cases, 0 config and advance user, because I really dont want to lose the flexibility of adding some good stuff like @ai called, so

ai commented

Anyway I think that we could close this issue. Main topic was about future-proof plugins. As was shown CSS specs are not so stable as ES2016 was in 2015, so plugins (even very well done with good support) could not be future-proof for zero-config case of CRA.

@yordis maybe you should open a separated issue about config.

I would prefer @gaearon to open a new issue if need it. The core contributors have the final word on this.

For design-led websites - which is a lot of websites, and what I make all day - unfortunately I can't use this amazing, maintained, JS build tool if it doesn't support SASS, or any kind of CSS betterer.

😔

If I could hook my own CSS (and other asset) build tools in with this somehow, and have the browser update with changes, I'd find a way to make it work.

For design-led websites - which is a lot of websites, and what I make all day - unfortunately I can't use this amazing, maintained, JS build tool if it doesn't support SASS, or any kind of CSS betterer.

If I could hook my own CSS (and other asset) build tools in with this somehow, and have the browser update with changes, I'd find a way to make it work.

It supports SASS in the exactly same way people have been using SASS for years. Run a console SASS watcher, and import the resulting CSS from your JS files.

Thank you, I ended up using gulp to build all my non-js tasks.

For SASS, I compile to _styles.css at the project src root, import that css file at my JS root.. and update npm start to

npm-run-all <gulp task> <react scripts>

and everything works great.

@gaearon I feel like that we can close this issue unless you want to implement this thing

@daviestar do you have one css file per component then? Or just a single massive _index.css file at project root?

@tonyxiao I put SASS files in with my components, but as I said above I don't really agree I should import them into JS as IMO, CSS doesn't work that way. I can easily avoid it by compiling all my SASS files to one CSS file at the root using gulp, then just import that so webpack picks it up in development.

Got it, thanks a pretty workable approach if you just completely avoid importing css into js. thanks for sharing @daviestar

Let’s close as there is no consensus and I don’t feel comfortable proceeding with this.

That Sassy nesting has finally been fixed.

MoOx commented

FYI, I deprecated cssnext in favor of postcss-preset-env, you might want to think about this one that offers stages like babel-preset-env!

ai commented

Yeap. With postcss-preset-env we can enable only stable CSS specs. It has stage option same as in Babel.

We run postcss-preset-env now.

I’d love to share this with my family and friends. I wrote PostCSS Preset Env, the tool you are using to future proof CSS. I hope it’s not presumptuous to ask, but might it earn a mention or thanks in the blog notes, @gaearon?

https://reactjs.org/blog/2018/10/01/create-react-app-v2.html

We should probably include it into the notes. I forgot.