wicg / container-queries Goto Github PK
View Code? Open in Web Editor NEWLicense: Other
License: Other
I’ve got a new idea for a solution to container queries 🤓
To describe the concept, lets first take a look at what we already have in browsers, imagine the following setup:
:root {
--context-width: 100vw;
}
.one-third {
width: 33.3%;
--context-width: calc(parent-var(--context-width) / 3);
}
.two-thirds {
width: 66.6%;
--context-width: calc(parent-var(--context-width) / 3 * 2);
}
Now I can create a component that is in a 16:9 ratio to its context width as easy as:
.component {
height: calc(var(--context-width) / 16 * 9);
}
No matter how nested the one-third
and two-thirds
containers are, a component
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 because parent-var()
is not a thing yet.
If we take this idea further and introduce a concept for custom property conditions with this syntax:
if( <condition> , <value-if-true>, <value-if-false> )
we could do something like:
.component {
float: if(var(--context-width) > 200px, left, none);
}
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 🎉
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 the var()
part would be resolved to something like calc((100vw / 3) / 3 * 2)
and further to 22.22vw
. This value can then be easily compared to 200px
.
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
to true
or false
and create components that just look perfect in every place. Such binary conditions already work in browsers today if you are very creative.
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.
Hi everyone,
I apologize in advance because I'm more a javascript developer than CSS, so the following may not make sense.
My understanding of the container-queries's circular problem is that a chidl rule could conflict with one of it's parent and influence him back.
However, a browser window (for example), resolve implicitly this problem : there is no way the HTML can influence the size of the window.
Why not create a new value frame
for the display
property ?
display: block
behavior<HTML>
display
property).container:min-width(450px) > .child
will be ignore if .container
has no display: frame
).Example:
.container {
display: frame;
float: left;
}
.container:hover {
display : block;
}
.child {
width: 500px;
}
.container:min-width(450px) > .child {
width: 400px;
}
.container
width >= 450px: child is 400px.container
widh < 450px, child is 500px, but don't influence the parent size. An scrollbar may be displayed (depending of the overflow-x
value)Regards,
Abraham
In #2 (comment) I posted an idea about a different syntax. It looks like:
.child:container(min-width: 150px) {
color: red;
}
And the nearest qualified ancestor is selected as the container to run the query against by the browser.
As I put more thoughts into this I got an idea of how the implementation issue (jumping between compute style and layout) could possibly be solved.
If I’m right the browser computes the styles by traversing the DOM tree from top to bottom. In this process it could already calculate and store the width if it knows that it doesn’t depend on its descendants. If it then reaches an element with a container query rule it already knows what the right container is – it’s the nearest ancestor for which it was able to calculate a width – and which width it has. So it should be possible to resolve the container queries without doing the layout process.
It may be that I’m totally wrong with my assumptions about browser internals, but it would be great if it is implementable this way.
If someone is interested in this idea I also wrote a prolyfill and a blog post about this version of container queries.
Not an issue, just something relevant:
Hi y'all... given that lack of activity in over a year, it might be time to concede defeat on this and "archive" this incubation.
Although we've not managed to solve this exactly problem on the Web, would it be fair to say that we have a set of capabilities on the web that are helping solve some the same problems (e.g., CSS grid and sub-grid)?
What do you all think? Or should we try to keep going? We can always un-archive later.
cc @WICG/chairs
I would like us to start the discussion this year by deciding on a "name" everyone could get behind.
Now I know that even the repo is called "container-queries", but I think there are some people out there that would rather call it "element query", or something else altogether.
Why is this important?
We need to be on the same page on this from the beginning, otherwise when it comes to talking about the exact syntax, I can already see ourselves get sidetracked by the fact that someone used "container" while another example has "element" and a third some version of "media".
In my opinion this could lead to discussions that are ultimately pointless, very much like the semicolon vs no semicolon in javascript.
Let's just pick a name, and stick with it.
That will also make syntax examples more consistent.
Now, My Opinion
I would keep the "container" name. 😂
I saw some people proposing syntaxes with "media", but I think we should make it obvious from the very beginning that this is something different from media queries to avoid confusion.
Historically to me it seems that we first started with "element" query, which was only about changing an element's styling based on some conditions on that very element.
Then, "container" queries started to pop up, which could also affect child elements inside the main "containing" element, hence the naming.
Example on how different the two might feel with a made-up syntax:
@element .SomeClass (width > 200px) {
/* Only rules are allowed here */
font-size: 16px;
}
@container .SomeClass (width > 200px) {
:self {
font-size: 14px;
}
.Child {
font-size: 16px;
}
}
In the case of the latter, instances of the ".SomeClass" element individually influence child nodes' styles, based on their own conditions.
(They are scoped in this sense.)
From experience, I think the latter is way more powerful than only applying styles to the element itself.
Now all that being said, I'm not totally against calling it @element
query either, so long as the same concept of containment / scoping applies.
Hi RICG!
I've been working on my own syntax for container-query style element queries - and have begun writing that up as a spec! I'd love to see some action in this repository (it's been a year and a half since the last update) and see if there's anything from my spec we can bring over, or what we can do to breathe some life into this again.
The repository for my container query syntax is here: https://github.com/tomhodgins/element-queries-spec
And the syntax described in that document is already able to be tested with the EQCSS JavaScript plugin. You can already find over a hundred examples of this syntax in use (case studies, and demos) on Codepen here: http://codepen.io/search/pens?q=eqcss&limit=all&type=type-pens
New year, refreshed desire to make Element Queries part of CSS!
May Tim Berners-Lee have mercy on our souls, because it is time to kick off The Great Bikesheddening.
I’m a big fan of the syntax @jonathantneal proposed way back when, but I have two issues with my own preference:
First, it doesn’t make sense strictly in terms of container queries. This syntax is fine for styling child elements if we always assume something like this:
.element:media( min-width: 60em ) .child-el {
background: papayawhip;
}
But there’s nothing stopping someone from writing:
.element:media( min-width: 60em ) {
background: papayawhip;
}
At which point, would we just throw those styles away? That seems strange.
The second issue is the repurposing of media
, which doesn’t seem to make semantic sense in this context. It feels a little telephone-gamed from media="handheld"
—which checks out—to @media( min-width: 30em )
—which kinda makes sense—to .el:media( min-width: 30em )
, which makes no sense whatsoever.
Before I go into details of this algorithm / solution, it's useful to establish some semantics.
First, there is some historical conflation of the terms "container queries" and "element queries". Depending on the solution (how someone thinks about the problem), these are often conflated to mean the same thing. There's discussion here about renaming the repo from "container queries" to "element queries" based on various conventions, etc, but I find that what's often missed when people talk about container queries vs. element queries is that people think they're talking about the same thing, when they often aren't.
To clarify:
Someone may say, "Yes, but isn't the container always an element?" In short, no. In some proposals, yes. In this proposal, no, and for (IMO) very good reasons.
This algorithm / approach has the following:
This is absolutely crucial, and what makes this query algorithm fast (as fast as media queries), deterministic, and single-pass.
What we are querying is the allocated width/height of the content box that this element will be placed in.
Immediately, someone may point out that the content box eventual dimensions can be affected by children. In order for this algorithm to be successful, that's why we must query what the content box is irrespective of children. In other words, what are the dimensions if no children were present?**
** Note: this doesn't mean we would measure as if the container were truly empty; specifically, an :empty
selector would not apply during measurement if children are present, children just don't affect the allocated width/height for the purposes of this algorithm.
Just to briefly address this question, the reason why we query the content box specifically vs. the parent element's width is to avoid ambiguity / surprise when switching box models. Queries always refer to the "available space" to the child element, so that you can set properties of the child based on what pixels (or other units) will actually be available. Querying the parent element's actual (or calculated) width value would strongly bind the child element to the parent's box model. Therefore, the container must be a known constant: the content box dimensions of the parent before children are rendered.
:container([MQ])
- selector query of the container, using MQ Level 3 or Level 4-style queries, and can query width / height [/ inline / block] (-axes)
aw
/ ah
/ ai
/ ab
- units relating to 1% of width / height / inline-axis / block-axis (semantically similar to vw
/ vh
/ vi
/ vb
)
<div class="parent">
<div class="child"></div>
</div>
.parent {
width: 100px;
height: 200px;
}
.child {
background: red;
color: white;
}
/* Also can be written (min-width: 100px) */
.child:container(width >= 100px) {
background: blue;
}
So far, this is similar to most other container query algorithms. Where it differs is that this query is non-circular.
For example:
.parent {
min-width: 50px;
}
.child:container(width <= 50px) {
width: 200px;
}
/** Will not apply **/
.child:container(width >= 200px) {
width: 50px;
}
The .parent
class, when laying out, has only been allocated a width of 50px. Therefore the allocated width of .parent
will stay fixed at 50px unless it is allocated a new width (not sized by children). (100aw
= 50px
)
Here's another simple example, assuming the same markup
.parent {
float: left;
}
.child {
width: 200px;
height: 100px;
background: red;
}
.child:container(min-width: 200px) {
background: blue;
}
In this example, because a float "wraps" around the child, and has no defined width to calculate irrespective of children, then the allocated width/height are both 0
(zero). Meaning the container query (min-width: 200px)
will not apply, and the background of .child
will be red
.
We can make the container query apply by changing this to the following:
.parent {
float: left;
width: 200px;
height: 100px;
}
.child {
width: 100%;
height: 100%;
background: red;
}
.child:container(min-width: 200px) {
background: blue;
}
In the above example, the background of .child
will be blue
.
Other examples of boxes that don't have allocated width / height would be inline boxes (display: inline-box / inline-flex / inline-grid
), or using width: fit-content / min-content
. If they don't have a width
/ min-width
or height
/ min-height
value that can calculate an allocated value before the layout of children is determined, then those values will be zero.
Note that block boxes with a width of auto will have an allocated (non-zero) value. That is, an allocated width can be determined irrespective of children.
So, given a viewport of 1024px
, and this markup:
<body>
<div class="wrapper"></div>
</body>
body {
padding: 0; margin: 0;
}
.wrapper:container(width >= 1024px) {
background: red;
}
In this case, a user agent stylesheet styles <body>
as a block with a width of auto. Therefore the wrapper's container query applies and the background will be red
.
Note that a container query applies to the pre-children content box of the parent. If you are slotting children into a grid, the queries of direct children of the grid will not be related to column / row slotting.
Meaning:
.parent {
width: 300px;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
}
.child {
grid-column: 1;
}
/** Does not apply */
.child:container(width = 100px) {
background: blue;
}
/** Does apply */
.child:container(width = 300px) {
background: red;
}
The above example helps make the important distinction between element queries and container queries. We're not querying the width that will be calculated for .child
. We are strictly querying the initial content box of .parent
.
However, we could easily nest children to "query" the width of the column for some powerful layout possibilities.
Given the following:
<div class="grid">
<div class="grid-item">
<div class="child">First</div>
</div>
<div class="grid-item">
<div class="child">Second</div>
</div>
</div>
.grid {
display: grid;
width: 300px;
grid-template-columns: 2fr 1fr;
}
.child {
height: 100px;
color: white;
}
.child:container(width = 200px) {
background: red;
}
.child:container(width = 100px) {
background: blue;
}
This would give you a layout like this:
Once .grid-item
is slotted into a column, it has a calculated initial content box width that can then be queried by the .child
.
height
/ inline
/ block
Most of these examples have been querying width because of the way we typically layout pages, and because of default collapsing behavior. But, given a fixed (or minimum) height on a parent, it can also be queried.
.parent {
height: 100px;
}
.child:container(height >= 100px) {
background: blue;
}
We can also query the inline-axis or block-axis, depending on writing direction or layout settings.
.child:container(inline >= 100px) {
background: blue;
}
.child:container(block >= 100px) {
background: blue;
}
An important thing to note is that while the initial content box dimensions of an element used for a query can't be affected by children layout, it doesn't mean it's a "fixed" value per page load. A parent element may be resized by resize of the viewport (depending on initial width / height values) or changed programmatically.
On resize, the browser must determine if a parent element has a new initial content box size. In other words, just because the element is "resized", doesn't mean the initial content box size has changed.
This is best demonstrated by example. In the following example, no amount of "resizing" the viewport / browser window will cause the container query to match.
.parent {
float: left;
}
.child {
width: 100vw;
}
.child:container(width >= 50vw) {
background: blue;
}
The initial content box width of parent is always 0
(zero), even though the .parent
element may be visibly resizing on-screen. (As an aside, this is an advantage over ResizeObserver
-based polyfills of content queries, which are subject to circularity, since they respond to any resize of the element.)
In the following example, however, resizing of the viewport will trigger matches / un-matches of the container query, and will dynamically apply those styles.
/** Assuming parent is a child of `<body>` */
.parent {
width: auto;
}
.child {
background: blue;
}
.child:container(width >= 200px) {
background: black;
}
.child:container(width >= 400px) {
background: red;
}
In the above example, note that container queries follow rules of the cascade. By default, .child
has a background of blue
. Once the available content width is 200px
or greater, the background is black
, overriding blue
. Once the available content width is 400px
or greater, the background is red
, overriding black
and blue
.
If the .parent
has explicit dimensions set as inline styles with JavaScript, then the available content box width would be updated, and container queries that query the .parent
content box would be re-evaluated.
2019 Container Queries are extremely powerful, but have an important companion piece which is allocated units. This allows you to easily make your CSS styles more modular / adaptive regardless of container.
Allocated units (aw
/ ah
/ ai
/ ab
) are units relating to 1% of initial container box width / height / inline-axis / block-axis, respectively.
Let's re-use our grid example.
<div class="grid">
<div class="grid-item">
<div class="child">First Child</div>
</div>
<div class="grid-item">
<div class="child">Second Child</div>
</div>
</div>
Based on how much space .child
may take, we can scale font-size to be reflective of that additional space.
.grid {
display: grid;
width: 310px;
grid-template-columns: 2fr 1fr;
grid-gap: 10px;
}
.child {
height: 100px;
padding: 10px;
color: white;
background: blue;
font-size: 15aw; /** 15% of allocated content box width */
}
This would result in:
Of course, you can use a combination of calc()
or newer min()
, max()
or clamp()
CSS functions (when available) to moderate the range / effect of allocated units.
As a result, you can define individual, reusable "modules" that adapt to defined containers.
Note that 100aw
or 100ah
etc. may resolve to 0
(zero) if the parent content box has no intrinsic dimensions.
Question to resolve: Would it be important to define a syntax for a default container width/height for a child element if a query returns a zero for either value? Or is min()
/ clamp()
sufficient? 🤔
A variety of examples of queries
/** MQ4 - value range */
.a:container(400px < width < 1000px) {}
/** can target children of queries, as expected */
.a:container(width > 10em) .b {}
/** negating queries - not equal to 100px */
.a:not(:container(width = 100px)) {}
/** Joining queries */
.a:container(width > 100px):container(height > 100px) {}
/** Nesting queries */
.a:container(width > 100px) .b:container(width > 50px) {}
Feedback welcome. There are likely to be opinions™ around various points of this proposal, such as syntax. I think the selling points are, because this query algorithm is one-way, fast, and predictable, it's something that I believe could be implemented in browsers much sooner than previous proposals, mostly because of zero circularity. Layout / queries can be determined in a single pass, as quickly as media queries are today. This makes this a potential drop-in replacement for many/most media queries, since as noted by many other, smarter people, many people use @media
queries when what they mean are container queries (the available width for my component).
That said, there are obvious use cases of using both @media
and 2019 Container Queries. I just didn't want to get too far in the weeds of creating use cases / examples, as this proposal was already quite long.
Heydon Pickering recently posted a great trick for making a flex items wrap below a breakpoint and display inline above the breakpoint — and the breakpoint is dependent on container width, not viewport. The trick involves using calc()
to force flex-basis
to be either an arbirarily low negative or high positive value.
This is one particular case of container queries, solved using today’s CSS. I know that, using flexbox & grid, there are a few other specific cases that can be accomplished as well. In fact, I believe his technique can be used to solve the specific use-case given in our Use Cases document.
This leaves me with two questions:
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.