How to Do a CSS @Supports Check on a @Media Rule

Is it possible to do a CSS @supports check on a @media rule?

This does not seem to work. Should it work?

No; @supports only supports property declarations, not at-rules or indeed any other grammatical construct in CSS. You're not looking to check for support for the @media rule anyway; you're trying to check for support for specific media features. But @supports doesn't support that either, even though media features share the same declaration syntax with property declarations.

To that end, you don't need the @supports rule at all. To check if a browser supports a certain media feature, simply write a @media rule with a media query list containing both the media feature and its negation:

@media not all and (pointer), (pointer) {  p { color: green; }}
<p>If this text is green, your browser supports the <code>pointer</code> media feature.

Use CSS.supports() with media queries?

No.

CSS Conditional Rules Module Level 3 only provides for testing support for property: value.

CSS Conditional Rules Module Level 4 adds the ability to test for selectors.

There's no provision for testing for support for hover.

Note that even in browsers which do support it, it might not be accessible to the user (e.g. if they use a traditional desktop browser without a mouse/trackpad/etc).

Nesting CSS @supports and @media queries

Both examples are valid CSS according to section 3 of the spec, and for the time being they seem to be consistently supported by browsers that understand both @supports rules and nested @media rules (also new to CSS3).

Although both examples will only apply the background: blue declaration when both the @media and @supports conditions are met,

  • A will apply either background: green or background: blue if and only if the (min-width: 50em) media query is met;
  • B will apply either declaration if and only if the browser understands @supports and supports flex-wrap: wrap.

Since your two examples mean subtly different things, which one you choose will depend on your use case:

  • If you do not want browsers that don't support @supports or flex-wrap: wrap to see either declaration, and to instead always apply background: red, choose B.
  • Otherwise, if you want any browser (that understands media queries anyway) to apply background: green at the specified viewport width, even if it will never apply background: blue due to not supporting @supports or flex-wrap: wrap, choose A.

Mixing an @supports with an @media query in CSS

There isn't a way to combine two different conditional at-rules into one with OR logic (AND logic, as you've stated, can be done by nesting, even though strictly speaking they're still two separate rules). Unless you have plumbing that can automatically duplicate your CSS across two separate conditional rules for you, you will have to do that manually.

If possible, consider approaching this from a progressive-enhancement point of view rather than a graceful-degradation one. That is, rather than applying fallback styles to browsers that either don't support grid or are displaying on smaller screens, apply grid styles to browsers that support grid and are displaying on larger screens — this is where the nesting of rules you mention then makes sense:

@supports (display: grid) {
/* Or screen and (min-width: 701px),
or not screen and (max-width: 700px),
depending on your needs and preferences */
@media screen and (min-width: 700px) {
/* Grid styles */
}
}

If you're not a fan of the extra indentation, you can reduce it somewhat, or just not indent at all ;)

Nesting @media rules in CSS

For those simply looking for an answer to "Which browsers support nesting of @media rules?", the short answer is that all modern browsers, including Firefox, Safari, Chrome (and its derivatives), and Microsoft Edge, now support nesting of @media at-rules as described in CSS Conditional 3. The code in the question with the nested @media at-rules should now work correctly everywhere, with the exception of Internet Explorer (which is no longer being updated with new features, meaning no version of IE will ever support this feature).

This feature did not exist in CSS2.1, since only media types existed at the time which you could simply group with a comma, which explains why support for this was very poor at the time this question was first asked.

What follows is an analysis of the original question with these historical limitations in mind.


There's a bit of terminology confusion that needs clearing up in order for us to understand what exactly is happening.

The code you have refers to @media rules, and not so much media queries — the media query itself is the component that follows the @media token, whereas the rule is the entire block of code consisting of @media, the media query, and the rules nested within its set of curly braces.

This may cause confusion among many when it comes to using media queries in CSS, as well as your specific case where a @media rule in an imported stylesheet works correctly even when the @import is accompanied by another media query. Notice that media queries can occur in both @media and @import rules. They're the same thing, but they're being used to restrictively apply style rules in different ways.

Now, the actual issue here is that nested @media rules are not valid in CSS2.1 because you're not allowed to nest any at-rules within @media rules. However, things seem quite different in CSS3. Namely, the Conditional Rules module states very clearly that @media rules can be nested, even providing an example:

For example, with this set of nested rules:

@media print { /* rule (1) */
/* hide navigation controls when printing */
#navigation { display: none }
@media (max-width: 12cm) { /* rule (2) */
/* keep notes in flow when printing to narrow pages */
.note { float: none }
}
}

the condition of the rule marked (1) is true for print media, and the condition of the rule marked (2) is true when the width of the display area (which for print media is the page box) is less than or equal to 12cm. Thus the rule ‘#navigation { display: none }’ applies whenever this style sheet is applied to print media, and the rule ‘.note { float: none }’ is applied only when the style sheet is applied to print media and the width of the page box is less than or equal to 12 centimeters.

Furthermore, it looks like Firefox is following this specification and processing the rules accordingly, whereas the other browsers are still treating it the CSS2.1 way.

The grammar in the Syntax module hasn't been updated to reflect this yet, though; it still disallows nesting of at-rules within @media rules as with CSS2.1. This specification is slated for a rewrite anyway, so I guess this doesn't matter.

Basically, CSS3 allows it (pending rewriting the Syntax module), but not CSS2.1 (because it neither defines media queries nor allows nested @media rule blocks). And while at least one browser has begun supporting the new spec, I wouldn't call other browsers buggy; instead, I'll say that they simply haven't caught up yet as they're really conforming to an older, more stable spec.

Lastly, the reason why your @import works is because @import is able to work conditionally with the help of a media query. However this has no relation to the @media rule in your imported stylesheet. These are in fact two separate things, and are treated as such by all browsers.

To make your code work consistently across browsers, you can either use your @import statement, or, since both of your rules use min-width, simply remove the nesting of your @media rules:

@media screen and (min-width: 480px) {
body {
background-color: #6aa6cc;
color: #000;
}
}

@media screen and (min-width: 768px) {
body {
background-color: #000;
color: #fff;
}
}

How to handle CSS media query negation including browsers which do not support it?

This is a little tricky; since the only way to apply styles for when a browser doesn't support a particular media feature is by taking advantage of the cascade, you'll need to apply the fallback style outside a @media rule altogether. This does come at a slight cost of duplicating some styles, unfortunately:

.elem {  width:200px;  height:200px;  background-color:#911;  margin:20px;}
/* If maximum 300px wide OR the the device is not able to hover */#dude { background-color:#191;}
/* If at least 300px wide and the device is able to hover */@media (min-width: 300px) and (hover: hover) { #carl { background-color:#191; } #dude { background-color:#911; }}
<div id="carl" class="elem"></div><div id="dude" class="elem"></div>

Mixing @supports and @media queries

At-rules such as @media, @supports and @keyframes are separate rules that cannot be grouped together by their at-keywords. That is, it's not possible to write a single rule with a single pair of curly braces that combines two different at-keywords as you are trying to do. So, unfortunately, nesting is your only option here.

The best you can do is write both statements — and their opening/closing braces — on the same lines:

@supports not (-ms-high-contrast: active) { @media (min-width: 1400px) {
}}

This is basically nesting them as shown in the linked question, minus the excess line breaks and indentation.

Is it possible to put CSS @media rules inline?

@media at-rules and media queries cannot exist in inline style attributes as they can only contain property: value declarations. As the spec puts it:

The value of the style attribute must match the syntax of the contents of a CSS declaration block

The only way to apply styles to one specific element only in certain media is with a separate rule in your stylesheet (be it linked externally or internally in a <style> element), which means you'll need to come up with a selector for it. You can grab one using your browser's dev tools, or figure out a class and/or ID combination that isolates this element:

#myelement { background-image: url(particular_ad.png); }

@media (max-width: 300px) {
#myelement { background-image: url(particular_ad_small.png); }
}

If you're unable to find a selector that will reliably match this element alone due to the nature of your page, you can use a custom property, provided you don't need to worry about specificity or Internet Explorer:

:root { --particular-ad: url(particular_ad.png); }

@media (max-width: 300px) {
:root { --particular-ad: url(particular_ad_small.png); }
}
<span style="background-image: var(--particular-ad);"></span>

CSS feature detection using media queries

From looking at the most recent media queries spec, this is not possible.

Also, to quotefrom here in the spec:

CSS renderers must treat as invalid (and ignore as appropriate) any at-rules, properties, property values, keywords, and other syntactic constructs for which they have no usable level of support.

Ignoring anything inside is to adhere to the spec.

How can you use the @supports css rule in material ui makeStyles?

The syntax for this is similar to the syntax for media queries. In your case, you would want the following:

const useStyles = makeStyles(() => ({
paper: {
'@supports (display: grid)': {
display: 'grid'
}
}
}))

Here's a working example:

import React from "react";
import Button from "@material-ui/core/Button";
import { makeStyles } from "@material-ui/core/styles";

const useStyles = makeStyles((theme) => ({
button: {
"@supports (background-color: red)": {
marginTop: theme.spacing(5),
backgroundColor: "red"
},
"@supports not (display: unsupportedvalue)": {
color: "white"
},
"@supports not (display: grid)": {
backgroundColor: "purple"
}
}
}));
export default function App() {
const classes = useStyles();
return (
<Button className={classes.button} variant="contained">
Hello World!
</Button>
);
}

Edit CSS supports

Related answer:

  • How can I use CSS @media for responsive with makeStyles on Reactjs Material UI?

Related documentation:

  • https://cssinjs.org/jss-plugin-nested?v=v10.5.0#nest-at-rules


Related Topics



Leave a reply



Submit