-
Notifications
You must be signed in to change notification settings - Fork 10
Container queries via custom properties (aka CSS variables) #5
Comments
The problem I'm seeing with the first example is that they're based on viewport-percentage units, but the elements you'll need to be styling may not scale perfectly with the browser's width and/or height. What we need to do these kinds of calculations is a unit that is aware of the width and height of the element as it appears on the page (not as we want it to appear). With that information we could write the kind of code in the first examples. Consider the difference in syntax between the two following examples - the first uses JavaScript to check the <div class=demo-1></div>
<style>
@element '.demo-1' {
$this {
background: lime;
height: eval('offsetWidth / (16/9)')
}
}
</style>
<script src=http://elementqueries.com/EQCSS.js></script> <div class=demo-2></div>
<style>
@element '.demo-2' {
$this {
background: orange;
height: calc(100ew / (16/9));
}
}
</style>
<script src=http://elementqueries.com/EQCSS.js></script> I think before a solution like this would be usable, we would need As for the conditional Here's a version of your <img src=http://staticresource.com/user.png class=component>
<style>
@element 'html' {
img {
float: eval('innerWidth > 200 ? "left" : "right"')
}
}
</style>
<script src=http://elementqueries.com/EQCSS.js></script> And then here are other ways I've used Min/Max font-size@element '[data-min-font],[data-max-font]' {
$this {
font-size: eval('
var vw = innerWidth/100*10, /* equal to 10vw */
min = getAttribute("data-min-font"),
max = getAttribute("data-max-font");
if (min !== null && max !== null) {
vw <= min ? min : max <= vw ? max : vw
} else if (min !== null) {
vw <= min ? min : vw;
} else if (max !== null) {
max <= vw ? max : vw;
}
')px;
}
} Sniffing Element Orientation@element 'div' {
$this {
background: eval("
offsetWidth == offsetHeight ? 'blue'
: offsetWidth > offsetHeight ? 'green'
: offsetWidth < offsetHeight ? 'red'
: null
")
}
} Faking :in-viewport@element 'p' {
$this {
/* Color is red when partly in-viewport */
color: eval("
var top = offsetTop - innerHeight;
var bottom = top + offsetHeight;
if (
(scrollY < offsetTop + offsetHeight)
&& (top < scrollY)
&& (bottom < scrollY + offsetHeight)
){
'tomato'
} else {
'inherit'
}
");
/* Background is green when fully in-viewport */
background: eval("
var top = offsetTop - innerHeight;
var bottom = top + offsetHeight;
if (
(scrollY < offsetTop)
&& (top < scrollY)
&& (bottom < scrollY)
){
'darkgreen'
} else {
'inherit'
}
");
}
} Centered or OverflowThis example below uses @element 'div h2' {
eval('offsetHeight <= parentNode.offsetHeight ? "$this" : ""') {
position: absolute;
top: 50%;
transform: translateY(-50%);
}
eval('offsetHeight >= parentNode.offsetHeight ? "$parent" : ""') {
overflow: auto;
overflow-y: scroll;
border-color: lime;
}
} Using If statement as selectorIn this example we use JavaScript to check the <div style=background-image:url(http://staticresource.com/user.png)></div>
<div style=background-image:url(http://staticresource.com/nebula.jpg)></div>
<style>
div {
height: 200px;
}
@element 'div' {
eval("window.getComputedStyle($it).backgroundImage.indexOf('nebula') !== -1 ? '$this' : '' ") {
border: 10px solid lime;
}
}
</style>
<script src=http://elementqueries.com/EQCSS.js></script> Based on these experiments and more I think that having container-style queries so we can write conditions once and affect styles for many elements is better than writing conditions many times in many rules. Another takeaway is that having an Should the |
I used the viewport units as an example, the CSS author could set the CSS variable to whatever suits best. An element with a fixed width like
IMO that’s exactly the thing that is hard/impossible for browsers to implement, because it has to jump between layout and style computation. For setting aspect-ratios we may get a new property in the future, this is not a topic for container queries IMO.
Your
Using .component {
if(var(--context-width) > 200px) {
float: left;
background: red;
}
else {
float: none;
background: green;
}
} or: .component:if(var(--context-width) > 200px) {
float: left;
background: red;
}
.component:if(var(--context-width) <= 200px) {
float: none;
background: green;
} |
I proposed the CSS conditions feature on WICG Discourse: And wrote an article on how they might be an alternative for container queries: |
Hey @ausi, great idea putting it on WICG Discourse to gather comment, it'll be great to see what people have to say! In your article you refer to the circularity problem with element/container queries, and today I recorded a video about this very issue: https://www.youtube.com/watch?v=QfM_JwSDdjo
I think there's a way to handle circularity that makes sense to how CSS works already! |
I don’t think that’s true for container queries. I watched your video, but I don’t think |
@ausi: I just gave https://au.si/css-conditions-cq-alternative a quick read, and found it really interesting. I’m ~offline this week, but give a quick 🙌; I’ll try to have more substantive comments in next week. |
@beep: Awesome thanks! I’m looking forward to it :) |
I had some thoughts about this today, and was thinking about how this could allow to choose among a set of differing layouts easily without need to extend this further, and wrote this: https://jsfiddle.net/35758bg7/1/ (basically you can duplicate all your layout paths and then from there choose one based on settings a small subset of properties on it based on --width) Posted this here just not to loose it, I don't say this is very smart or relevant, just didn't know where to post it otherwise :) |
When it comes to container queries themselves, there will be usecases this can solve, and others it won't. This actually hits on a common thing that I tell folks when they state, "I need element/container queries" - if you can tell me at a pixel dimension when you want to change the design then you can probably still devise a way to utilize media queries, although it won't be nearly as legible nor clean as your solution - but you don't NEED container queries. That said, I do know of other desires to have conditionals that inspect variable values and apply different styles accordingly. My only worry with this approach, is this will add yet an additional step in the cascade and this isn't fully defined as you can see in this issue I filed here and for us (Edge) this is a pain: w3c/csswg-drafts#411 (comment) and still should do a PR at this point (I haven't found the time). We saw numerous interop issues as this can change the calculation of the variable that you're checking the condition on. And based on my worries for at-apply (which has been abandoned) you may end up in the same situation in this case where if we make it so that the condition is checked at, let's say, in between step 1 and 2 you'll result in some need to stop circularity within the cascade as well (just like we have with layout and style) which could be that you can't change a custom prop within animations that are used within a condition (you would need to do this anyway where you can't change the variable within the condition similar to what we do with animation tainting). At any rate, I think it's something we should at least discuss as it would help out scenarios such as Roma's conditions using calc() |
@gregwhitworth Thank you for taking a look at this! I agree that this feature would not solve all use cases for container queries, but I think it would solve many of them. I will reiterate on that once the use cases in wicg/cq-usecases are defined, as there is some work going on there currently. Regarding circularity, I think this can possibly be solved by restricting the conditions to |
I just wrote another article regarding CSS conditions, including a nice hack which lets us use some form of CSS conditions today: au.si/css-conditions-today I think this demonstrates that the groundwork for CSS conditions is already there and the general concept of such a feature is possible to implement in browsers. I still believe that this can be the answer to most use cases of container queries and at the same time doesn’t create headaches for browser makers. |
I’ve got a new idea for a solution to container queries 🤓
What we already have today
To describe the concept, lets first take a look at what we already have in browsers, imagine the following setup:
Now I can create a component that is in a 16:9 ratio to its context width as easy as:
No matter how nested the
one-third
andtwo-thirds
containers are, acomponent
inside them or at the root level would always be in the desired 16:9 ratio. This works already, take a look at this CodePen, although we need to hack a little becauseparent-var()
is not a thing yet.New concept for CSS conditions
If we take this idea further and introduce a concept for custom property conditions with this syntax:
we could do something like:
This way we told the
component
to only float left if it’s in a context (or container?) with more than 200px width, otherwise it should not float. Sounds pretty much like container queries to me 🎉From the perspective of a browser
My knowledge for browser internals in very limited, but AFAIK one big problem with an implementation of container queries is, that the engine would have to jump between layout and style computation, which could result in unstable results and might be very bad for performance. I think the concept of simple CSS conditions would not suffer from that problem. Taking the example from above, the condition
var(--context-width) > 200px
can be worked out without the need for a layout. First thevar()
part would be resolved to something likecalc((100vw / 3) / 3 * 2)
and further to22.22vw
. This value can then be easily compared to200px
.Flexibility
Another benefit I see with this solution is, that the CSS author can decide how to set and inherit the variables. If one creates a website which has bright and dark sections, he could set
--context-dark
totrue
orfalse
and create components that just look perfect in every place. Such binary conditions already work in browsers today if you are very creative.Downsides
The main downside of this solution is that we need to set variables for every container that changes the available width for its children. And we would sometimes not get the “exact” value if e.g. a scrollbar reduces the available width.
What do you think?
The text was updated successfully, but these errors were encountered: