rabblerouser / core

Pluggable, extensible membership database for community organising
GNU Affero General Public License v3.0
17 stars 10 forks source link

RFC: Styling #93

Closed camjackson closed 7 years ago

camjackson commented 7 years ago

This RFC is to look at our options for styling the UI, and to pick a preferred approach. As always, we don't need to set this in stone forever, but we do need to at least pick something for the immediate future.

I'm going to try to keep this mostly informational, and put my opinions in a separate comment. I'm sure that my biasses will creep in though, so please feel free to suggest any corrections or additions to this summary.

Global vs. other styling

Global styles

There are certain things that are a natural fit for a traditional stylesheet such as fonts, box-sizing, etc. These would probably go in a single, top-level stylesheet that effects the whole application. This stylesheet should be kept as small and simple as possible, and shouldn't need a preprocessor like SASS.

All other styling

Everything else is a bit more uncertain. Plain old CSS has well-known issues with its global-by-default nature, and can very quickly become difficult to maintain, because you don't know how many places any given selector is affecting. In addition, you often end up in a specificity battle, and it's not long before everything becomes !important.

There are lots of approaches to solving this problem. Here is a decision tree that attempts to summarise the main choices to be made, and what solutions they might lead to (the options here are deliberately biassed towards what's in common use in the React ecosystem, seeing as that's what we're using):

styles decision tree - 2

Decision 1: Stylesheets or CSS-in-JS?

Keeping separate stylesheets might be more familiar to people who have worked with CSS in the past. On the other hand, it might not go far enough addressing some of the problems with CSS.

Some argue that mixing the styles in with the JavaScript is fundamentally wrong, as it violates the separation of concerns. Others would argue that splitting code up based on language is somewhat arbitrary - with React, the decision has already been made to define views (i.e. HTML) in JavaScript, so why not bring the styles in as well, and have all three encapsulated in one place for any given logical component?

CSS in JS may make it harder for e.g. designers who can write HTML & CSS (but not JS) to just work on the styles. Again though, that ship may have already sailed with the decision to use React.

Decision 1A: Assuming stylesheets - conventions or tooling?

If we are to keep writing stylesheets, then we'll definitely need to pick an approach that solves some of those specificity and global-ness problems.

Some claim that you don't need fancy tools and libraries to make CSS work - you just need to learn how to write good CSS. In that vein, specific methodologies/conventions exist in order to keep things neat. In the absence of any real experience with these, I'll just list some that I've heard of: OOCSS, BEM, SMACSS.

The counterpoint is that those conventions put the burden on developers to remember how things are meant to work, whereas a tool-based approach can put more constraints around how you work, making harder to forget/get it wrong. CSS Modules is an approach where stylesheets are split into import-able modules. The class names in each module get assigned a random suffix, so that .button in one module doesn't conflict with .button in another module. You then import the relevant module into JavaScript and look up the class names from the imported object. This solves the scoping problem. (See the link above for examples.)

Decision 1B: Assuming CSS-in-JS - inline styles or generated stylesheets?

The most obvious way to do CSS in JS is to just put everything in inline styles, e.g. <div style={{color: 'red'}} />. This works fine until you need to do something that inline styles don't support, such as pseudo-selectors or media queries. Radium is a library that solves this problem by tracking those states in JavaScript. I.e., if you put a :hover in your inline styles, then Radium attaches listeners to the onmouseover and onmouseout events of that element, tracks the hover state, and activates your hover style accordingly. For the most part this works fairly well, though some (perhaps rightly) criticise the decision to reimplement in JavaScript, behaviour that browsers provide natively.

The other approach to CSS-in-JS is to use some technique to generate a stylesheet from the JavaScript. Similar to CSS Modules, the scoping problem is then solved by randomising the class names, and looking them up from a JavaScript object.

Decision 1Bi: Assuming CSS-in-JS with generated stylesheets, which library?

At this point the decision seems to mostly be down to: which library's implementation is the best, and which library's API do we like the most?

After some research, the three most viable options seem to be:

Rather than inject my own analysis here, I'm just going to suggest having a look at the readme for each of the above libraries, in order to get your own feel for how the work. After that, this article is an interesting direct comparison of Aphrodite and JSS.

For completeness, one final link is this repo, which has a table comparing a whole bunch of CSS-in-JS libraries. If you see one that you think should be a contender, call it out!

yearofthedan commented 7 years ago

Thanks for the thorough write-up. There are so many approaches to this and I think coming to a considered decision early will be awesome.

Here's a couple of things that come to mind for me:

  1. An app level CSS file to do basic stuff including browser default resets is good to me.
  2. I agree that you’d struggle trying to touch only CSS / HTML these days without knowing js, particularly in single page apps, but we can definitely make it easier (or harder if so desired :wink:)
  3. 🎙 Opinioned rant: My main issue working CSS is when I need to target specific elements. !important I don’t see too much use of, but more common is to see either nested selectors or individual classname / ids to target the element. Too much nesting couples the CSS to the HTML and starts describing the DOM structure which makes it brittle. I’ve found SASS makes nesting easier, so often brings out the worst. I’m not a massive fan of it (and do others experience the pain of trying to work out imports and calculations and where mix-ins and variables are coming from?).
    • I should point out that I’m not all that familiar with particular patterns for CSS, so it could be that I'm just not very good at it. I could be swayed by a convincing argument here.
  4. I like a component focus where the style is kept with the component. I find it easier to reason about and I don’t have to go hunting through the project structure to understand what’s going on.
  5. My experience is with Radium and I haven’t had major issues with writing, updating or reading the 'inline' styles. I am open to hearing a convincing argument about avoiding inline styles. Its auto vendor prefixing is a useful addition if you can't guarantee that users will be running the latest.
  6. From the options, I’m interested in looking into styled-components. It looks like it may remove some boiler-plate and have better support for pseudo-selectors.
  7. Also just adding that layout is an interesting problem to keep in mind. I’ve had some success with layout components in the past but I do have concerns that they can pollute the DOM with divs and lead to accessibility issues. The to-be-adopted web components spec lets you define custom elements to provide a semantic tag encapsulating this, but it's currently only really available in Chrome.

So in summary, I lean towards anything that keeps the styles with the component and is easy to reason about.

pameck commented 7 years ago

The sign up app will be plain old css on a single stylesheet as it is easier for Rabble Rouser users to override it with their own styles.

As for the admin app, we still don't know but we're leaning towards the in-JS css given that it seems easier to maintain for us devs :)

yearofthedan commented 7 years ago

Started using styled-components. Will keep working with it for now unless there are concerns.