Debug panel

Close debug panel
Roma’s Unpolished Posts

Responsive tab-size

Published on:
Categories:
Obscure CSS 5, CSS 89
Current music:
This is The Kit
Birchwood Beaker
Current drink:
Thyme, rosemary & lemon infusion

The tab-size

There exists a not well-known CSS property: tab-size. It sets the width, in spaces, of the tab indentation, usually found in code blocks of authors who use tabs instead of spaces (good job, authors).

One of the benefits of using tabs for indentation: it is possible to control how big the indentation will be. CSS is not exception, and that’s why tab-size property is very useful. Especially, given its default value is the enormous 8 spaces. It is very likely you’d want to override it for your code blocks:

	One default tab here!
	One overridden tab here!
<pre>
	One default tab here!
</pre>
<pre style="tab-size: 4">
	One overridden tab here!
</pre>

Responsiveness

However, who says that this value should be static?

	One tab here!
	One tab here!
	One tab here!
<div class="container">
	<pre class="dynamic-tab">	One tab here!</pre>
</div>
<div class="container" style="width: 66%">
	<pre class="dynamic-tab">	One tab here!</pre>
</div>
<div class="container" style="width: 33%">
	<pre class="dynamic-tab">	One tab here!</pre>
</div>
.container {
	container-type: inline-size;
}
.dynamic-tab {
	--available-width: 100cqi;
	--step-size: 10ch;
	@supports not (z-index: calc(1px/1px)) {
		--captured-length: var(--available-width);
		--captured-length2: var(--step-size);
		--ratio: tan(atan2(
			var(--captured-length),
			var(--captured-length2)
		));
		tab-size: round(up, var(--ratio), 2);
	}
	@supports (z-index: calc(1px/1px)) {
		tab-size: round(
			up,
			var(--available-width) / var(--step-size),
			2
		);
	}
}
Example showing tab-size being dynamic based on its container width.

Ok, that CSS looks scary! If all browsers supported typed arithmetic — an ability to divide two values of the same type to get the unitless ratio between them — we could simplify it to just this:

.dynamic-tab {
	tab-size: round(up, 100cqi / 10ch, 2);
}

Now, that’s much more readable, right? We divide the available space by some length to get the number of times it fits in it, and then round it up to be even.

This makes the tab-size start from being two spaces wide, and increase with the available space. If needed, a min() could be added if we’d wanted to limit how high it could grow.

But — typed arithmetic is not currently available in all browsers. That said, we can work around this.

I am using the tan(atan2()) hack by Jane Ori alongside Captured Custom Properties to capture some values that compute to px and then get the ratio between them via tan(atan2()).

I am also using @supports for the browsers to see only what they need to based on the typed arithmetic support. If we were not abstracting the values into custom properties, we could rely on fallbacks, but when custom properties are present we can’t use them.

That’s It

I first experimented with this almost exactly 2 years ago, and earlier this year shared an updated version of this technique on Mastodon.

This time, I finally wrote a proper blog post about it, and at the same time slightly updated it to use the typed arithmetic alongside the @supports rule.

I also updated my blog’s CSS to use this, although here it only switches between values of 2 and 4 spaces, and maybe a bit overcomplicated due to some of my blog’s implementation details — I even used the Named Container Presence Check that I described in the previous post.

It is fun finding places where the typed arithmetic could help with responsiveness: any place you see an integer or a unitless number could be an opportunity to try it. How can we connect our container’s size with it? Maybe use for color components? z-index? Number of repeats in grid’s repeat()? Something else?

Go on, and experiment. We need more experiments!

Please share your thoughts about this on Mastodon!