CSS native variables not working in media queries
Asked Answered
L

12

355

I am trying to use CSS variables in media query and it does not work.

:root {
  --mobile-breakpoint: 642px;
}

@media (max-width: var(--mobile-breakpoint)) {

}
Liles answered 21/11, 2016 at 14:47 Comment(2)
Just to clarify for people finding this through Google: you can use CSS custom properties inside the scope of a media query, you just cannot use them in a media query declaration.Horseplay
Imagine if you changed --mobile-breakpoint inside of the media query's :root. That would be a recursive glitch I thinkKazim
R
279

From the spec,

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.)

So no, you can't use it in a media query.

And that makes sense. Because you can set --mobile-breakpoint e.g. to :root, that is, the <html> element, and from there be inherited to other elements. But a media query is not an element, it does not inherit from <html>, so it can't work.

This is not what CSS variables are trying to accomplish. You can use a CSS preprocessor instead.

Rese answered 21/11, 2016 at 15:7 Comment(4)
The answer is correct in that the spec doesn't currently handle CSS variables in media queries, but incorrect in that is not what CSS variables are trying to accomplish. Reducing repetition and magic numbers is exactly why CSS variables were created -- see w3.org/TR/css-variables-1/#introStockyard
BTW how is this NOT a property value ?? It's max-width's property value! It should totally work!Honestly
@Honestly Actually, no. It may look like the max-width property because you write the same text, but it accomplishes something completely different. A CSS property declaration acts as a "setter", it sets a value for an element, while the max-width in a media query is a "getter": it requests the current width of something (and then compares it to the value provided). The text max-width: 1000px as a media query is (in the form of some NON-VALID pseudocode) something like viewport.getWidth() <= 1000px, while as a property declaration it would be element.setMaxWidth(1000px).Nigrescent
any updates for 2023?Trula
D
143

As Oriol has answered, CSS Variables Level 1’s var() cannot currently be used in media queries. However, there have been recent developments that will address this problem. Once CSS Environment Variables Module Level 1 is standardized and implemented, we’ll be able to use env() variables in media queries in all modern browsers.

The CSS Working Group (CSSWG) codified env() in a new standard (currently at a draft stage): the CSS Environment Variables Module Level 1 (see this GitHub comment and this comment for more info). The draft calls out variables in media queries as an explicit use case:

Because environment variables don’t depend on the value of anything drawn from a particular element, they can be used in places where there is no obvious element to draw from, such as in @media rules, where the var() function would not be valid.

If you read the specification and have a concern, or if you want to voice your support for the media-query use case, you can do so in issue #2627, in issue #3578, or in any CSS GitHub issue labeled with “css-env-1”.

GitHub issue #2627 and GitHub issue #3578 are devoted to custom environmental variables in media queries.


Original answer from 2017-11-09: Recently, the CSS Working Group decided that CSS Variables Level 2 will support user-defined environment variables using env(), and they will try to make them be valid in media queries. The Group resolved this after Apple first proposed standard user-agent properties, shortly before the official announcement of iPhone X in September 2017 (see also WebKit: “Designing Websites for iPhone X” by Timothy Horton). Other browser representatives then agreed they would be generally useful across many devices, such as television displays and ink printing with bleed edges. (env() used to be called constant(), but that has now been deprecated. You might still see articles that refer to the old name, such as this article by Peter-Paul Koch.) After some weeks passed, Cameron McCormack of Mozilla realized that these environment variables would be usable in media queries, and Tab Atkins, Jr. of Google then realized that user-defined environment variables would be especially useful as global, non-overridable root variables usable in media queries. Now, Dean “Dino” Jackson of Apple will join Atkins in editing Level 2.

You can subscribe to updates on this matter in w3c/csswg-drafts GitHub issue #1693 (for especially relevant historical details, expand the meeting logs embedded in the CSSWG Meeting Bot’s resolutions and search for “MQ”, which stands for “media queries”).

Divisive answered 9/11, 2017 at 22:51 Comment(0)
C
120

What you can do however is @media query your :root statement!

:root {
     /* desktop vars */
}
@media screen and (max-width: 479px) {
    :root {
        /* mobile vars */
    }
}

Totally works in Chrome, Firefox and Edge at least the latest production versions as of this posting.

One limitation: if you need to access the value as a variable – for example to use in calculations elsewhere – you will need to have a variable, and it requires defining the variable in two places: the media query and variable declaration.

