Modern CSS architectures compared

webpapaya
webpapaya
Mar 1, 2016 · 5 min read

CSS in a growing web application is really, really hard. I’m doing CSS related projects for a while now and every time I need to start a CSS project from scratch I think: “This time I need to do it right.” The hard truth is, every CSS project I touched didn’t feel right, even if they had been touched by Senior Creative Developers (That’s what experienced CSS enthusiasts have been called at my internship in New York). At some point the whole styling feels bloated with special rules everywhere. They either come from browser incompatibilities or designers who want to move a heading 1px to the top and 3px to the bottom. Even though most requirements from designers might seem stupid for us developers, it’s the designers job to make the product, landing page or whatever else he was designing look awesome. As developers we somehow need to implement and deliver those small design tweaks, without screwing up our CSS architecture.

Besides design decisions, dead styles become a real issue and bloat stylesheets for no reason. This might not be the biggest issue for small static web pages, but for growing single page applications dead code becomes a pain. Detecting dead CSS selectors is hard, especially when selectors are added dynamically to the dom. As selectors are spreaded around two different languages, simple refactorings like renaming and extracting common styles becomes almost impossible. Even tough preprocessors like Sass, Less provide mixins which can be used to share common styles, the automated refactoring possibilities are still limited. When using mixins, developers need to decide the common styles up front which might lead to the wrong abstraction.

“Duplication is better than the wrong abstraction” Sandi Metz

Besides inconsistent design decisions and the lack of automated refactorings, CSS the language itself is a pain as Vjeux summarizes:

  • Global Namespace
  • Dependencies
  • Dead Code elimination
  • Minification
  • Sharing Constants
  • Non-deterministic Resolution
  • Isolation
  • New Syntax/Language

Here at Crewmeister we plan to do a redesign of our application and for that purpose we’re looking for a flexible and maintainable CSS architecture which can easily evolve over time. In our opinion the new architecture should contain the following qualities:

  • Easy reasoning about where styles are coming from
  • Avoid selector clashes
  • Special cases are easy to implement:
    e.g. Second item in a navigation needs to be bold
    Application | Pricing | Features | Login
  • Portability of our components:
    use same menu bar in our app, blog and landing pages
  • Consistent Styling Possible (DRY Styles)
  • automatically add vendor prefixes
  • variables/constants
  • fast unit tests for our styling
    element A needs to look exactly like element B

During my research I found the following four CSS architectures, which looked interesting for us:

Regular CSS with a naming conventions

As Chrome and Firefox recently implemented CSS variables, one killer features of CSS preprocessors is natively available in modern browsers. Even tough IE and Safari don’t support them yet, PostCSS with the CSS next plugin port this syntax to incompatible browsers.

Because of the global context of CSS naming can become very hard. This global context lead to a couple of naming conventions. Hiljá Studio gives an overview of different naming conventions.

Advantages:

  • easy to get started (when plain CSS is used)
  • integration is easy
  • designers/developers are used to it
  • autoprefixer can be integrated
  • Browser developer tools can be used
  • cache and gzip files

Disadvantages:

  • new language as a dependency
  • global css declarations (name clashes)
  • portability is hard
    js and css files need to be included
  • dead code elimination is hard
  • automated refactoring tools are missing

Tools:

CSS Modules:

CSS modules are an unofficial specification of a CSS extension which solve the global context of CSS.

A CSS Module is a CSS file in which all class names and animation names are scoped locally by default. (CSS modules)

CSS modules make it possible to import stylesheets into a JS file. Under the hood the imported selectors are replaced automatically by a global unique selector. Because CSS modules are neither an official standard nor implemented by any browser, a build tool like webpack or browserify (with CSS loader plugin) is required.

CSS modules only solve the global CSS issue. If variables or automatic vendor prefixing should be part of the architecture a separate build step is necessary.

Advantages:

  • no global CSS anymore
  • Developer tools can be used

Disadvantages:

  • min. 2 dependencies required
    CSS Preprocessor
    JS build tool (Webpack, Browserify + Plugin)
  • new language as a dependency
  • portability is hard
    js and css files need to be included
  • dead code elimination is hard
  • automated refactoring tools are missing

Tools:

Inline styles generated by JS

Inline styles seems to be the new trending topic in the react community. It allows the usage of one unified language to generate styles, markup and business logic. Styles are generated by js objects which are transformed to inline css rules. Those rules don’t support media queries and pseudo elements (:hover, :before, …), which need to be reimplemented in the browser. There are libraries like Radium which solve this issue for react applications. For that purpose radium is reimplementing media queries and pseudo elements in JS.

Advantages:

  • no global context
  • no new language dependency
  • JS refactoring tools can be used
  • dead code can be detected
  • styles can be calculated during runtime
    e.g.. custom color scheme for ever customer, without generating a new stylesheet on the server

Disadvantages:

Tools:

JS generated stylesheets

JS generated stylesheets can be seen as a new kind of CSS preprocessor. Stylesheets can either be generated on the server or during runtime on the client. Server side stylesheet generation enables caching of styles and gzip compression. As Styles are written in JS, automatic refactoring tools can be used to eliminate dead code and extract common styles.

Advantages:

  • no global context
  • no new language dependency
  • IDE refactoring tools can be used
  • dead code can be detected
  • styles calculation during runtime is possible
  • autoprefixer can be integrated
  • cache and gzip files

Disadvantages:

  • prototyping/debugging with dev tools is more difficult
  • designers might not be familiar with js

Tools:

Conclusion:

CSS was never built with web applications in mind. As styles are getting more complicated we need to find a convenient way to deal with styling concerns. Here at Crewmeister, we decided to give “JS generated stylesheets” a try. In our case we can use the tools we already know and don’t need to introduce a new syntax/context into our stack. We just need to include a new library and for the first face of the redesign thats all. Because we already minify our JS we don’t need to add a dedicated minification step for our styles. JS became the main language we’re speaking on the client, which enables us to use refactoring tools to easily rename wrongly spelled ‘selectors’, extract common styles and remove dead code.

We just started adopting this pattern in our application and for now this looks really promising. We still need to solve auto vendor prefixing and server side generation of our styles, but I think those are issues which can be solved.

webpapaya

Written by

webpapaya

Hello my name is Thomas Mayrhofer and I'm a Software Craftsman living in Munich.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade