A workaround for using custom properties in media queries
I’m a big fan of custom properties and I use them almost everywhere. One of the places where I’d love to use them, but can’t, is with media queries. The following code won’t work in any browser.
- Code language
- css
:root { --breakpoint-s: 480px; } @media (min-width: var(--breakpoint-s)) { /* do something */ }
The Custom Properties specification explains:
The var() function can be used in place of any part of a value in any property on an element. The var() function can not be used as property names, selectors, or anything else besides property values. (Doing so usually produces invalid syntax, or else a value whose meaning has no connection to the variable.)
That’s unfortunate, but I recently found a workaround that I’d love to share with you. Instead of using a media query, you can use a container style query.
- Code language
- css
@property --inline-size-s { syntax: "<length-percentage>"; inherits: true; initial-value: 100vi; } :root { --breakpoint-s: 48em; --inline-size-s: min(var(--breakpoint-s), 100vi); } body { background-color: var(--bg-color); --bg-color: oklch(0.94 0.01 99); @container style(--inline-size-s: var(--breakpoint-s)) { --bg-color: oklch(0.87 0.21 95.82); } }
Let me guide you through the steps I took to come up with this solution.
Breakdownpermalink
The first step is to define a custom property for the breakpoint. Then, query the viewport’s width and check whether it matches or exceeds the breakpoint. As already teased with the title of this article, you have to use container style queries for that, rather than media queries. Container style queries enable you to check whether a container has a specific property and computed value assigned.
- Code language
- css
:root { --breakpoint-s: 48em; --inline-size-s: 100vw; } @container style(--inline-size-s: var(--breakpoint-s)) { body { --bg-color: oklch(0.87 0.21 95.82); } }
That looks good but it doesn’t work for two reasons.
You’re comparing two strings: does “48em” match “100vi”? That, of course, will never be true. What you actually want to do is compare the computed values of two lengths. Using the @property
at-rule you can define an explicit type for your custom properties.
From the specification for the min-width
property, we know that its value can be a <length-percentage>
, so you can use that for the syntax
property, which defines the types. You also have to provide an initial value; 100vi
is a good default for our wannabe media queries.
The property should also be inheritable, so you set inherits
accordingly.
- Code language
- css
@property --inline-size-s { syntax: "<length-percentage>"; inherits: true; initial-value: 100vw; }
Your query now works, but only if the viewport width is exactly 48em (roughly 768px, depending on the user’s preferred root font size).
You want it to work at at least 48em, though. The problem there is that if the computed value of --inline-size-s
exceeds 48em it can’t match your breakpoint --breakpoint-s
. That’s why you want --inline-size-s
to match the viewport width but only until it reaches 48em.
You can use the min()
function for that. It takes the smaller of the provided values.
- Code language
- css
:root { --breakpoint-s: 48em; --inline-size-s: min(var(--breakpoint-s), 100vi); }
--inline-size-s
matches 100vi as long as the computed value of 100vi is lower than 48em. Otherwise, it matches 48em.
Here’s the whole snippet.
- Code language
- css
@property --inline-size-s { syntax: "<length-percentage>"; inherits: true; initial-value: 100vi; } :root { --breakpoint-s: 48em; --inline-size-s: min(var(--breakpoint-s), 100vi); } body { background-color: var(--bg-color); --bg-color: oklch(0.94 0.01 99); @container style(--inline-size-s: var(--breakpoint-s)) { --bg-color: oklch(0.87 0.21 95.82); } }
Tip
Toggle the CSS panel to see the effect
See the Pen A workaround for using custom properties in media queries (Demo 1) by piccalilli (@piccalilli) on CodePen.
Performancepermalink
You may wonder if there are any performance issues. I haven’t tested this solution on a large scale, but I can’t imagine so. Unlike container size queries, which query the size of the query container’s principal box, container style queries only query computed values of its container. That is much safer and harmless in terms of performance, which is also one of the reasons why every element is a style container by default. In the following demo, you can even change the breakpoint on the fly using a range slider.
See the Pen A workaround for using custom properties in media queries (Demo 2) by piccalilli (@piccalilli) on CodePen.

Downsidespermalink
That’s a great solution to a problem probably many of us had, but there are also some downsides.
Browser Support
Container style queries are currently not supported by Firefox, at the time of writing, but I’ve heard from trustworthy sources that they’re being prioritised. Unfortunately, there isn’t a great way to progressively enhance this solution because there’s no feature detection for at-rules in CSS. If you really want to make it work, read this post by Bramus.
Verbosity
That isn’t really an issue, but it’s annoying that for every breakpoint, you need two custom properties, and you have to register one of them.
- Code language
- css
@property --inline-size-s { syntax: "<length-percentage>"; inherits: true; initial-value: 100vi; } @property --inline-size-m { syntax: "<length-percentage>"; inherits: true; initial-value: 100vi; } :root { --breakpoint-s: 48em; --inline-size-s: min(var(--breakpoint-s), 100vi); --breakpoint-m: 64em; --inline-size-m: min(var(--breakpoint-m), 100vi); }
That will change when browsers start supporting style ranges.
- Code language
- css
@container style(100vi <= --inline-size-s) { /* The rest of your CSS */ }
Container queries
With this solution, you can only query the viewport, not a container. If you write min(var(--breakpoint-s), 100cqi)
, the 100cqi doesn’t refer to the style container but to a parent container. So, the only way to make it work would be to wrap the style container in another inline-size container.
See the Pen A workaround for using custom properties in media queries (Demo 3) by piccalilli (@piccalilli) on CodePen.
Wrapping uppermalink
I love this solution. Not just because it solves a problem I recently had, but also because it showcases the power of modern CSS features like @container
, @property
or min()
really well.
Browser support may be a topic for you right now, but hopefully, it will soon be a thing of the past.
Enjoyed this article? You can support us by leaving a tip via Open Collective