Chinquapin answered 26/1, 2019 at 4:42 Comment(3)
I'm not entirely sure what you suggest? this means no support right?Canoewood
@Canoewood browsers do not support CSS vars in the media rule, but they do support the technique shown in this answer, which is to declare vars in media rule. I guess what this answer isn't showing is actually using a var. You could (for example) declare different values for --text-size in each :root in the answer and then later do something like h1 { font-size: var(--text-size) } and it'll use the appropriate value.Gauntlett
Oh, that is PRO. That is just brilliant. Thank you @Sean.Accroach
U
37

Apparently it's just not possible to use native CSS variables like that. It's one of the limitations.

A clever way to use it is to change your variables in the media-query, to impact all your style. I recommend this article.

:root {
  --gutter: 4px;
}

section {
  margin: var(--gutter);
}

@media (min-width: 600px) {
  :root {
    --gutter: 16px;
  }
}
Urbani answered 21/11, 2016 at 14:57 Comment(3)
I don't understand the meaning of "change your variables in the media-query", you can show example?Liles
Yes, just did, it's in the article I linked. I know it's not what you expected, but CSS variables just can't be used to define the media queriesUrbani
This is important. Imagine using the custom property's value in the @media rule - and then also wanting to change the value of that custom property - at that same break-point. it's weird... but they are custom properties - and not just variables. So, - it seems like the reason it wasn't created that way - is better explained with an example than the spec. A combo would be best.Coronagraph
W
24

The level 5 specification of media queries define Custom Media Queries that does almost what you are looking for. It allows you to define breakpoint similar to how you do with CSS variables and later use them in different places.

Example from the specification:

@custom-media --narrow-window (max-width: 30em);

@media (--narrow-window) {
  /* narrow window styles */
}
@media (--narrow-window) and (script) {
  /* special styles for when script is allowed */
}

There is still no support for this actually so we have to wait before using this feature.

Wolfie answered 13/8, 2021 at 22:25 Comment(0)
C
18

One way to achieve what you want is using npm package postcss-media-variables.

If you are fine with using npm packages then you can take a look documentation for same here:

Example

/* input */
:root {
  --min-width: 1000px;
  --smallscreen: 480px;
}
@media (min-width: var(--min-width)) {}
@media (max-width: calc(var(--min-width) - 1px)) {}

@custom-media --small-device (max-width: var(--smallscreen));
@media (--small-device) {}
Caviar answered 21/11, 2016 at 15:9 Comment(2)
et. al : With postcss you can also use cssnext cssnext.io/features/#custom-media-queriesHindermost
@sebilasse: custom-media-queries does not address the main problem of not being able to use css variables as breakpoints for media queriesToed
S
9

Short Answer

You can use JavaScript to change the value of media queries and set it to the value of a css variable.

// get value of css variable
getComputedStyle(document.documentElement).getPropertyValue('--mobile-breakpoint'); // '642px'

// search for media rule
var mediaRule = document.styleSheets[i].cssRules[j];

// update media rule
mediaRule.media.mediaText = '..'


Long Answer

I wrote a small script which you can include on your page. It replaces every media rule with a value of 1px with the value of the css variable --replace-media-1px, rules with value 2px with --replace-media-2px and so on. This works for the media queries with, min-width, max-width, height, min-height and max-height even when they are connected using and.

JavaScript:

function* visitCssRule(cssRule) {
    // visit imported stylesheet
    if (cssRule.type == cssRule.IMPORT_RULE)
        yield* visitStyleSheet(cssRule.styleSheet);

    // yield media rule
    if (cssRule.type == cssRule.MEDIA_RULE)
        yield cssRule;
}

function* visitStyleSheet(styleSheet) {
    try {
        // visit every rule in the stylesheet
        var cssRules = styleSheet.cssRules;
        for (var i = 0, cssRule; cssRule = cssRules[i]; i++)
            yield* visitCssRule(cssRule);
    } catch (ignored) {}
}

function* findAllMediaRules() {
    // visit all stylesheets
    var styleSheets = document.styleSheets;
    for (var i = 0, styleSheet; styleSheet = styleSheets[i]; i++)
        yield* visitStyleSheet(styleSheet);
}

// collect all media rules
const mediaRules = Array.from(findAllMediaRules());

// read replacement values
var style = getComputedStyle(document.documentElement);
var replacements = [];
for (var k = 1, value; value = style.getPropertyValue('--replace-media-' + k + 'px'); k++)
    replacements.push(value);

// update media rules
for (var i = 0, mediaRule; mediaRule = mediaRules[i]; i++) {
    for (var k = 0; k < replacements.length; k++) {
        var regex = RegExp('\\((width|min-width|max-width|height|min-height|max-height): ' + (k+1) + 'px\\)', 'g');
        var replacement = '($1: ' + replacements[k] + ')';
        mediaRule.media.mediaText = mediaRule.media.mediaText.replace(regex, replacement);
    }
}

CSS:

:root {
  --mobile-breakpoint: 642px;

  --replace-media-1px: var(--mobile-breakpoint);
  --replace-media-2px: ...;
}

@media (max-width: 1px) { /* replaced by 642px */
  ...
}

@media (max-width: 2px) {
  ...
}
Scudo answered 10/5, 2020 at 16:43 Comment(0)
N
3

You can build a media query programmatically using matchMedia:

const mobile_breakpoint = "642px";
const media_query = window.matchMedia(`(max-width: ${mobile_breakpoint})`);
function toggle_mobile (e) {
  if (e.matches) {
    document.body.classList.add("mobile");
  } else {
    document.body.classList.remove("mobile");
  }
}
// call the function immediately to set the initial value:
toggle_mobile(media_query);
// watch for changes to update the value:
media_query.addEventListener("change", toggle_mobile);

Then, instead of using a media query in your CSS file, apply the desired rules when body has the mobile class:

.my-div {
  /* large screen rules */
}

.mobile .my-div {
  /* mobile screen rules */
}
Nutmeg answered 15/8, 2021 at 4:38 Comment(0)
B
2

As you can read other answers, still not possible to do so.

Someone mentioned custom environmental variables (similar to custom css variables env() instead of var()), and the principle is sound, though there are still 2 major issues:

  • weak browser support
  • so far there is no way to define them (but probably will be in the future, as this is so far only an unofficial draft)
Bike answered 15/12, 2018 at 14:19 Comment(0)
O
2

This is not exactly an answer to using variables in media queries, but it tackles the problem of having to specify the width multiple times in components

My solution uses container queries in combination with media queries and exploits the possibility to have named containers.

First we need to define the breakpoints

@media (min-width: 399px) {
  body {
    container-type: inline-size;
    container-name: xsmall;
  }
}

@media (min-width: 1079px) {
  body {
    container-type: inline-size;
    container-name: xsmall small;
  }
}

@media (min-width: 1439px) {
  body {
    container-type: inline-size;
    container-name: xsmall small medium;
  }
}

@media (min-width: 1999px) {
  body {
    container-type: inline-size;
    container-name: xsmall small medium desktop;
  }
}

Here we use the possibility to define multiple container names on elements. Using the mobile first approach and adding styles as we go up in screen size.

Now if we want to make some changes to a element in the small screen size and up I can add this style definition

@container small (min-width: 0) {
  ... some css
}

IMO the only thing that makes this look hacky is that the container query need to have a predicate, so the (min-width:0) is needed to make it work.

With this approach we can specify screen breakpoints in a single place and and remove all absolute screen sizes queries from components.

Ornis answered 25/8, 2023 at 11:33 Comment(0)
R
0

With Bootstrap 5, I've solved this with the media-breakpoint-down() function.

First of all, you'll need to import the functions, variables, and mixins files:

@import "~/bootstrap/scss/functions";
@import "~/bootstrap/scss/variables";
@import "~/bootstrap/scss/mixins";

And then, include your specific media style:

@include media-breakpoint-down(lg) {
.btn {
  padding: 20px;
  margin-right: 15px;
}}
@include media-breakpoint-down(md) {
.btn {
  padding: 10px;
  margin-right: 5px;
}}
@include media-breakpoint-down(sm) {
.btn {
  display: none;
}}

Where Breakpoints are defined as a map of (name: minimum width), order from small to large: (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px)

You can also use the media-breakpoint-up(), media-breakpoint-only(), media-breakpoint-between().

See the official doc: https://getbootstrap.com/docs/5.0/layout/breakpoints/#media-queries

Reagent answered 23/11, 2023 at 20:59 Comment(0)
H
0

How to use Variables for Media Query Break Points

So guys i tried something like this below but it wasn't working, so i found out that css variables does not work on break points so what i did was to use SCSS

  @media only screen and (max-width: var(--bp-tablet)) {
    display: none;
  }

SCSS CODE

  1. Create your variable for scss

$bp-tablet: 56.25em; // 900px
  1. call the variable in the breakpoints and that's it!

media only screen and (max-width: $bp-tablet) {
    display: none;
  }
Humerus answered 25/2 at 1:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.