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

A rough proposal for syntax #2

Open
Wilto opened this Issue Jun 5, 2015 · 22 comments

Comments

Projects
None yet
9 participants
@Wilto
Contributor

Wilto commented Jun 5, 2015

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.

@Wilto Wilto changed the title from A _rough proposal_ for syntax to A rough proposal for syntax Jun 5, 2015

@yoavweiss

This comment has been minimized.

Show comment
Hide comment
@yoavweiss

yoavweiss Jun 5, 2015

@tabatkins had a decent syntax proposal when we last met up in Redmond. Tab?

yoavweiss commented Jun 5, 2015

@tabatkins had a decent syntax proposal when we last met up in Redmond. Tab?

@aFarkas

This comment has been minimized.

Show comment
Hide comment
@aFarkas

aFarkas Jun 5, 2015

Maybe I shouldn't say something, because I don't know anything. But maybe you get inspired:

@container ((.element) with ( min-width: 60em )) {
  .child-el {
    background: papayawhip;
  }
}

aFarkas commented Jun 5, 2015

Maybe I shouldn't say something, because I don't know anything. But maybe you get inspired:

@container ((.element) with ( min-width: 60em )) {
  .child-el {
    background: papayawhip;
  }
}
@WebDevTmas

This comment has been minimized.

Show comment
Hide comment
@WebDevTmas

WebDevTmas Jul 2, 2015

I'm new to the issue but shouldn't we stick closer to the already existing syntax?

@media screen and (max-width: 600px) on (.container) {
    .element {
        background-color: lightblue;
    }
}

WebDevTmas commented Jul 2, 2015

I'm new to the issue but shouldn't we stick closer to the already existing syntax?

@media screen and (max-width: 600px) on (.container) {
    .element {
        background-color: lightblue;
    }
}
@serapath

This comment has been minimized.

Show comment
Hide comment
@serapath

serapath Jul 2, 2015

I would like to see a JavaScript API for this to evolve in parallel, thus you can use element queries in your css, but you can also listen to them in javascript and once they trigger they execute a callback in which you might update the class of certain elements or maybe just to make an ajax call to request some more data needed for the "expanded layout" of a certain component when the screen size increases, right?

serapath commented Jul 2, 2015

I would like to see a JavaScript API for this to evolve in parallel, thus you can use element queries in your css, but you can also listen to them in javascript and once they trigger they execute a callback in which you might update the class of certain elements or maybe just to make an ajax call to request some more data needed for the "expanded layout" of a certain component when the screen size increases, right?

@serapath

This comment has been minimized.

Show comment
Hide comment
@serapath

serapath Jul 2, 2015

NodeList.prototype.forEach = Array.prototype.forEach;

document.querySelectorAll('.container').forEach(function (container) {
  container.addEventListener('@media screen and (max-width: 600px)', function (event) {
    console.log(event.oldWidth); // ?
    console.log(event.width); // ?
    // do something ajax related to fetch more/less data depending on whats needed by the component
  });
});

serapath commented Jul 2, 2015

NodeList.prototype.forEach = Array.prototype.forEach;

document.querySelectorAll('.container').forEach(function (container) {
  container.addEventListener('@media screen and (max-width: 600px)', function (event) {
    console.log(event.oldWidth); // ?
    console.log(event.width); // ?
    // do something ajax related to fetch more/less data depending on whats needed by the component
  });
});
@jonathanKingston

This comment has been minimized.

Show comment
Hide comment
@jonathanKingston

jonathanKingston Jul 2, 2015

Member

Worth taking advantage of element's having the ability to nest? With implied @nest from: http://discourse.specifiction.org/t/css-nesting-specification/839/29

.element {
  @container ( min-width: 60rem ) {
    .child-el {
      background-color: blue;
    }
  }
}

Alternatively that could be inlined to:

.element@container ( min-width: 60rem ) {
  .child-el {
    background-color: blue;
  }
}

Alternatively it could be similar to matches:

.element:is( min-width: 60rem ) {
  .child-el {
    background-color: blue;
  }
}

However I suspect the containing element needs to be declared very explicitly to be a container to give the "iframe like behaviour" I have heard others talk about.

Such that I think this would be bad:

:is( min-width: 60rem) {
  .child-el {
    background-color: blue;
  }
}

If all containing elements that matched that criteria, I can see that one being expensive.


As for JavaScript, some extension of MatchMedia should be sufficient:
https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia

The spec outlines a way to add listeners whihc should match what @serapath is after:
http://dev.w3.org/csswg/cssom-view/#the-mediaquerylist-interface

