-
Notifications
You must be signed in to change notification settings - Fork 667
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[selectors][mediaqueries] :media() pseudo-class as a shortcut for one-off media queries #6247
Comments
I definitely agree with this functionality. Having some tests appear in an MQ vs others appearing in a selector means we have to do some awkward nesting and reduplication. This is also the motivation behind a generic conditional rule that combines @media and @supports (the @when proposal that I need to revive). With that background, perhaps we should make the pseudo-class name more generic, so that it can handle support queries as well? |
Agreed, I was wondering about it when I opened the issue, but decided against it as I thought it would complicate the syntax too much. But if you also think so, let's do that. |
I ran into this last week and after asking Tab about it, have found myself here! Here's a reduced case https://codepen.io/argyleink/pen/OJpmEWE where I would hope to eliminate the duplication between light and dark |
I love this idea. I kinda wish media queries just worked this way in the first place. |
We discussed this as an approach to container queries, and it's probably good to consider them as part of this conversation. If the only syntax is selector-based, it makes larger changes (impacting multiple selectors) more repetitive – though nesting could help that problem: .card:container(width > 30em) {
& .card-image { … }
& .card-content { … }
& .card-footer { … }
} With container queries specifically, this also introduces a different way to solve the "named container" problem – but it can be kinda confusing as well. Right now we always have the selector-target query its nearest container: /* body and card are both containers */
body, .card { contain: inline-size style layout; }
@container (width > 30em) {
.card { /* card queries the size of body */ }
.card .content { /* content queries the size of card */ }
}
.card:container(width > 30em) .content {
/* card queries the size of body, in order to update content */
} I don't see it as a universally better solution than the at-rule syntax, but I do think it can provide some interesting trade-offs. Interested to see where this conversation goes. |
Adding Also, you know Emilio won't accept that it makes Element.matches depend on layout. Also also, it's not compatible with other CSS things, e.g.:
|
This comment was marked as duplicate.
This comment was marked as duplicate.
Rather than make media queries & container queries work in selectors, would it be better to make selectors work in @ rules when nested? I haven't thought hard about the parsing here, but something like: :root {
@media (prefers-color-scheme: dark),
&.dark {
/* dark mode rules */
}
} |
Might be more interesting to leverage @scope (.light-scheme) {
a { color: darkmagenta; }
}
@scope (.dark-scheme) {
a { color: plum; }
} With @when media(prefers-color-scheme: dark) or scope(.dark) {
/* styles */
} I do think it will have some weird side-effects to mix conditional at-rules and selectors: @when media(prefers-color-scheme: dark) {
@layer foo {
/* this is fine */
}
} .dark {
@layer foo {
/* this is not fine */
}
} @when media(prefers-color-scheme: dark) or scope(.dark) {
@layer foo {
/* ?? */
}
} Same is true for other syntax proposals. :root {
@media (prefers-color-scheme: dark),
&.dark {
@layer foo {
/* ?? */
}
}
} |
Could also consider a I realized the other day that style queries help work around this issue, but with an unfortunate parent/child limitation. We can do something like: @media (prefers-color-scheme: dark) {
html { --mode: dark; }
}
.dark-mode { --mode: dark; }
/* the result of this query is based on the combined media-query/selector resolutions */
@container style(--mode: dark) {
/* has to be a descendant of the element that sets the value 👎🏼 */
body {
background: black;
color: white;
/* the full list of colors only have to be defined in one place 👍🏼 */
}
} But it feels like a workaround, rather than a full solution. |
Putting selectors into MQs/etc has the wrong semantics; the selector isn't a query with a truth result, it's actually selecting elements, a completely independent action from the conditional itself. |
Well, not really. Pseudo-classes are query something about the element, whereas Fwiw, that's why I suggested: :root {
@media (prefers-color-scheme: dark),
&.dark {
/* dark mode rules */
}
} It's saying "this block of styles applies when this media query matches, or where this selector matches". It isn't putting MQs into element classes, nor is it putting selectors into MQs. |
My point is that the semantics of a pseudo-class (any selector, really) is "filter the currently-matched set of elements according to condition X". The fact that Conditional rules, on the other hand, either match or not, and activate or deactivate the rules inside of them. They haven't previously had any effect on the set of matched elements. Obviously we could add this, changing their pattern of behavior, but it would be a fairly significant change. We could no longer imagine conditional rules as "turning off" rules, but rather as a type of selector in itself that filters the set of matched elements. (Also a nit: putting selector syntax nakedly into MQ syntax would be a no-go; the grammar is too wide and would force us to be very careful evolving both selectors and MQs in the future. But a |
I think context is something about the element. A similar API would be Here is a hypothetical example of context by target class or media query: :root:is(.dark, :when(media(prefers-color-scheme: dark))) {
/* dark mode rules */
} Here is a hypothetical example of context by target class or style query: :root:is(.dark, :when(style(--mode: dark))) {
/* dark mode rules */
} |
Getting back to the problem of code duplication, could we do something similar to SASS's mixin? Instead of setting a Custom Property flag and using container queries to style, you could add a block of code to a ruleset. /* With flag */
.dark { --mode: dark; }
@media(prefers-color-scheme: dark) { --mode: dark; }
@container(style(--mode: dark)) {
/* rules */
}
/* With mixin */
@mixin dark-mode {
/* rules and nested rules that will map to the selector that includes the mixin */
}
.dark { @includes dark-mode }
@media(prefers-color-scheme: dark) {
@includes dark-mode
} It doesn't have to be as full-featured as SASS's mixin, just the code reuse and dynamic scoping would be enough. |
Would this pseudo-class counterpart to the Assuming this applies to all applicable at-rules, this might also solve the issue/proposal I opened today in #10356. This way,
Having a way to use these at-rules inline as pseudos would be amazing. I initially preferred the syntax proposed by @jakearchibald (above) and by @Jothsa in #8840, but that would also impose some limitations. In the case of However, with a pseudo-class rule counterpart available, much of the redundancy in this example: dialog {
transform: translateY(-50%);
&, &::backdrop {
transition: all 0.25s ease-out allow-discrete;
opacity: 0;
}
&[open] {
transform: translateY(0);
&, &::backdrop {
opacity: 1;
}
}
@starting-style {
&[open] {
transform: translateY(-50%);
&, &::backdrop {
opacity: 0;
}
}
}
} …can be consolidated, leaving us with this: dialog {
&, &[open]:starting-style {
transform: translateY(-50%);
&, &::backdrop {
transition: all 0.25s ease-out allow-discrete;
opacity: 0;
}
}
&[open] {
transform: translateY(0);
&, &::backdrop {
opacity: 1;
}
}
} I think @jonathantneal's suggestion of introducing a If we did want a way to match the query syntax exactly, we could consider It might help to organize a comprehensive list of all at-rules that could benefit from a pseudo-class counterpart like this.
Or should we introduce only one new pseudo-class, |
Authors often need to duplicate rules across media queries and selectors. For example, this kind of thing is common:
To my knowledge, there is no way to reduce this duplication with CSS, even if we take CSS nesting into account. Authors either write flimsy JS to toggle the class per the media query, use preprocessor mixins, or just live with the duplication.
Also, often media queries only contain a single rule, and could benefit from a more concise syntax.
With this proposal, the code above would be written as:
Any
:media()
pseudos within a selector would desugar as media queries, joined withand
and prepended withnot
accordingly (e.g.:media(foo):not(:media(bar))
should desugar to@media (foo) not (bar) {...}
).The text was updated successfully, but these errors were encountered: