Skip to content
This repository has been archived by the owner on Feb 16, 2023. It is now read-only.

Element query should be based on element width. #11

Closed
jehoshua02 opened this issue Sep 2, 2014 · 27 comments
Closed

Element query should be based on element width. #11

jehoshua02 opened this issue Sep 2, 2014 · 27 comments

Comments

@jehoshua02
Copy link

I propose element queries should be based not on the parent width, but on the element width. The element might be constrained in some cases to a fixed width while the parent may be larger.

I would modify the document as follows:

What this document proposes is the addition of an “element query” syntax, allowing breakpoints to be applied based on the width of a parent container the element itself. For the purposes of this example, we would be then be able to scope out modules’ layouts to the size of the module itself.

I haven't read any farther than this yet. There may be other modifications necessary to keep everything in harmony.

@tabatkins
Copy link
Contributor

Can you give an example of where this would help that couldn't be addressed by just putting the styles into the element directly?

@jehoshua02
Copy link
Author

Hello @tabatkins! I'm excited by the effort I'm seeing here.

In order to answer your question I'd need to see a more concrete example of the proposed element query syntax.

@jehoshua02
Copy link
Author

What about something like this?

.element {
  /* default .element styles */
}

.element (min-width: 300px) {
  /* styles when .element width is greater than 300px */
}

.element (min-width: 480px) and (max-width: 768px) {
  /* styles when .element width between 480px and 768px */
}

If the syntax were something like this, I would expect it to be based on the .element width, not it's parent.

@jehoshua02
Copy link
Author

Maybe that looks too much like a normal css rule?

@element .element (min-width: 300px) {
  .element {
    /* styles for .element when .element widht is greater than 300px */
  }
  .element .child {
    /* styles for .element .child when .element width is greater than 300px */
  }
  .element .another-child {
    /* styles for .element .another-child when .element width is greater than 300px */
  }
}

I would expect this element query to be evaluated for each element matching selector .element and the styles only being applied to that individual element if that individual element's width matches the criteria.

Did I just open a can of worms?

@tabatkins
Copy link
Contributor

Syntax doesn't actually matter; I was just looking for an example of what you'd actually want to do with it.

Seems like element width is either pre-known (if it's a definite length) or directly calculated from the container's width (if it's a percentage); the former means you can just write your CSS against that length, the latter means you can use an EQ against the container width. EQ against the container allows for more things, as well.

Do you have an example of something you want to do with EQs that would be easier if it was based on the element instead (or even better, difficult/impossible to base off the container)?

@jehoshua02
Copy link
Author

To be honest, I haven't been working with element-level breakpoints long enough to have some really solid examples.

The reason I brought it up is that It feels illogical to rely on the parent width when what you really care about is the element width. And when looking at the code, I'd want to read it something like this "When the element has a width of X, style it and it's children like this ..."

Typically, I just want to style the children of the element, to shuffle the layout of the child elements so they fit better based on the element's width. Perhaps I need to read it closer.

I understand that developing standards probably requires more than just "feelings". So let me think about it and see if I come up with some examples. Heck, you might come up with an example before I do.

As far as syntax, the parent should not be part of the syntax, since I want the element to look right no matter what it's parent might be.

On the other hand, perhaps you could point me toward an explanation why the parent width is preferable? My stance is still susceptible to change. Maybe what you call "parent container" is what I'm calling "element"? So maybe there is no difference in what we are talking about only how we say it?

I wrote a library breakpoints.js. That's probably the best reflection of how I think about element queries. You'll see that I use the element's own width when evaluating the breakpoint. Actually, I use the element's "outer" width (probably another discussion what "width", in the context of EQs, actually means: content-box, padding, border, margin, box-sizing, etc).

@jehoshua02
Copy link
Author

What I could do for experience is branch breakpoints.js and see what it would look like if I base it off parent width and get back to you.

@tabatkins
Copy link
Contributor

If you just need to style the children, that means the element itself is the container (for the children). They can respond to the element's width using the existing mechanism.

@alanmoo
Copy link
Contributor

alanmoo commented Nov 4, 2014

It seems that the current use case proposal is based on modular elements wrapped in columns used for layout (think Bootstrap). By basing the element query on the width of the parent, this use case works beautifully, as layout and components are on separate levels within the DOM.

One use case that comes to mind where it might be necessary to rely on the width of the element itself is when flexbox is used to define the width of components. In these cases, width of the element is unknown until the flexed layout is calculated, and the width of the parent is irrelevant. I suppose the EQ could be applied to only elements inside one of these flex-sized elements, but then you'd be enforcing a specific DOM structure on the use of all elements using EQ's.

@jehoshua02
Copy link
Author

@alanmoo I might be missing the point of your comment. I also might be bikeshedding. Let me know.

If I understand correctly, we have a series of blocks in a container. To style the blocks, particularly the layout of content within the block, you can attach the EQ in two places: on the container or on the blocks within the container.

  1. EQ on container. Not so modular for styling the block since all that neat block styling won't follow the block if placed outside the column layout. But there are some definite use cases:
    • Overriding some block styles to make the block better fit in the layout. For example, removing/adding extra padding or margins on the blocks, or adding borders or background colors to the blocks.
    • Implementing the column layout itself. For example, floating the blocks, setting their width, or using flexbox, to achieve a column layout based on the width the container has available for itself.
  2. EQ on block. This is probably more commonly what I would want to do. This way, it doesn't matter where my component is, it always has it's own way of responding to the space it is given, independent of anything else happening in the layout.

My two cents:

  • Everything is a block, and I would use EQ on whatever block I need to, always to style the children of the EQ target based on the target's width.
  • There may be use case for styling the target of the EQ itself, but I have a hard time thinking of one that makes sense.
  • I can put an EQ on a column container to style the blocks to achieve a column layout.
  • Or I can put an EQ on the blocks themselves, to style their contents in whatever way is most appropriate for the space available to the block.
  • I could also do both at the same time, one EQ to achieve the column layout on the container, and another EQ for the blocks. This is where EQ gets really cool.
  • I would probably never, or rarely, style the EQ target's children's children. This seems like a violation of the separation of concerns rule. But I don't think it's the EQ's job to enforce discipline.

@tabatkins
Copy link
Contributor

All of your use-cases are about styling the contents of a block based on the size of the block. That's an EQ-on-container, where "container" is the block and "stuff responding to EQ" is the contents of the block.

Maybe you're interpreting "container" too literal-mindedly?

@jehoshua02
Copy link
Author

Bottom line: Something rubbed me the wrong way about the wording in that paragraph. My proposed change isn't quite right either. So I need to help come up with something else. How about this?

What this document proposes is the addition of an “element query” syntax, allowing breakpoints to be applied based on the width of a parent container allowing styling of an element's children and/or itself based on the element's width. For the purposes of this example, we would be then be able to scope out modules’ layouts to the size of the module itself.

Also, there's probably fewer use cases for it, but what about other properties besides width? There's nothing in the EQ name that says it's limited to just the width property.

What this document proposes is the addition of an “element query” syntax, allowing breakpoints to be applied based on the width of a parent container allowing styling of an element's children and/or itself based on the value of one or more of the element's properties. For the purposes of this example, we would be then be able to scope out modules’ layouts to the size of the module itself.

@jehoshua02
Copy link
Author

@tabatkins Yes. Everything is a block. And most use cases are setting EQ on block and styling contents.

I think I was just looking for consistent language. After all, it is an "element query". Not a "parent query".

To me it wasn't clear enough what a "breakpoint" is, what it's "applied" to, what we are styling (the EQ target element's children and/or itself), and what the target of the EQ is (width on an element).

I'm probably just swallowing the elephant and straining at the gnat here.

@tabatkins
Copy link
Contributor

allowing styling of an element's children and/or itself based on the element's width

Again, styling children is fine, with the EQ magic that makes the parent pretend it doesn't have any children when sizing itself. But styling the element itself is really circular. However, none of the use-cases you've described seem to require that.

After all, it is an "element query". Not a "parent query".

It queries the size of an element, as opposed to a "media" query that queries the medium the page is rendered on.

The terminology around "breakpoints" is fairly common in responsive designs; we can clear it up, of course.

@wnr
Copy link
Contributor

wnr commented Feb 5, 2015

Hello all.
I'm doing my Master's Thesis regarding element queries. I've been lurking around for a while reading your discussions. Now I've got to the point where I feel like I need to start asking questions. I hope I won't be too troublesome for you guys :)

@tabatkins When I first thought about how EQ would work, I too as @jehoshua02 based my approach on having the queries targeting any element (as opposed to the only container elements). I understand that it might usually (or always) be the case that you want to query the container, but I'm trying to understand why this limitation is a good thing. You wrote earlier:

EQ against the container allows for more things, as well.

Could you please elaborate on that?

Thanks

@jehoshua02
Copy link
Author

@wnr I think in the end, @tabatkins and I realized that we were talking about the same thing. What I was trying to point out, not very articulately or precisely, is the inconsistency in language. They are "element queries" -- queries against an element. Not "container queries". But they are essentially the same thing, it's just what element, or context, from which you are looking at it. I apologize if I have caused any confusion.

@wnr
Copy link
Contributor

wnr commented Feb 6, 2015

@jehoshua02 Thank you for your quick response. From what I understand of your discussion here I don't think you are talking about the same thing.

Forgive me for bringing syntax into the discussion (but I feel like it's needed to not get confused about terminology). If we forget about valid use cases for a while, there is clearly a difference between the following two approaches:

Let's say we have this piece of HTML in a module (we don't know the surroundings):

<div id="container">
   <div class="child"></div>
   <div class="child"></div>
</div>

Approach 1 (container-only queries)

The approach Tab seems to be recommending
This would restrict us to only be able to apply rules on the child elements, right? Like so:

.child {
   background: red;
}

/* Made up syntax. Will only match if the #container element is between 600 and 1000 pixels wide. */
#container:eq(min-width:600px and max-width:1000px) .child {
   background: blue;
}

So this example will make the children of the container colored blue when the container element is of a width between 600 and 1000 pixels. Otherwise the children will be red.

Approach 2 (any-element queries)

The approach jehoshua02 (and I) seems to be recommending

.child {
   background: red;
}

/* Made up syntax. Will only match if the #container element is between 600 and 1000 pixels wide. */
#container:eq(min-width:600px and max-width:1000px) .child {
   background: blue;
}

/* Made up syntax. Will only match if the #container element is above 1000 pixels wide. */
#container:eq(min-width:1001px) {
   background: yellow;
}