Member

jonathanKingston commented Jul 2, 2015

Worth taking advantage of element's having the ability to nest? With implied @nest from: http://discourse.specifiction.org/t/css-nesting-specification/839/29

.element {
  @container ( min-width: 60rem ) {
    .child-el {
      background-color: blue;
    }
  }
}

Alternatively that could be inlined to:

.element@container ( min-width: 60rem ) {
  .child-el {
    background-color: blue;
  }
}

Alternatively it could be similar to matches:

.element:is( min-width: 60rem ) {
  .child-el {
    background-color: blue;
  }
}

However I suspect the containing element needs to be declared very explicitly to be a container to give the "iframe like behaviour" I have heard others talk about.

Such that I think this would be bad:

:is( min-width: 60rem) {
  .child-el {
    background-color: blue;
  }
}

If all containing elements that matched that criteria, I can see that one being expensive.


As for JavaScript, some extension of MatchMedia should be sufficient:
https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia

The spec outlines a way to add listeners whihc should match what @serapath is after:
http://dev.w3.org/csswg/cssom-view/#the-mediaquerylist-interface

@serapath

This comment has been minimized.

Show comment
Hide comment
@serapath

serapath Jul 3, 2015

@jonathanKingston i took a look, but I do not have a feeling that this solves the issue for javascript

  1. It doesnt seem to work for element queries or container queries (only for normal media queries)
  2. It doesnt seem to work as eventlisteners or the matching query emitting an event, so i would probably need to write something like:
var setInterval(function testAllTheTime () {
  if (window.matchMedia('@media screen and (max-width: 600px)').matches) {
    /* the viewport is at most 600 pixels wide, while it was "bigger" before */
  } else {
    /* the viewport is more than 600 pixels wide, while it was smaller before*/
  }
}, 100); // test every 100 ms - most of the time its wasted, because the query doesnt match

so instead what i'd like to see:

document.querySelector('#containerFooBar').addEventListener('@media screen and (max-width: 600px)', function (event) {
    if (event.matches) {
        /* the viewport is at most 600 pixels wide, while it was "bigger" before */
    } else {
         /* the viewport is more than 600 pixels wide, while it was smaller before*/
    }
}); // better performance, because the browser fires an event once a media query matches - so i dont need to check all the time

serapath commented Jul 3, 2015

@jonathanKingston i took a look, but I do not have a feeling that this solves the issue for javascript

  1. It doesnt seem to work for element queries or container queries (only for normal media queries)
  2. It doesnt seem to work as eventlisteners or the matching query emitting an event, so i would probably need to write something like:
var setInterval(function testAllTheTime () {
  if (window.matchMedia('@media screen and (max-width: 600px)').matches) {
    /* the viewport is at most 600 pixels wide, while it was "bigger" before */
  } else {
    /* the viewport is more than 600 pixels wide, while it was smaller before*/
  }
}, 100); // test every 100 ms - most of the time its wasted, because the query doesnt match

so instead what i'd like to see:

document.querySelector('#containerFooBar').addEventListener('@media screen and (max-width: 600px)', function (event) {
    if (event.matches) {
        /* the viewport is at most 600 pixels wide, while it was "bigger" before */
    } else {
         /* the viewport is more than 600 pixels wide, while it was smaller before*/
    }
}); // better performance, because the browser fires an event once a media query matches - so i dont need to check all the time
@aFarkas

This comment has been minimized.

Show comment
Hide comment
@aFarkas

aFarkas Jul 3, 2015

@serapath
There is a addListener method:

matchMedia('(max-width: 600px)').addListener(function(){
    console.log(this.matches);
});

However container queries are element specific and can't described with a global matchMedia interface.

Maybe something like this:

document.querySelector('#containerFooBar').matchContainer('(max-width: 600px)').addListener(function(){
    console.log(this.matches);
});

aFarkas commented Jul 3, 2015

@serapath
There is a addListener method:

matchMedia('(max-width: 600px)').addListener(function(){
    console.log(this.matches);
});

However container queries are element specific and can't described with a global matchMedia interface.

Maybe something like this:

document.querySelector('#containerFooBar').matchContainer('(max-width: 600px)').addListener(function(){
    console.log(this.matches);
});
@jonathanKingston

This comment has been minimized.

Show comment
Hide comment
@jonathanKingston

jonathanKingston Jul 4, 2015

Member

@aFarkas exactly that syntax is what I was thinking thanks.

Member

jonathanKingston commented Jul 4, 2015

@aFarkas exactly that syntax is what I was thinking thanks.

@serapath

This comment has been minimized.

Show comment
Hide comment
@serapath

serapath Jul 4, 2015

I'd prefer arguments being passed into the callback instead of having this.matches, but otherwise looks great 👍

serapath commented Jul 4, 2015

I'd prefer arguments being passed into the callback instead of having this.matches, but otherwise looks great 👍

@pdaoust

This comment has been minimized.

Show comment
Hide comment
@pdaoust

pdaoust Jul 6, 2015

I was thinking about what @WebDevTmas said and, while I was initially resistant to the idea, it's slowly growing on me. On the one hand, adding container query syntax to @media queries is overloading an already burdened concept (container queries really don't have much to do with the UA's media at all). It would be nice, semantically, to break free and choose a keyword that describes exactly what container queries do: query the container.

That said, overloading @media has a few things going for it:

  • People already know about the size-based media query concept; adding one more predicate onto it would simply be seen as enhancing its feature set. Could be a low-friction way of getting developer acceptance.

  • (This is the more important point in my mind...) It'd work in all the places we already use @media queries, such as matchMedia() and <img sizes="..."/>. I personally don't want to write container queries for my nice user profile widget and then have to resort to media queries when I specify the sizes in that widget's user avatar image. Would we change the grammar of sizes to support container queries somehow? We wouldn't need to if media queries had an on (.container) sort of predicate. You just get the new functionality for free.

    (Please ignore for a moment that the element's size would be unknown at parse time because CSS only blocks rendering and not parsing. I realise that browser vendors will be none too happy about having to support a responsive image selecting algorithm like that. I'm just thinking about developer-friendliness right now.)

pdaoust commented Jul 6, 2015

I was thinking about what @WebDevTmas said and, while I was initially resistant to the idea, it's slowly growing on me. On the one hand, adding container query syntax to @media queries is overloading an already burdened concept (container queries really don't have much to do with the UA's media at all). It would be nice, semantically, to break free and choose a keyword that describes exactly what container queries do: query the container.

That said, overloading @media has a few things going for it:

  • People already know about the size-based media query concept; adding one more predicate onto it would simply be seen as enhancing its feature set. Could be a low-friction way of getting developer acceptance.

  • (This is the more important point in my mind...) It'd work in all the places we already use @media queries, such as matchMedia() and <img sizes="..."/>. I personally don't want to write container queries for my nice user profile widget and then have to resort to media queries when I specify the sizes in that widget's user avatar image. Would we change the grammar of sizes to support container queries somehow? We wouldn't need to if media queries had an on (.container) sort of predicate. You just get the new functionality for free.

    (Please ignore for a moment that the element's size would be unknown at parse time because CSS only blocks rendering and not parsing. I realise that browser vendors will be none too happy about having to support a responsive image selecting algorithm like that. I'm just thinking about developer-friendliness right now.)

@aFarkas

This comment has been minimized.

Show comment
Hide comment
@aFarkas

aFarkas Jul 6, 2015

@pdaoust

Please ignore for a moment that the element's size would be unknown at parse time because CSS only blocks rendering and not parsing.

You forget something here. What you want is that the browser should delay selecting a resource until the size of the container is known (so you can use container queries to describe your sizes). But as soon as the browser is doing this, you don't need to use sizes at all. Because at this time the browser is able to also compute the the display size of your image. At the end you get something similar what lazySizes is already doing with the data-sizes="auto" feature.

matchMedia() can't be re-used, because this API is global and container queries are element specific.

aFarkas commented Jul 6, 2015

@pdaoust

Please ignore for a moment that the element's size would be unknown at parse time because CSS only blocks rendering and not parsing.

You forget something here. What you want is that the browser should delay selecting a resource until the size of the container is known (so you can use container queries to describe your sizes). But as soon as the browser is doing this, you don't need to use sizes at all. Because at this time the browser is able to also compute the the display size of your image. At the end you get something similar what lazySizes is already doing with the data-sizes="auto" feature.

matchMedia() can't be re-used, because this API is global and container queries are element specific.

@pdaoust

This comment has been minimized.

Show comment
Hide comment
@pdaoust

pdaoust Jul 6, 2015

