CSS –var: inherit(–var) + 2; Yes, you can! Without JS!

inherit(–var) or parent(–var) is a long desired CSS feature which has been discussed by the CSS Working Group and is tracked with an open issue in the csswg-drafts repo. Their discussion seems to be leaning towards only allowing direct > descendan…

inherit(--var) or parent(--var) is a long desired CSS feature which has been discussed by the CSS Working Group and is tracked with an open issue in the csswg-drafts repo. Their discussion seems to be leaning towards only allowing direct > descendants to pull value from the direct parent, which is still very useful.

What I’ll be demonstrating today works beyond direct parents though, with one small caveat: the source and recipient elements will need a common selector (both div, or both .whatever, for example) to facilitate the handoff. So no backwards compatible transpiling for where their discussion stands now…

The easy way – with mixins

Hit the ground running with a reusable Less (or Sass) mixin to generate the CSS for us.

Note: No :has() or container queries here, this works for 89% of global users at time of writing. Our limiting factors are the :where() selector and Level 4 :not() selector. 🎉

At the root .hue element, --hue evaluates to 0deg because it uses the fallback in inherit(--hue, 0deg). From there, every .hue inherits the previous value and adds 30deg to it.

How it works – The Selectors

The core of this idea is that .sel:not(.sel .sel) will only select .sel that isn’t nested in another .sel element.

Then .sel .sel:not(.sel .sel .sel) will only select a .sel that is a descendant of exactly one ancestor .sel but no more.

Do that up to a maximum depth…
then split them into two groups to set up the next part of this trick:
odd and even.

Finally, wrap them in a :where() to drop the specificity of the selector to 0 so anything else we write in our CSS will override it without specificity wars. (Most selectors in most CSS libraries will be wrapped in :where() in the future so you never have to worry about developer users fighting your selectors.)

In our compiled codepen, the .hue selector, up to a maximum nesting of 12, winds up looking like this:

:where(
.hue:not(.hue .hue),
.hue .hue .hue:not(.hue .hue .hue .hue),
.hue .hue .hue .hue .hue:not(.hue .hue .hue .hue .hue .hue),
.hue .hue .hue .hue .hue .hue .hue:not(.hue .hue .hue .hue .hue .hue .hue .hue),
.hue .hue .hue .hue .hue .hue .hue .hue .hue:not(.hue .hue .hue .hue .hue .hue .hue .hue .hue .hue),
.hue .hue .hue .hue .hue .hue .hue .hue .hue .hue .hue:not(.hue .hue .hue .hue .hue .hue .hue .hue .hue .hue .hue .hue)
) {
  ... // odd-nesting code
}
:where(
.hue .hue:not(.hue .hue .hue),
.hue .hue .hue .hue:not(.hue .hue .hue .hue .hue),
.hue .hue .hue .hue .hue .hue:not(.hue .hue .hue .hue .hue .hue .hue),
.hue .hue .hue .hue .hue .hue .hue .hue:not(.hue .hue .hue .hue .hue .hue .hue .hue .hue),
.hue .hue .hue .hue .hue .hue .hue .hue .hue .hue:not(.hue .hue .hue .hue .hue .hue .hue .hue .hue .hue .hue),
.hue .hue .hue .hue .hue .hue .hue .hue .hue .hue .hue .hue:not(.hue .hue .hue .hue .hue .hue .hue .hue .hue .hue .hue .hue .hue)
) {
  ... // even-nesting code
}

How it works – The Odd / Even var() Chain

What we wrote originally is

--hue: calc(inherit(--hue, 0deg) + 30deg)

Then, inside the odd-nesting selector, the vanilla CSS becomes:

--hue: calc(var(--io1_hue, 0deg) + 30deg);
--io2_hue: var(--hue);

What this is doing is looking for an --io1_hue var that only exists on even-nesting (but is automatically inherited because it’s a CSS var). So at the root .hue, if we haven’t overwritten --hue, it uses the 0deg fallback.

Then, it creates an odd-nesting export of --hue by creating and setting the --io2_hue var to --hue.

And, inside the even-nesting selector, the vanilla CSS becomes:

--hue: calc(var(--io2_hue, 0deg) + 30deg);
--io1_hue: var(--hue);

Which does the exact same thing, except now inherit(--hue became var(--io2_hue which is looking for the export from our odd-nesting depth.

Finally, the chain is complete when we set the --io1_hue export for the next odd-nesting to consume.

\
/
\
/
\
/
\
🙂

That’s it! Have fun!

And we don’t have to think about any of the details because the inherit() abstraction just makes sense and it’s all we have to write now! <3

If you enjoy this or make any useful or fun demos, please do tweet @ me!

Linkage!

Docs: https://propjockey.github.io/css-inherit-fn/
Live Demo Playground: https://propjockey.github.io/css-inherit-fn/#live-examples
Package: https://www.npmjs.com/package/css-inherit-fn
Repo: https://github.com/propjockey/css-inherit-fn
Twitter: https://twitter.com/Jane0ri


Print Share Comment Cite Upload Translate
APA
Jane Ori | Sciencx (2024-03-29T07:10:25+00:00) » CSS –var: inherit(–var) + 2; Yes, you can! Without JS!. Retrieved from https://www.scien.cx/2022/07/04/css-var-inherit-var-2-yes-you-can-without-js/.
MLA
" » CSS –var: inherit(–var) + 2; Yes, you can! Without JS!." Jane Ori | Sciencx - Monday July 4, 2022, https://www.scien.cx/2022/07/04/css-var-inherit-var-2-yes-you-can-without-js/
HARVARD
Jane Ori | Sciencx Monday July 4, 2022 » CSS –var: inherit(–var) + 2; Yes, you can! Without JS!., viewed 2024-03-29T07:10:25+00:00,<https://www.scien.cx/2022/07/04/css-var-inherit-var-2-yes-you-can-without-js/>
VANCOUVER
Jane Ori | Sciencx - » CSS –var: inherit(–var) + 2; Yes, you can! Without JS!. [Internet]. [Accessed 2024-03-29T07:10:25+00:00]. Available from: https://www.scien.cx/2022/07/04/css-var-inherit-var-2-yes-you-can-without-js/
CHICAGO
" » CSS –var: inherit(–var) + 2; Yes, you can! Without JS!." Jane Ori | Sciencx - Accessed 2024-03-29T07:10:25+00:00. https://www.scien.cx/2022/07/04/css-var-inherit-var-2-yes-you-can-without-js/
IEEE
" » CSS –var: inherit(–var) + 2; Yes, you can! Without JS!." Jane Ori | Sciencx [Online]. Available: https://www.scien.cx/2022/07/04/css-var-inherit-var-2-yes-you-can-without-js/. [Accessed: 2024-03-29T07:10:25+00:00]
rf:citation
» CSS –var: inherit(–var) + 2; Yes, you can! Without JS! | Jane Ori | Sciencx | https://www.scien.cx/2022/07/04/css-var-inherit-var-2-yes-you-can-without-js/ | 2024-03-29T07:10:25+00:00
https://github.com/addpipe/simple-recorderjs-demo