Applying OOP concepts to CSS
Tags: css, html, oop, tailwindcss
Published: 2020-10-16 04:30:17

## Series introduction

Recently, [Tailwind](https://tailwindcss.com/) has seen a [large explosion in growth](https://npm-stat.com/charts.html?package=tailwindcss): going from 390,000 npm downloads in Oct 2019, up to 1.8 million downloads just last month. This unprecedented popularity comes with it the [*Java phenomenon*](https://www.quora.com/Why-is-Java-so-polarizing-Everyone-seems-to-either-love-it-or-hate-it): deeply polarising views within the community where there are [those who love it](https://dev.to/swyx/why-tailwind-css-2o8f), and [those who simply hate it](https://johanronsse.be/2020/07/08/why-youll-probably-regret-using-tailwind/) (not to forget [those that can't decide](https://areknawo.com/my-mixed-feelings-about-tailwind-css/) as well).

However, this series is **not** going to be about whether you should or should not be using Tailwind. Rather, this first article will attempt to be an introduction to some [OOP concepts](https://www.geeksforgeeks.org/object-oriented-programming-oops-concept-in-java/), and how they can be applied to writing cleaner CSS. The next article will then dive deeper into the [SOLID principles](https://en.wikipedia.org/wiki/SOLID), and how Tailwind relates to all of these. Then, perhaps we can even rationalise why there is such a [large divide in the community](https://dev.to/nickytonline/what-are-your-thoughts-on-tailwind-css-573m) regarding the use of Tailwind.

## Object-oriented programming in CSS

Let's start applying object-oriented programming (OOP) principles to CSS. Firstly, a disclaimer: the idea of object-oriented CSS is not new, and has been around for quite some time. However, there is a lack of articles clearly linking OOP *concepts* to CSS. If the explanation given here confuses you, you may want to take a peak at the extensive [OOCSS wiki](https://github.com/stubbornella/oocss/wiki). Note, however, that some definitions used here may differ.

Also, there are quite a few concepts related to OOP - and only a couple which can best be applied to CSS. As such, this article will only touch on the following concepts: **encapsulation**, **abstraction**, and **composition**.

### Objects in CSS

Firstly, let's define what an "object" is in CSS. In OOP languages like Java, it makes sense to have objects like `Car` or `Elephant` which clearly have well defined states and behaviours. What then, represents an object in CSS? For simplicity's sake, let's define an "object" in CSS to be synonymous with [selectors](https://www.w3schools.com/cssref/css_selectors.asp).

For example, here is a class selector and an id selector, both with defined styles within them, and which we can refer to as two different objects, `.object-1` and `#object-2`:

```css
.object-1 {
  /* styles here */
  /* ... */
}

#object-2 {
  /* styles here */
  /* ... */
}
```

Note that **this definition is not official**, and is nothing more than syntactic sugar made to drive the concept closer to OOP. With that, let's start!

### Encapsulation in CSS

**Encapsulation is the process of grouping logically related data and operations together in an entity**. Basically, things which make sense to be together, ought to be together. Every block of style you write in a CSS selector (or "object" as we have defined) can essentially be taught of as a single encapsulation. Note that there are good ways to encapsulate code and bad ways to encapsulate code.

As an example, let's try building a simple HTML button. This button should have a 2-pixel border that is cyan in colour, white text, and a teal background (yes, gruesome design, I know). In the CSS of the following snippet, we have a `#teal-button` object, which is an encapsulation of the styles needed to style the button:

```html
<!-- HTML -->
<button id="teal-button">Press Me!</button>

<!-- CSS -->
<style>
#teal-button {
  border: solid 2px cyan;
  color: white;
  background: teal;
}
</style>
```

Now, what happens if we want to have a similar button, except with a red background this time? Well, if we do not want to refactor our previous code, here's an easy way:

```html
<button id="teal-button">Press Me!</button>
<button id="red-button">Press Me!</button>

<style>
#teal-button {
  border: solid 2px cyan;
  color: white;
  background: teal;
}

#red-button {
  border: solid 2px cyan;
  color: white;
  background: red;
}
</style>
```

Notice that we have redundantly duplicated portions of the CSS. Is there a need to have the same styles for `border` and `color` in two separate places? This is a classic example of the violation of the aptly named [*Don't Repeat Yourself* (DRY) principle](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself). In OOP, we would have said that the `#teal-button` object and the `#red-button` object have similar states which could be better **aggregated or encapsulated into a common, shared object**:

```html
<button id="teal-button" class="base-btn teal-bg">Press Me!</button>
<button id="red-button" class="base-btn red-bg">Press Me!</button>

<style>
/* contains styles common to both buttons */
.base-btn {
  border: solid 2px cyan;
  color: white;
}

.teal-bg {
  background: teal;
}

.red-bg {
  background: red;
}
</style>
```

As your app grows and as you add more buttons of different background colours, using the first approach means that your CSS will grow extremely fast. Refactoring will also be an absolute nightmare. For example, in the second approach, a single line of CSS has to be modified to make all buttons change their borders to green. How many lines of CSS do you need to modify in the first approach for the same effect?

It is easy to reason to yourself how the second approach promotes code reusability, while reducing unnecessary code duplication. Writing less CSS comes with it two clear advantages:

- Smaller bundle size for your built website
- Much better developer experience (no more waddling over pages of id selectors, or changing multiple pages of CSS just to have a consistent design)

### Abstractions in CSS

**Abstraction is the process of hiding the internal working of a process from its end user**. Do not confuse this with encapsulation, which is the act of grouping things together in an entity. Abstractions *rely* on encapsulations: we group things together in an entity via encapsulation, and then expose that entity as an abstraction for the end user to use. This way, there is no need for the user to know *how* the entity is implemented, only *what* it does.

In CSS, *the end user is actually yourself and your fellow developers*. Using the same example above, we have actually practised sound abstractions by defining the `teal-bg` and `red-bg` classes. There is no need to look at the actual implementation of the CSS classes to know that the `teal-bg` class is obviously applying the teal colour to the background.

Similar to encapsulation, there are good and bad ways to abstract things. In the previous example, the `base-btn` class does not immediately give us a visual understanding of how the button is implemented. Does this mean that the `base-btn` class is bad? Well... It depends, and we shall leave this to the next article. What we do know is that the act of **using semantic names for classes** is one of the ways abstractions can be effectively used in CSS.

Ok, you might be asking, *but what is the point*? Let's have another example, where we want to make a button, and a paragraph, both with blue texts. If you have been paying attention to the previous section, we now know that we should avoid repeating the same code via encapsulation. As such, we can come up with this:

```html
<!-- HTML -->
<button class="blue-text-button">Press Me!</button>
<p class="blue-text-button">Here is some text...</p>

<!-- CSS -->
<style>
.blue-text-button {
  color: blue;
}
</style>
```

Yes, this fits the design specifications perfectly fine, and we have also managed to not repeat ourselves. But the issue with this is that the HTML/CSS has lost their semantic meaning. Without looking at how the CSS is implemented, do you know what exactly the `blue-text-button` class does? Should the `button` word be part of `blue-text-button` if it does not even style a single part of the button? More importantly, why does the `p` tag, which clearly isn't a button, depend on the CSS class whose name has `button` in it?

In order to let your fellow developer (or yourself in the future) understand immediately what you are trying to achieve, a better approach will be to rename the `blue-text-button` class to `blue-text`. Now, with sound abstractions, it is immediately obvious what this snippet is trying to achieve, **without even looking at the implementation of the CSS**:

```html
<button class="blue-text">Press Me!</button>
<p class="blue-text">Here is some text...</p>
```

Of course, one could argue that the above CSS implementation is trivial, and does not detract one from immediately understanding what `blue-text-button` does by looking at the CSS implementation. What about this case where your website has a custom colour palette:

```html
<button class="custom-colour-1">Press Me!</button>
<p class="custom-colour-2">Here is some text...</p>

<style>
.custom-colour-text-1 {
  color: #51c6e0;
}

.custom-colour-text-2 {
  color: #5be051;
}
</style>
```

Even looking at the CSS implementation wouldn't save you (unless of course, you memorised the hex colours...). Assuming the colours are part of your company/brand, and which probably will not change for quite some time, a better approach would be to name the classes like so:

```css
.brand-blue-text {
  color: #51c6e0;
}

.brand-green-text {
  color: #5be051;
}
```

Now, you can safely use the class `brand-blue-text` or `brand-green-text` anywhere in your HTML, where you and your fellow developers will immediately know that *what* you are trying to achieve.

### Compositions in CSS

**Composition is the act of modelling a *has-a* relationship between objects**. For example, *a car has an engine*, and *a human being has a heart*. Again, if you are sharp enough, the use of composition have already been demonstrated in very our first example. Instead of putting all the styles into `#teal-button`, we have instead defined two different classes, `.base-btn` and `.teal-bg`, from which we then *compose* together to make the original style of `#teal-button`.

The whole idea of composition is to reuse existing code, reduce unnecessary code duplication, and to design cleaner CSS (basically the whole point of this series if you haven't got the memo yet). Let's reuse our first example again, except with one more green coloured button this time:

```html
<button id="teal-button" class="base-btn teal-bg">Press Me!</button>
<button id="red-button" class="base-btn red-bg">Press Me!</button>
<button id="green-button" class="base-btn green-bg">Press Me!</button>

<style>
.base-btn {
  border: solid 2px cyan;
  color: white;
}

.teal-bg {
  background: teal;
}

.red-bg {
  background: red;
}

.green-bg {
  background: green;
}
</style>
```

Now, say for instance we want to add a 8px padding around the teal and red buttons only (ie. the green button should stay as is), what would be the best way to do this?

As our first try, we could define this padding within `.teal-bg` and `red-bg`:

```css
.teal-bg {
  background: teal;
  padding: 8px;
}

.red-bg {
  background: red;
  padding: 8px;
}
```

However, this violates what we've learnt in *abstractions*: it's utterly confusing for the class `teal-bg` to also apply padding when the name of the class clearly has no mention of padding in it. To remedy this, we could rename the classes, which would then require us to refactor all the instances in which the original `teal-bg`/`red-bg` has been referenced - not ideal.

For our second try, we can instead put the padding style in `base-btn`, which would require an `!important` in `.green-bg` to override the padding (remember that the green button also takes `.base-btn` as a class):

```css
.base-btn {
  border: solid 2px cyan;
  color: white;
  padding: 8px;
}

.teal-bg {
  background: teal;
}

.red-bg {
  background: red;
}

.green-bg {
  background: green;
  padding: 0px !important;
}
```

Now, however, we've made our CSS really fragile and dangerous. For a component with a defined padding, applying `green-bg` to it will lead to that component losing its defined padding - not ideal.

The better approach is to define a class just for the padding, which we can then *compose* with the teal and red buttons like so:

```html
<button id="teal-button" class="base-btn teal-bg padding-8px">Press Me!</button>
<button id="red-button" class="base-btn red-bg padding-8px">Press Me!</button>
<button id="green-button" class="base-btn green-bg">Press Me!</button>

<style>
.base-btn {
  border: solid 2px cyan;
  color: white;
}

.teal-bg {
  background: teal;
}

.red-bg {
  background: red;
}

.green-bg {
  background: green;
}

.padding-8px {
  padding: 8px;
}
</style>
```

Now, this looks much better! We have encapsulated the common padding style into a single class, and abstracted it such that the class name `padding-8px` makes sense. By not modifying the other existing classes, we have further reduced the risk of breaking anything which relies on them. Notice also, that by reading the HTML only (without reading the CSS), we can still visualise roughly how the end product looks like.

## Conclusion

That's it, now you're on you way to writing cleaner CSS! We have only just scratched the surface here regarding OOP and object-oriented CSS. In the next article, we shall dive more deeply into exactly why some of the negative examples shown here are *bad*, and actually start looking at Tailwind and the SOLID principles.