@aFarkas oh, good point; that totally didn't occur to me. So I wonder how this should be resolved... it'd sure be nice to have the browser select the correct image size based on its actual, post-layout size. That sounds like a big tangled mess to me, going against the reason we needed sizes in the first place. (But oh how I wish we didn't need sizes... Maybe we need something like <img defer/>.)

Thoughts? I sorta feel like I should break this off into a separate issue.

pdaoust commented Jul 6, 2015

@aFarkas oh, good point; that totally didn't occur to me. So I wonder how this should be resolved... it'd sure be nice to have the browser select the correct image size based on its actual, post-layout size. That sounds like a big tangled mess to me, going against the reason we needed sizes in the first place. (But oh how I wish we didn't need sizes... Maybe we need something like <img defer/>.)

Thoughts? I sorta feel like I should break this off into a separate issue.

@aFarkas

This comment has been minimized.

Show comment
Hide comment
@aFarkas

aFarkas Jul 6, 2015

This has nothing to do with container queries. It would be an additon to responsive images. (Introduce a new attribute: autosize or lazysize.)

aFarkas commented Jul 6, 2015

This has nothing to do with container queries. It would be an additon to responsive images. (Introduce a new attribute: autosize or lazysize.)

@pdaoust

This comment has been minimized.

Show comment
Hide comment
@pdaoust

pdaoust Jul 6, 2015

@aFarkas yeah, I realised that it's not really specific to container queries after thinking through the issue; hence my previous comment re: wishing we didn't need sizes and wanting something like defer, which would fill the same use case as your autosize or lazysize.

Looks like someone's already thinking along those same lines: https://github.com/ResponsiveImagesCG/ri-defer-usecases

pdaoust commented Jul 6, 2015

@aFarkas yeah, I realised that it's not really specific to container queries after thinking through the issue; hence my previous comment re: wishing we didn't need sizes and wanting something like defer, which would fill the same use case as your autosize or lazysize.

Looks like someone's already thinking along those same lines: https://github.com/ResponsiveImagesCG/ri-defer-usecases

@tabatkins

This comment has been minimized.

Show comment
Hide comment
@tabatkins

tabatkins Jul 6, 2015

Re: a JS API, this is coming - browsers have been talking about resize events on all elements, which would give you this ability.

tabatkins commented Jul 6, 2015

Re: a JS API, this is coming - browsers have been talking about resize events on all elements, which would give you this ability.

@serapath

This comment has been minimized.

Show comment
Hide comment
@serapath

serapath Jul 8, 2015

wow awesome :-) resize events for elements?

On 6 July 2015 at 19:34, Tab Atkins Jr. notifications@github.com wrote:

Re: a JS API, this is coming - browsers have been talking about resize
events on all elements, which would give you this ability.


Reply to this email directly or view it on GitHub
#2 (comment)
.

serapath commented Jul 8, 2015

wow awesome :-) resize events for elements?

On 6 July 2015 at 19:34, Tab Atkins Jr. notifications@github.com wrote:

Re: a JS API, this is coming - browsers have been talking about resize
events on all elements, which would give you this ability.


Reply to this email directly or view it on GitHub
#2 (comment)
.

@ausi

This comment has been minimized.

Show comment
Hide comment
@ausi

ausi Jul 14, 2015

Collaborator

At which point, would we just throw those styles away? That seems strange.

What about using a pseudo class on the child element itself? Like so:

.child-el:container( min-width: 60em ) {
  background: papayawhip;
}

It wouldn’t be possible to omit the child element then.

Another benefit would be, that the browser can determine what element should be used as the container. The browser can traverse the DOM tree up and select the first qualified element as the container. A qualified element has to match certain characteristics, e.g. it must have a width which doesn’t depend on the size of its child elements. If the browser finds no qualified parent element it can use the viewport size. So this behavior would also solve the recursion issue.

Collaborator

ausi commented Jul 14, 2015

At which point, would we just throw those styles away? That seems strange.

What about using a pseudo class on the child element itself? Like so:

.child-el:container( min-width: 60em ) {
  background: papayawhip;
}

It wouldn’t be possible to omit the child element then.

Another benefit would be, that the browser can determine what element should be used as the container. The browser can traverse the DOM tree up and select the first qualified element as the container. A qualified element has to match certain characteristics, e.g. it must have a width which doesn’t depend on the size of its child elements. If the browser finds no qualified parent element it can use the viewport size. So this behavior would also solve the recursion issue.

@pdaoust

This comment has been minimized.

Show comment
Hide comment
@pdaoust

pdaoust Jul 14, 2015

@ausi

A qualified element has to match certain characteristics, e.g. it must have a width which doesn’t depend on the size of its child elements. If the browser finds no qualified parent element it can use the viewport size. So this behavior would also solve the recursion issue.

