Features I look forward to in future CSS

attr(), :has(), and a “timeline” for the future

When writing a web page, I try to avoid JavaScript. Relying on a general-purpose programming language for layout doesn't feel right, not to mention that JavaScript can be blocked or unsupported. In a perfect world, HTML describes the content,[1] and CSS takes care of delivery.

CSS has come a long way, and websites like css-tricks.com are full of brilliant examples. With the coming of variables and calc(), and the going of Internet Explorer, JavaScript and CSS preprocessors are slowly becoming unnecessary for my simple use cases.[2] With that in mind, here are two likely future CSS features that I am looking forward to.

The :has() pseudo-class

Truth be told, the :has() pseudo-class (allowing to select a parent by its children) is probably more of a utility feature to me. I would use it in places where I would now use classes, allowing me to focus more on content when writing HTML. But Jen Simmons shows some really cool use-cases, some of which are currently impossible to implement purely in CSS. WebKit and Blink already support it. Hopefully, Mozilla will follow suit soon.

The attr() function

Yes, the attr() function, for referencing the value of an HTML attribute, is not that new. In fact, it is widely supported — for use in the content property. But I would like to use it in places like this:

<div class=timeline data-from=2000 data-to=3000>
	<section data-year=2000>Something happened.</section>
	<section data-year=2200>Then this.</section>
	<section data-year=2300>Then that.</section>
	<section data-year=2400>And such.</section>
	<section data-year=2800>And then, after a long time.</section>
	<section data-year=3000>Finally.</section>
</div>
<style>
.timeline {
	--from: attr(data-from);
	--to: attr(data-to);
	--span: calc(var(--to) - var(--from));
}
.timeline section {
	--year: attr(data-year);
	--reltime: calc(calc(var(--year) - var(--from)) / var(--span));
	background-color: rgb(255 140 0 / var(--reltime));
	padding: .5em;
}
.timeline section::before {
	content: var(--year);
	margin-right: 1em;
}

A timeline, where the further we go towards the end, the darker the orange background gets, giving a bit of sunset vibes.

This does not work in any current browser. For a good reason: attr(data-from) will return 2000 as a string, as will all attr(attribute) calls, and therefore the calc()s will fail. Luckily, the draft specification allows us to specify the desired type (as well as a fallback value).

The proper code (according to the current draft) becomes:

.timeline {
	--from: attr(data-from number); /* specify number type */
	--to: attr(data-to number);
	--span: calc(var(--to) - var(--from));
}
.timeline section {
	--year: attr(data-year number);
	--reltime: calc(calc(var(--year) - var(--from)) / var(--span));
	background-color: rgb(255 140 0 / var(--reltime));
	padding: .5em;
}
.timeline section::before {
	content: attr(data-year); /* re-attr() since we need a string */
	margin-right: 1em;
}

Sadly, at the time of writing, no browser supports it yet. For use cases like this, we are either bound to a preprocessor (separate class for each timeline, seperate class for each timeline section) or we must fall back to JavaScript.

To show what the above should look like, we can use a tiny bit of JavaScript:

const timelines = document.querySelectorAll(".timeline");
const sections = document.querySelectorAll(".timeline section");
const convert = attr => el =>
	el.style.setProperty(`--${attr}`, parseFloat(el.dataset[attr]));
timelines.forEach(convert("to"));
timelines.forEach(convert("from"));
sections.forEach(convert("year"));

If you are from the future, and all is well, you should see the effect below. If not, press to let JavaScript do the attr()s.

Something happened.
Then this.
Then that.
And such.
And then, after a long time.
Finally.

Neat, right?

Conclusion

The web is close to the ideal situation where HTML can be used just for content, and all styling can be handled in CSS. JavaScript will still be invaluable for interactive content, as it should be. But for simple websites, the added complexity and performance overhead will become unnecessary — W3C/WHATWG volente.


  1. Preferably, without any stylistic class attributes. ↩︎

  2. For this site, I only use PostCSS for inlining some @imports and for nesting, which will hopefully become part of CSS sooner or later. ↩︎