So with this approach it is possible to also color the container element based on it's own width. The children will behave exactly as in approach 1. The difference is that the container will be colored yellow when the width of the container is above 1000 pixels.

I do realize that the outcome of approach 2 is possible to achieve with approach 1 by wrapping the content by another container like so:

<div id="outer-container">
   <div id="container">
      <div class="child"></div>
      <div class="child"></div>
   </div>
</div>
.child {
   background: red;
}

/* Made up syntax. Will only match if the #outer-container element is between 600 and 1000 pixels wide. */
#outer-container:eq(min-width:600px and max-width:1000px) .child {
   background: blue;
}

/* Made up syntax. Will only match if the #outer-container element is above 1000 pixels wide. */
#outer-container:eq(min-width:1001px) #container {
   background: yellow;
}

Back to the question

So it seems to me that everything you can do with approach 2 you can also do with approach 1 by adding extra markup. The question is if it holds the other way around. Can you by using approach 2 do anything you can do with approach 1? I can't come up with anything that you cannot do. If I didn't misunderstand @tabatkins when he wrote EQ against the container allows for more things, as well. means that Tab have thought of something that cannot be done with approach 2, but can be done with approach 1. That's why I would like Tab to elaborate on this :)

If you two indeed have been talking about approach 2 all along, I believe the document should be changed according to your initial issue proposal.

Sorry for the super-long post.

@jehoshua02
Copy link
Author

@wnr I vote for approach #2. That's what I imagine when I think EQ. Is syntax abstinence stultifying our discussions?

@PaulSpr
Copy link

PaulSpr commented Feb 6, 2015

Aren't both approaches the same thing? From what I can gather you would be able to set an EQ on any element and you wouldn't need to specify a child unless you wanted to. That would be a weird constraint given how CSS selectors all currently work.

What might be meant with the container is that the containers width would be used in determining of an EQ matches when the element it is specified on has width: auto?

An important discussion might be what to do if an element width is set to an absolute value like width: 500px, would it only ever match an EQ for 500px (or a range that holds 500x) or would it listen to the space the parent gives it? The latter which is problematic if you're using percentage widths.

And what if an element matches an EQ that sets an absolute width meaning the element is "locked" to that EQ. Or even better (and discussed elsewhere in this repo) if an EQ sets a width that triggers another EQ which could easily result in infinite loops or at least a buggy mess? The solution there would be to ignore any CSS value that triggers layout of course, but would we want that? You would at least want to be able to set position: relative so you can absolutely position other elements in the element.

@PaulSpr
Copy link

PaulSpr commented Feb 6, 2015

Re-reading this thread I think everyone is talking about the same thing and has the same goal. The confusion is around how widths for the EQ element are determined.

I think we shouldn't allow the element on which the EQ is on the set it's own width, but that is will of course listen to it's own width (which is the entire point of EQs).

A containing element would however be able to set children widths. That way you can make a container with children morph from a stacked row layout to a column layout based on the width of the container. The children within the container would have their own styling based on their EQs.

And if a container is a certain width, the children within it would be able to have an absolute width as long as they can't set it on themselves with their own EQs (in which case it would never change anymore).

So in code terms (just to make it more clear):

/* This shouldn't be allowed */
#container .child(min-width: 300px){
    width: 200px;
}

/* This should be allowed */
#container(min-width: 300px) .child{
    width: 200px;
}

@wnr
Copy link
Contributor

wnr commented Feb 8, 2015

@PaulSpr

