Styling your application. How we deal with CSS.

Vlad Antsitovich
9 min readApr 4, 2022

--

Anyone who has been writing CSS for a while, knows there are complexities that come with it and how painful it can be.

In example below we can see the oldfashioned approach of how we organized our CSS in project. In the left side we can see all CSS files in our project and on the right side the output CSS after concationtion and optimizaiton.

The reason why this approach is oldfassiond is because in modern web development we use component approach. So we need to store all CSS as close as possible with our components.

We live in world of “solutions” and there are hundreds of tools and librarys that helps us, but there is no universal solution. So let’s figure out witch is better and when we need use these.

But before we start I would like to provide list of some important CSS consepts every developer who works with styles should know:

Vanilla CSS

Most of us are absolutely terrified to touch raw CSS. And it’s justified.

The following is a list of the issues with CSS:

  • Large code base
  • Global namespace (name collisions)
  • Dependencies
  • Dead code elimination
  • Minification
  • Sharing constants
  • Non-deterministic resolution
  • Isolation

A large code base we need to maintain and support

You need to dig into the stylesheet. Most of the time this means going through 1000s of lines of CSS. Looking for media queries and how everything else comes together. When you need to make changes to UI the easiest thing to do is to create a new CSS class and forget about everything that was previously written. But you can create a duplicate CSS.

Global namespace (name collisions)

One of the well-known problem of CSS is that all the selectors are global. No matter how we organize our styles, using namespaces or a procedure such as the Block, Element, Modifier (BEM) methodology, in the end, we are always polluting the global namespace, which we all know is wrong. It is not only wrong in principle, but it also leads to many errors in big code bases, and it makes maintainability very hard in the long term. Working with big teams, it is non-trivial to know whether a particular class or element has already been styled, and most of the “time, we tend to add more classes instead of reusing existing ones.

Dependencies

Another problem with CSS regards the definition of the dependencies. It is very hard, in fact, to state clearly that a particular component depends on a specific CSS and that the CSS has to be loaded for the style to be applied. Since styles are global, any style from any file can be applied to any element, and losing control is very easy.

Dead code

Since CSS code bases tend to become huge quickly, we lose control over them, and the problem is to do with dead code elimination. It is not easy to identify quickly which styles belong to which component, and this makes deleting code incredibly hard. In fact, due to the cascading nature of CSS, removing a selector or a rule can result in an unintended result within the browser.

Dead code is any code that’s never executed. Every developer ends up uncomfortable removing CSS. Because they’re unsure which parts of the site are affected by them. If I remove this, would it break the site? There are tools, which remove unused CSS, like PurgeCSS.

Minification

Another pain of working with CSS concerns the minification of the selectors and the class names, both in the CSS and in the JavaScript application. It might seem an easy task but it is not, especially when classes are applied on the fly or concatenated in the client; this is the fourth problem.

Not being able to minify and optimize class names is pretty bad for performance, and it can make a huge difference to the size of the CSS.

Sharing constants

Another pretty common operation that is non-trivial with regular CSS is sharing constants between the styles and the client application. We often need to know the height of a header, for example, to recalculate the position of other elements that depend on it.

Usually, we read the value in the client using the JavaScript APIs, but the optimal solution would be to share constants and avoid doing expensive calculations at runtime. This represents the fifth problem that vjeux and the other developers at Facebook tried to solve.

Non-deterministic resolution

In fact, in CSS, the order matters, and if the CSS is loaded on demand, the order is not guaranteed, which leads to the wrong styles being applied to the elements.

Suppose, for example, that we want to optimize the way we request CSS, loading the CSS related to a particular page only when the users navigate to it. If the CSS related to this last page has some rules that also apply to the elements of different pages, the fact that it has been loaded last could affect the styling of the rest of the app. For example, if the user goes back to the previous page, they might see a page with a UI that is slightly different than the first time they visited it.

It is incredibly hard to control all the various combinations of styles, rules, and navigation paths, but again, being able to load the CSS when needed could have a critical impact on the performance of a web application.

We’ll try to split our code into small chunks to not burden the client to download a big CSS file. But it’s introducing a new issue for us as developers that we need to deal with.

Isolation

Last but not least, is related to isolation. In CSS, it is almost impossible to achieve proper isolation between files or components. Selectors are global, and they can easily be overwritten. It is tricky to predict the final style of an element just by knowing the class names applied to it because styles are not isolated and other rules in other parts of the application can affect unrelated elements. This can be solved by using inline styles.

Pre & post processors

Pre & post processors allow us to use features that vanila CSS don’t have, like: mixins, nesting, variables and etc.

Wait… you can say that in CSS we have variables, it’s not true, in vanilla CSS we have custom properties, we can’t use them as variables. For example we can use Sass variable for selectors.

More:

CSS Modules

A CSS Module file looks like regular CSS file, but we can scope to an individual component. So we don’t need to worry about name collisions.

CSS modules let you import your .css file into a JavaScript Object with the CSS definitions as properties. It also lets you use the compose property to extend and modularize style definitions.

Example

That how will look our styles in name.module.css file.

and we will use it in our component like this:

import css from './name.module.css'

during the build of our application this construction will be replaced to:

Unique hash at the end allow us to make unique string that will never be the same with another it helps us to avoid any class names conflicts.

We can combine Sass and CSS Modules together to make our life even easier.

More:

Utility classes

A utility class is simply a class that does one thing. For example a class mb-5, could mean add a bottom margin of 5px. You’ll have many classes but they all do one thing.

By simply looking at the HTML you’ll instantly get an idea of what each class purpose is.

One thing to keep in mind is that utility-first CSS doesn’t need any library, it is simply a methodology that can be applied to CSS, similar to how BEM works.

Pros:

  • Now it’s much easier to understand and add any changes or add something new.
  • You won’t need to actually write any new CSS.

Cons:

  • Developer Experience
We can’t even see this whole code line

TailwindCSS

TailwindCSS is a “utility first” CSS framework. It takes a totally different approach from other CSS frameworks — like Bootstrap or Zurb Foundation.

Tailwind CSS is incredibly performance focused and aims to produce the smallest CSS file possible by only generating the CSS you are actually using in your project.

Tailwind doesn’t have any ready made components. You’re expected to design the site, every section and every component, heading, button… You can have 2 websites built with Tailwind, without them having anything in common.

It helps you, in writing more maintainable CSS.

Like Tailwind, there are other CSS frameworks, which took this approach. Some are Basscss, Beard and turretcss.

More:

CSS Frameworks

The main different between Utility styles is that they provides pre-build components.

Bootstrap

The problem with CSS Frameworks like Bootstrap is that they looks like sites build with Bootstrap :)

Bootstrap dictates how everything should look. And it’s hated by designers, for good reason. We cannot have all website looking the same. It’s not good for business. And not good for the web. For an administrative dashboard or an internal tool, this might not be an issue. But if you’re making a consumer-facing application, you probably want it to identify more with a specific company brand.

And one of the big cons for using Bootstrap is result of a large bundle size, because there are a lot of unused classes that will get included in the final CSS.

Component Libraries

A component library is a set of re-usable components. There are no official rules for what a component library means.

Most popular component libraries are Material UI, Chakra UI, Manine and etc.

Component libraries suffer from the same issues as CSS frameworks when it comes to creating a “unique” look and feel.

But if you work in product company like Netflix, Google, AirBnb and even smaller and you need the same look design for each sub projects you will definitely come up to component library solution. It can be your own or any public but customizable component library. And it will give one main benefit that really cost it — speed up development process. Work with designers, business analicess, developers, QAs.

More:

CSS-in-JS

The main reason for this solution to allow you write CSS in your JavaScript code. And we can do programmatic things and it makes easy to create dynamic styles.

But for you CSS-in-JS maybe not a good approach because all styles lockup inside the JavaScript runtime, we shifts all process on client and it means we overload client. So if we want to make our app faster it’s not a right way.

So the fundamental problem with the CSS-in-JS approach is that it’s not CSS.

Luckily for us we also have another “solutions”, for example: linaria or compiled.

More:

Conclusion

As I said before: We live in world of “solutions” but we don’t have universal solution for each case and project.

The choice of a specific solution is behind what tasks you need to solve, but now you know at least several possible ways.

Sources and more:

Sign up to discover human stories that deepen your understanding of the world.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

No responses yet

Write a response