apptension / react-boilerplate

A production ready boilerplate used in Apptension for all web React projects
MIT License
37 stars 7 forks source link

Introduce a company-wide CSS module standard #23

Closed pziemkowski closed 6 years ago

pziemkowski commented 6 years ago

We have standards for virtually everything else, but CSS. It was a little bit neglected, so we need to come up together with a solution to this problem.

There're a couple of solutions widely used:

Here's a great comparison of most popular technologies: https://github.com/MicheleBertoli/css-in-js

And of course, known and tested solutions:

@mikeqcp @wojciech-panek

cpBurn commented 6 years ago

Webpack CSS modules are great. Me and @mkleszcz used this in Shopthat and other projects, the bad part is that we needed to figure out how to handle it for the tests since the styles weren't processed to increase performance. For our case, @mkleszcz implemented a way to mock the styles:

import { keys, unset } from 'lodash';

const mock = {};

const resetClasses = () => {
  keys(mock).filter(k => k !== 'registerClasses').forEach(key => unset(mock, key));
};

const registerClasses = (clss) => {
  clss.map(value => {
    mock[value] = value;
  });
};

afterEach(resetClasses);

mock.registerClasses = registerClasses;

export default mock;

And because we were overwriting Module.prototype.require we could do

  if (path.search(/\.s?css/) !== -1) {
    return styleLoaderMock;
  }

So to use in the tests, we still would need to

  beforeEach(() => {
    styles.registerClasses(['my-class']);
  });

I know that we are not overwriting require anymore in current version of boilerplate, so maybe it's not a viable solution anymore.

pziemkowski commented 6 years ago

@cpBurn CSS modules are certainly a viable option since they still offer the most mature environment available.

The only thing I don't like is the weird notation of retrieving specific classes in a project you mentioned.

mkleszcz commented 6 years ago

I think it would be good to have some decision criterias here... For me the right tool should be/have:

Do you see anything else?

pziemkowski commented 6 years ago

@mkleszcz

compatibile with React and React Native but not tightly coupled (not every project will be based on React)

React Native is out of the discussion since it is not a CSS. There is a built-in Stylesheet solution there.

support for server render + would be good to have ATF out of the box

As far as I know, all mentioned solutions support SSR (ATF is a much more complex thing than just a styling issue, so we might even treat it separately)

full CSS support (best to avoid things like mimic :hover state using onMouseEnter events - @radium)

Well, then we can't use SCSS either, since it's not a "full CSS" by this definition. And honestly, since we would be using JS there are no limits to what we can do. This is an example of how material-ui does media queries:

[theme.breakpoints.up('sm')]: {
    container: {
      paddingTop: theme.spacing.unit * 8,
    },
  },

syntax highlight (at least for jetbrains) and linting

In CSS in JS you, of course, have a JS syntax highlight. The clue is in changing approach, and instead of using complex shorthand notation you introduce helper functions which can produce specific result.


Please, don't reject CSS in JS just because you are used to CSS. Just treat CSS as a regular code, and a lot of doors open up :)

wojciech-panek commented 6 years ago

I'm not that experienced in this issue but when it comes to voting I'm in for the styletron. Apart from all basic functionality it is designed to work with react and I really like the the idea to use props inside styles and extract some behaviour from regular components. Even if it is not that mature I think it is worth giving it a try.

The one think that I'm missing in styletron is the possibility to use some more sophisticated linters for example to force ordering props inside stylesheets in certain way to improve readability.

I've noticed that people tend to use styles as components more deliberately instead of just copy-pasting some css so I think it is good way to go.

Furthermore I think that no matter which tool we will choose we should provide solid example with themes, helper functions and so on. Maybe we can ask Kuba for help and make it look a little better?

wojciech-panek commented 6 years ago

There is one more big con which I haven't noticed writing post above: it is really hard to debug things when named cases are not present in DOM tree. I'm not sure if it is a trade-off which we are willing to take.

cpBurn commented 6 years ago

Please, don't reject CSS in JS just because you are used to CSS. Just treat CSS as a regular code, and a lot of doors open up :)

True, but I think approaches that moves away from the traditional separation of files .js and .css or .scss, will introduce more complexity and it will be harder to board in new people. Maybe our best approach here would be somewhere between JS and CSS, and for that case I think JSS is the best. We can still separate things in a simple way:

export const styles = {
  button: {
    fontSize: 12,
    '&:hover': {
      background: 'blue'
    }
  },
  ctaButton: {
    extend: 'button',
    '&:hover': {
      background: color('blue').darken(0.3).hex()
    }
  },
}

And can be used as similar as we use CSS Modules now:

render() {
    return <button className={styles.ctaButton} >Click me</button>;
}

Although it's not CSS/SCSS, it has a structure that reminds a well SCSS file so that wouldn't cause a big impact on people who never saw that.

mikeqcp commented 6 years ago

I don't have one winner from the list, but just couple thoughts based on my experience.

I usually go with "webpack CSS modules" and I think it is a good option that offers quite good flexibility for adjustments while still being separated into plain ".scss" files instead of merged with JS. In terms of testing, it's not that hard like Daniel wrote - you can just use libraries like this instead: https://github.com/bmatcuk/mock-css-modules. And then after you want to access styles.button in your code, you get "button" string returned there. On the other hand, regarding weird notation @pziemkowski mentioned - for me personally I like using https://github.com/gajus/react-css-modules to be able to provide module classes through separate attribute, so instead of:

<button className={`class-one ${styles.classTwo}`}/>

you can use equivalent:

<button className="class-one" styleName="class-two"/>

Second option that I've already used is styled-components and I think it's pretty good solution too. It also feels like quite mature already and supports all CSS features like hovers, transitions etc with standard notation while still being able to modify CSS based on props or use any JS variables inside the CSS code. The only important thing (which can be a pro or con) is that it forces a specific approach for separating presentation elements which need to be created separately and shouldn't contain any logic inside.

cpBurn commented 6 years ago

In terms of testing, it's not that hard like Daniel wrote - you can just use libraries like this instead: https://github.com/bmatcuk/mock-css-modules. And then after you want to access styles.button in your code, you get "button" string returned there.

@mikeqcp we didn't wanted to rely on that library, seems to be old and not maintained :/

React CSS Modules seems pretty cool though!

pziemkowski commented 6 years ago

To sum it up:

On a face to face meeting, everyone in the thread so far included, we decided to go with styled-component library and build styling ecosystem around it.

We will revise this approach after a couple of projects.