Daniel Casanueva's Blog

Algorithmic CSS

I'm not big into frontend stuff, but lately I have been learning some CSS reading "Every Layout" by Heydon Pickering and Andy Bell. And as a consequence I ended up creating this new blog, just to try the concepts out. And while at it, I might as well polish up my English and share some of my ideas. Do not expect regularity or consistency.

This is the first time that I create a website that has no <div> element whatsoever. The experience was quite fun. I wasn't trying to avoid it. I was simply looking to use the right semantic elements. Sure, this is a very simple website: it only has a header, an index of blogposts, and a page to read each of them. But for this, using only semantic elements was enough.

The posts themselves are written in Markdown and converted to HTML using mmark . The server was written using servant-server , a library that has become one of the most commonly used tools to build web servers in Haskell.

I haven't finished reading "Every Layout" yet, but there are a couple of things that I learned that I think are worth sharing, even if many of you already know about them. I didn't.

The modular scale

A modular scale consists of taking lengths from the set \(M = \{ b r^n \mid n \in \mathbb{Z} \}\), where we call \(b > 0\) the base length and \(r > 1\) the ratio. In CSS, one would typically set \(b = 1 \text{rem}\) and use it as the font size for paragraphs. From there, one can pick different values for \(n\) in order to style other elements.

For example, if we set at the :root selector the values --base-length for \(b\) and --ratio for \(r\), we can set line height and header font sizes in the following way:

p { line-height: calc(var(--base-length) * pow(var(--ratio), 1)) }

h1 { font-size: calc(var(--base-length) * pow(var(--ratio), 3)) }
h2 { font-size: calc(var(--base-length) * pow(var(--ratio), 2)) }

And this works reasonably well. The user gets to set the base length through their browser settings, while the web developer gets to set the ratio. From these two numbers, everything else is set by the algorithm. I find this simplicity appealing.

The calc syntax gets rather long though. This can be shortened by setting some values in :root for the commonly used scales:

:root
  { --base-size: 1rem
  ; --size-1: calc(var(--base-size) * pow(var(--ratio), 1))
  ; --size-2: calc(var(--base-size) * pow(var(--ratio), 2))
  ; --size-3: calc(var(--base-size) * pow(var(--ratio), 3))
    }

And yes, I'm aware that's not the standard place for semicolons in CSS, but it's how I like it.

Also, notice that this website uses a modular scale. I guess you were already expecting that.

The owl

Another thing I learned was the owl selector. You can read more about it in detail in "Axiomatic CSS and Lobotomized Owls" . This is a CSS selector that acts not on individual elements, or elements from a class, but on any element that follows another one within the same container. It looks like this: * + *. It's from this look that it was named the "lobotomized owl". I'd rather just call it the owl.

This selector is often used to specify spacing between consecutive elements. For example:

* + * { margin-top: 2em }

In this example, you get each element to be separated from the previous one by 2em, no matter where it is. This might not be what you want for every single element, but you can always specify a different margin for specific elements below the general rule.

We can also use the modular scale to set the space between elements:

* + * { margin-top: calc(var(--base-length) * pow(var(--ratio), 2)) }

Furthermore, if you want the spacing to work regardless of the language used (different languages flow the content on different directions), you can use margin-block-start instead of margin-top:

* + * { margin-block-start: calc(var(--base-length) * pow(var(--ratio), 2)) }

Conclusion

As a math enthusiast, I find satisfying to apply mathematical concepts to CSS. I have also been trying to get more familiar with semantic HTML elements and stop using (and abusing) <div> for everything. I'm also trying to get better at accesibility, which is somehow related. For this purpose, I started reading the Mozilla documentation for web accessibility and I found it's a good place to start.