That's a really interesting perspective, and I wonder whether the browser makers would be amenable to that sort of idea. It avoids the need to create a concept of 'viewport-like elements', because the browser already knows what sorts of things are explicitly sized. It might also present a simple model for determining which axis/axes should be considered fixed and hence queryable: "does any parent of this child have an explicit height? if yes, and it matches the query, then apply the styles."

It does require the user to be explicit about container size, but I don't see that as a bad thing. My one concern is that it might be hard to reason about what container is being queried, but then, that's just what we've come to expect from CSS, isn't it? "What is this element floating inside? What's the stacking context? What's the positioning context? Why is this button inheriting a green border?! AAAAA!"

@tabatkins As a guy who works pretty closely with people who write browsers, can you see any compelling reasons to consider or reject this as a solution?

pdaoust commented Jul 14, 2015

@ausi

A qualified element has to match certain characteristics, e.g. it must have a width which doesn’t depend on the size of its child elements. If the browser finds no qualified parent element it can use the viewport size. So this behavior would also solve the recursion issue.

That's a really interesting perspective, and I wonder whether the browser makers would be amenable to that sort of idea. It avoids the need to create a concept of 'viewport-like elements', because the browser already knows what sorts of things are explicitly sized. It might also present a simple model for determining which axis/axes should be considered fixed and hence queryable: "does any parent of this child have an explicit height? if yes, and it matches the query, then apply the styles."

It does require the user to be explicit about container size, but I don't see that as a bad thing. My one concern is that it might be hard to reason about what container is being queried, but then, that's just what we've come to expect from CSS, isn't it? "What is this element floating inside? What's the stacking context? What's the positioning context? Why is this button inheriting a green border?! AAAAA!"

@tabatkins As a guy who works pretty closely with people who write browsers, can you see any compelling reasons to consider or reject this as a solution?

@tabatkins

This comment has been minimized.

Show comment
Hide comment
@tabatkins

tabatkins Jul 14, 2015

Whether you base it on a particular container or the "nearest" container doesn't matter a ton; both are workable approaches. The former is more powerful than the latter, but slightly harder to work with as a result, so it's a trade-off.

tabatkins commented Jul 14, 2015

Whether you base it on a particular container or the "nearest" container doesn't matter a ton; both are workable approaches. The former is more powerful than the latter, but slightly harder to work with as a result, so it's a trade-off.

@pdaoust

This comment has been minimized.

Show comment
Hide comment
@pdaoust

pdaoust Jul 14, 2015

Okay, just checking, cuz I'm pretty ignorant of all this stuff :-) I do like the idea of container queries just not working if the container (whether explicit/particular or implicit/nearest) doesn't have well-defined sizing of some sort (either block-level and normal flow, or floated/absolute and explicitly sized, or flex-with-already-resolved-layout). I feel like it might make layout calculation simple because the parent's size has already been defined and can only be re-layouted as a result of being influenced by elements outside the container.

pdaoust commented Jul 14, 2015

Okay, just checking, cuz I'm pretty ignorant of all this stuff :-) I do like the idea of container queries just not working if the container (whether explicit/particular or implicit/nearest) doesn't have well-defined sizing of some sort (either block-level and normal flow, or floated/absolute and explicitly sized, or flex-with-already-resolved-layout). I feel like it might make layout calculation simple because the parent's size has already been defined and can only be re-layouted as a result of being influenced by elements outside the container.

@ausi

This comment has been minimized.

Show comment
Hide comment
@ausi

ausi Jul 14, 2015

Collaborator

My one concern is that it might be hard to reason about what container is being queried, but then, that's just what we've come to expect from CSS, isn't it?

I think it wouldn’t be that hard in real world examples. It would work similar to position: absolute which goes the DOM tree up until it finds a “positioned” ancestor. So it should be familiar for CSS authors.

IMO a great advantage of this approach is, that as a CSS author I cannot write “wrong” container queries with it, because I’m unable to select an invalid container. And selectors like .my-el:container(...) would look better than *:media(...) > .my-el for cases where I don’t now the parent element, which may be a common case.

Collaborator

ausi commented Jul 14, 2015

My one concern is that it might be hard to reason about what container is being queried, but then, that's just what we've come to expect from CSS, isn't it?

I think it wouldn’t be that hard in real world examples. It would work similar to position: absolute which goes the DOM tree up until it finds a “positioned” ancestor. So it should be familiar for CSS authors.

IMO a great advantage of this approach is, that as a CSS author I cannot write “wrong” container queries with it, because I’m unable to select an invalid container. And selectors like .my-el:container(...) would look better than *:media(...) > .my-el for cases where I don’t now the parent element, which may be a common case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment