The Case for CSS

There is a lot of thinking and development around bringing styling support into Javascript. The obvious benefits are that we are able to have every output of our application in one single programming language. We can produce HTML using JSX or a template syntax, we can either attach inline styles or generate Stylesheets which are dynamically attached to the document.

I’d like to offer a quite critical viewpoint here. I’d like to take the position of the end user, not of the application developer. The actual user of our application benefits when we have quick load times and excellent rendering performance. No questions.

Therefor, to produce the best results, we need to enable the browser to do as many things in parallel as possible. Also it might make sense to prevent anything we do on the client which could have been done during deployment. With these goals in mind there are some issues with any solution for client side JavaScript based styling.

Repeat, Repeat, Repeat

Vendor prefixing of style properties and values require some kind of database and additional logic. If we rely on JavaScript on the client side we have to transfer the database (which is typically quite large) to the client and have to “expand” the original declarations into vendor prefixed declarations. While feature testing is fine for simple properties availability it is far trickier to handle the value part. This means quite some processing power and memory usage.

There is also no actual benefit from having CSS in some JavaScript runtime memory. Once created we can’t freeze the created stylesheets easily. The code and maybe mem-cached property/value normalization is still in the client waiting for the next block of CSS to normalize. Client side styling solutions tend to consume more memory than pre-generated static CSS does. This is especially dramatic on memory compromised mobile devices.

Too much Flexibility

The idea for CSS in JS is also often combined with the concept to use a lot of mechanics from native JavaScript e.g. variables, conditions, loops, etc. While this is obviously useful: making the super static CSS somewhat dynamic. The side effect though is that there is a very clear chance that you add so much logic to the styling — which depend on some runtime condition — that this can’t be statically compiled anymore. Static compilation is a fundamental requirement to make server side rendering useful though. Just to clarify: what I mean by „static“ here, is the idea that in the end the styling definitions result into the exactly same output, independent from the rendering location (client vs. server). That is actually the same limitation we have for SSR in React based applications: It even verifies the actual rendered content with checksums.

The original concept of dynamism in pure CSS is to toggle CSS classNames and HTML attributes not changing the “content” of the actual classes. Unfortunately exactly this aspect is not prevented by most solutions client side CSS generation. Responsibility for this is added to the application developer. Conclusion: To make CSS-in-JS successful for server side rendering we should limit its features so that we are ideally able to do most of the features during deployment.

Multiple Invalidation of Document Styling

Browsers are highly optimized for the rendering of static documents. HTML is still mainly a document container. Interactivity was optimized for either for:

  1. changing classnames or attributes
  2. changing inline style properties
  3. adding, removing & modifying elements

Something which is pretty radical and therefor “expensive” for the runtime, but is probably not super obvious, is to add/modify/remove stylesheets to the document. We dealt with this issue years ago during the development of the qooxdoo framework. Most browsers work with some kind of compiled document stylesheet. There is a lot of caching and building of style trees involved in making the previously mentioned interactive mutations as fast as possible.

When we are adding new stylesheets, we are invalidating the compiled style information of the document. When each component is compiling its own CSS and adding it into a style tag this is invalidating this cache and re-evaluating the selectors and document nodes a few times. This adds a lot of pressure to these browser subsystems. (I can’t wait to hear that something has been optimized in recent years, but I did not heard about that yet.)

Flash of Unstyled Content

For an optimal user experience it’s required that HTML elements added to the document have a valid styling before the browser is trying to render them. FOUC might apply for both: client side and server side rendering. The second is by far more complex to deal with in typical CSS-in-JS solutions. As most of these solutions require information about the client e.g. for vendor prefixing, they generate the stylesheets during mounting of the existing HTML tags to client side logic (rehydration) phase. Unfortunately nothing prevents the browser from rendering the existing HTML before the rehydrated stylesheet kicks in. Hiding the HTML before the JavaScript is being executed would destroy the benefit of SSR completely.

Asset Prefetching

Resources added to CSS files are downloaded in parallel with all other resources. When we are adding CSS dynamically to the document we are preventing the browser engine from benefitting from this. Typically styles are added to the document when the individual component is first being rendered. This is mainly done for reducing the initial rendering time. Unfortunately this also means that referenced assets (web fonts, icons, background images) could only start loading when the stylesheet in generated and then added to the document. While it’s (somehow) possible to detect the loading state of stylesheets and delay the rendering of the component, it’s probably not possible to do this with all the referenced assets used in there. It would be beneficial if the browser would have already taken care of this. Using HTTP2 it could have downloaded all these required assets already while doing other work. Producing CSS in JavaScript brings a lot of the negative aspects of JavaScript to CSS. Something previously asynchronous with excellent network utilization is being made pretty synchronous

No styling specific tooling

Doing CSS in JavaScript is always a second class development platform for implementing styles. Right now it’s tricky to even get the basics every CSS developers regularly uses. Auto complete with matching values for specific properties e.g. for display, syntax highlighting of errors e.g. for simple typos like rgb(0, 0, 0, 0.5), linting with tools like stylelint, etc.

Further there is some kind of lock-in. You are actually betting on the specific syntax and functionality. Right, this is somewhat comparable to choosing Sass, Less, Stylus or PostCSS plugins. But generally the ecosystems around CSS-in-JS are far smaller than the previously mentioned more classical approaches. Try counting the possible alternatives. Wait a few weeks for adding additional candidates to your list.

No delegation of CSS possible

In most larger development teams the implementation of stylesheets could be delegated to more experienced CSS developers. That everybody can be done by everybody: One of the biggest lies in our industry. Specialists can handle CSS cross browser issues and layouting details much easier and faster than most SPA developers. These should better focus on topics of overall application architecture, data flow, performance, etc. If we could split development work into two blocks which are implemented by more experienced developers — why wouldn’t you do that?

Benefits of pre-generated CSS files

So what are the benefits of not doing fancy CSS-in-JS but keeping a more traditional approach:

  • CSS Modules allow for sandboxing of components and different application views.
  • Adding prefixes can be implemented with the largest and best database from Can I Use without any performance and size side implication on the client side. Don’t fall into the assumption that some basic vendor prefixer is capable of doing nearly the same.
  • Variables, conditionals and loops are fully resolved during deployment. Everything PostCSS or Sass can do is strictly limited to something which do not require (or even are capable of) access to runtime state.
  • Excellent combination with SSR as both HTML and CSS are statically produced beforehand and send to the client. Parallel loading of required assets, no flash of unstyled content.
  • Full CSS tooling available with excellent editor support, linting, auto completion and no lock-in a specific syntax.
  • Preprocessors have access to the file system. They are able to query referenced assets e.g. image dimensions, automatic SVG spriting, generating icon fonts based on usage patterns, etc.
  • Sourcemaps are pointing developers to the original source location of every line of your CSS. This is pretty hard. You can’t inspect a style which is generated by JavaScript. Sure enough with the sandboxing it should be clear from where styles are coming.
  • Make us of a great number of experienced and productive HTML/CSS-only developers who can easily build cross browser layouts and designs. Modern SPA are not just logic and data but also modern visuals and animations.

Our company, Sebastian Software, built SPAs for many years for and with different enterprise customers (Deutsche Telekom, Deutsche Bank, Huawei, …). We evaluate the needs, consult and build solutions for each client individually. We did not yet come to the conclusion that generating CSS in JavaScript is the best solution for any of these.

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.