Aren't both approaches the same thing? From what I can gather you would be able to set an EQ on any element and you wouldn't need to specify a child unless you wanted to. That would be a weird constraint given how CSS selectors all currently work.

I realize now that I wasn't too clear in my example of approach 1. I indeed meant that in approach 1 you can only query parents, meaning that you are required to specify a child in the selector. This is my interpretation of what @tabatkins is recommending (and probably rightfully so due to implementation specifics). So with that clarification, the two approaches are clearly not the same. Approach 1 is a subset of approach 2. In other words approach 1 is the same as approach 2 only with the constraints that you have to specify a child in the selector (i.e. writing the EQ against the container/parent of the element that you want to style).

I am aware of the implementation problems that approach 2 brings, and I do understand that approach 1 is more valid in terms of performance of rendering engines (from what I gather from the mail conversations at org.w3.www-style). However, I think this is not the place for taking that into consideration, since we only want to talk about use cases here and keep all implementation/solution specific things out. Although I think you have many valid points @PaulSpr I believe it is too early (or not the right place) to discuss or even think about those things. The only reason I brought this up is to change the document paragraph:

What this document proposes is the addition of an “element query” syntax, allowing breakpoints to be applied based on the width of a parent container. For the purposes of this example, we would be then be able to scope out modules’ layouts to the size of the module itself.

Because it can now be interpreted as that the document is assuming approach 1. Since this document should only address use cases, I think it should be changed.

My suggestion is to the following:

What this document proposes is the addition of an “element query” syntax, allowing breakpoints to be applied based on the width of a parent container conditional style rules to be applied to elements based on element dimensions. For the purposes of this example, we would be then be able to scope out modules’ layouts to the size of the module itself.

Or something similar (leaving the words parent, container and self out of the description). Keeping it a bit vague about syntax/approach specifics.

@PaulSpr
Copy link

PaulSpr commented Feb 8, 2015

@wnr I always assumed that approach 2 was the way EQs would work. So I also agree with the more vague description.

That said, even though we are now in the use-case phase of the proposal, how they would actually work is important. Especially given the main use-case: Creating adaptive layout pieces.

A good example is the representation of a book, let's call it a cover layout, with a title, cover, author, publish date, publisher, genre, etc.. When viewing a review of the book, the cover layout is in a small sidebar. Under the review there are two related books using the cover layout in 2 columns. One another page there is an overview of all the books with a 4 column layout. This is all for a desktop kind of layout.

If we write EQs against the cover layout width (approach 2) we get super simple and very efficient CSS.

If we write EQs against the elements containing the cover layout elements (approach 1) we would probably have a lot of the same code in each of the containers. The related books for example would at some point (small screen) get the exact same layout as the book in the sidebar. These styles would then be present in both the sidebar containing element as well as the related books containing element.

If an element could be a standalone piece of layout/content it should be responsible for its own layout, but let its size be dictated by what its parent says it should be.

This goes many ways. In the book example the cover layout is responsible for its layout in different sizes, but the related books layout (which contains 2 cover layouts) is responsible for its layout, in this case how the cover layouts are arranged. Think: 1-column, 2-columns, the spacing between the elements overall background color, etc.. This changes the size of the elements it contains, which themselves present them in the best possible way.

@wnr
Copy link
Contributor

wnr commented Feb 8, 2015

@PaulSpr I agree. So how do we move forward from this? Any input from an owner of this repo? I'll create a PR in the meantime.

@tabatkins
Copy link
Contributor

The fundamental technical issues against an element's styles depending on themselves still apply. We can't reasonably apply selectors against an element's own width for styling itself; it has to be a container with certain properties (namely, that it no longer pays attention to its children when computing certain dimensions of its size).

I recognize that this makes some cases harder to write, and there might be some cases that it makes impossible, but it's unavoidable technically.

@jehoshua02
Copy link
Author

@tabatkins

So you are saying that this ...

#container:eq(min-width:1001px) {
   background: yellow;
}

... is technically impossible?

Bummer.

@tabatkins
Copy link
Contributor

Rather, that allowing that implicitly allows cases that are technically impossible.

@wnr
Copy link
Contributor

wnr commented Feb 9, 2015

Okay so we at least have a definite answer now, and are not stuck in the "we are talking about the same thing". For those of you that are unfamiliar with the constraints that Tab is talking about here is a mail conversation that I think explains it pretty good: http://markmail.org/search/?q=%22The+%3Amin-width%2F%3Amax-width+pseudo-classes%22#query:%22The%20%3Amin-width%2F%3Amax-width%20pseudo-classes%22+page:1+mid:i5abwvvbm6dakwxb+state:results

I thought this document was supposed to be solution-less and not conforming to implementation issues, but I guess it could be wise to have it doing so.

Shouldn't the document state the assumptions we make about constraints and the API?

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants