WICG / cq-usecases

Use cases and requirements for standardizing element queries.
Other
184 stars 24 forks source link

Document height-based CQ use case #63

Open keithjgrant opened 6 years ago

keithjgrant commented 6 years ago

Following this discussion, document a use case with a height-based CQ. A banner with min-height specified in vh units, with three child elements.

If the banner element is beneath a certain height, the three elements should appear inline. If it is above a certain height, they can stack various ways.

beep commented 6 years ago

Pinging #60. @keithjgrant, assigning to you for the initial draft.

keithjgrant commented 6 years ago

Correction: don't use vh. Make it instead a grid item as part of a larger layout. The contents of the rest of the grid can affect the size of the banner element.

(If the layout is defined using vh, then regular media queries could solve the problem)

keithjgrant commented 6 years ago

I’m having a tough time really nailing this down. Every time I come up with a scenario, it’s clearly contrived, and I can think of a better layout that would make more sense.

Here’s kind of what I’ve got, maybe bouncing some ideas around might help...


Page layout using flexbox and flex-direction: column. There are three flex items stacked atop one another: a header, a banner, and a main content area.

The flex container has a min-height: 100vh, so it's stretching to fill the viewport. The header has an auto height of, say 200px, so it's basically a fixed/known size. The banner and the main content area are flex: 1 1 0, so they will grow to fill the height of the container.

Contents inside the banner and main are laid out using grid or flex or whatever. The banner's contents are the colored boxes; the main consists of the three gray boxes beneath.

vertical-cq-1

Then, assume the contents in the main area become longer. This means it will take up more of the screen height, leaving less for the banner. In this case, we would like the banner to lay out differently, so it requires less space:

vertical-cq-2

keithjgrant commented 6 years ago

This is my current permutation. My devil’s advocate objection right now is this: Why is the flex-basis set to zero for the banner and the main area? It really should be auto, which means the height of the banner would be determined by its contents, and it wouldn’t become shorter than our CQ.

I could force the wrapping flex container to a height of 100vh (rather than a min-height)… but that’s really not a great practice.

beep commented 6 years ago

@keithjgrant, I think this is a stellar start. A couple quick questions from me:

keithjgrant commented 6 years ago

I’m not entirely convinced height-based CQ’s are necessary, or at least not as necessary as width-based ones. This has been a sort of thought experiment for me to figure out how I would use them, but I’m jumping through hoops to generate a plausible scenario. I’m beginning to suspect I wouldn’t ever use them, personally.

keithjgrant commented 6 years ago

Now that I said that, I realized I do have a real-world app where I want to constrain the height. https://sidecar.us is built primarily to work on mobile as a PWA.

I’ve worked to keep the home screen at 100vh tall. Currently, it can overflow, if content gets too long, but I’m not too happy about it. Maybe I’ll noodle on that as a different starting point.

beep commented 6 years ago

Now that I said that, I realized I do have a real-world app where I want to constrain the height. https://sidecar.us is built primarily to work on mobile as a PWA.

I think focusing on a real-world example could be a really great starting point! The way I think of it, we’re trying to demonstrate an existing need here; we don’t have to be responsible for inventing new ones, if that makes sense?

Possibly related: I thought @eeeps had a marvelously insightful comment in Slack, about the distinction between features and use cases:

I know I sound like a broken record, but we need to be figuring out what features like children queries or queries directly on images are actually useful for - like on the level of specific common layouts or workflows or roadblocks people are running into over and over again. “Responsive, portable, self contained ads” is a use case - number of children queries or queries on inputs isn’t. Without use cases, those are solutions looking for problems

(Only sharing it here if it’s helpful to you. Please disregard if it isn’t!)

keithjgrant commented 6 years ago

I think what I’m finding when it comes to height is not so much that I want to query the height of the container so much as I want to be able detect overflow and take steps to shrink things down in attempt to prevent it. Would that still fall in the scope of what we’re doing here?

beep commented 6 years ago

@keithjgrant Hmm! I’m not sure.

My gut read says that, yes, it’s probably out of scope. This feels like a more general property status query, which is highly rad, but mayyyyybe not what we’re after?

But—but!—I don’t want to preemptively axe a possibly stellar use case. I’d say keep working at it, if you think it has merit? In other words, if you think there’s a common pattern or problem here that container queries might be able to solve, let’s get it written up!

eeeps commented 6 years ago

Just popping by to say that @keithjgrant it’s super-cool to get to read you thinking concretely though a possible use case and finding out that you didn't really want what you thought you wanted. That's the Use-Cases-first system at work! (;

When you say “overflow” – do you mean overflow within a DOM element or overflow of the viewport?

If the viewport, that would be an awesome media query! Not a container query though.

If you want to be able to query DOM nodes though... hm. If we come up with a general way to query facts about elements (in the same way that media queries let you query all sorts of disprate things about a browsing context), this sounds totes-in-scope to me – with two big caveats:

  1. Being able to query whether or not an element is overflowing its height or width, and adjusting its descendants (so that it won't be overflowing anymore) in response, seems to be positively fraught with circularity.
  2. The “width query” use cases seem to be what everybody wants and has always wanted. To solve them (and their inherent circularity issues), we may not end up with a general querying mechanism of the sort that I’ve posited above. I don't think that overflow queries are important enough to block work on width queries. If it ends up looking like they might – I will be the first to push them overboard, out of the scope boat and into the sea.
tomhodgins commented 6 years ago

I do think there's a use case for a height condition when element queries, though it will be much more rare that you would use it than width-based queries. Far more often than wanting to query height I've reached for other height (and width) informed features like querying the aspect-ratio or orientation of an element so I can style it or another element.

How about something like this as a use-case for height: a design that can have a variable amount of content in it, and depending on how tall/long that content ends up a designer may wish use the space differently. Perhaps it's something like a sidebar widget with a fixed width, so the more text content it contains the taller it will get.

This example isn't very pretty, but it works:

<div id=sidebar>
  <div class=widget>
    <h2>Widget Title</h2>
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p>
    <button>Click Me</button>
  </div>
  <div class=widget>
    <h2>Widget Title</h2>
    <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
    <button>Click Me</button>
  </div>
</div>

<style>
  * {
    box-sizing: border-box;
  }
  #sidebar {
    width: 300px;
    border: 1px solid;
    padding: 1em;
  }
  .widget {
    border: 1px solid;
    padding: 1em;
  }
  .widget + .widget {
    margin-top: 1em;
  }
  button {
    display: block;
    width: 100%;
    font-size: 20pt;
    padding: 2em;
  }
  @element .widget p and (min-height: 300px) {
    :self ~ button {
      font-size: 14pt;
      padding: 1em;
    }
  }
</style>

<script src=http://elementqueries.com/EQCSS.js></script>

screen shot 2018-02-28 at 8 53 26 pm

And on a related note, about overflow and visibility, I've made a few helper plugins for doing things like this as well if you want to see what they might look like, or how they differ from the element query plugins I've written.

Is that element partly or fully visible in the viewport?

// Viewport Visibility
function viewport(selector, option, rule) {

  let styles = ''
  let count = 0

  const features = {
    partly: (tag, rule) => {
      const top = tag.offsetTop - innerHeight
      const bottom = top + tag.offsetHeight
      return (scrollY < tag.offsetTop + tag.offsetHeight)
             && (top < scrollY)
             && (bottom < scrollY + tag.offsetHeight)
             ? rule
             : ''
    },
    fully: (tag, rule) => {
      const top = tag.offsetTop - innerHeight
      const bottom = top + tag.offsetHeight
      return (scrollY < tag.offsetTop)
             && (top < scrollY)
             && (bottom < scrollY)
             ? rule
             : ''
    }

  }

  document.querySelectorAll(selector).forEach(tag => {

    const attr = selector.replace(/\W/g, '')
    const evaluated = features[option](tag, rule)

    tag.setAttribute(`data-viewport-${attr}`, count)
    styles += `[data-viewport-${attr}="${count}"] { ${evaluated} }\n`
    count++

  })

  return styles

}

Demo: http://staticresource.com/viewport.html

Is that element in sight or out of sight vertically within an overflowed parent?

// Visible Vertically Within Parent
function inSight(selector, rule) {

  let styles = ''
  let count = 0

  document.querySelectorAll(selector).forEach(tag => {

    const attr = selector.replace(/\W/g, '')

    let underTop = tag.offsetTop + tag.offsetHeight >= tag.parentElement.scrollTop
    let aboveBottom = tag.offsetTop < tag.parentElement.scrollTop + tag.parentElement.offsetHeight

    if (underTop && aboveBottom) {

      styles += `[data-insight-${attr}="${count}"] { ${rule} }\n`
      tag.setAttribute(`data-insight-${attr}`, count)
      count++

    } else {

      tag.setAttribute(`data-insight-${attr}`, '')

    }

  })

  return styles

}
// Out of Sight Vertically in Parent
function outOfSight(selector, rule) {

  let styles = ''
  let count = 0

  document.querySelectorAll(selector).forEach(tag => {

    const attr = selector.replace(/\W/g, '')

    let aboveTop = tag.offsetTop + tag.offsetHeight < tag.parentElement.scrollTop
    let underBottom = tag.offsetTop > tag.parentElement.scrollTop + tag.parentElement.offsetHeight

    if (aboveTop || underBottom) {

      styles += `[data-outofsight-${attr}="${count}"] { ${rule} }\n`
      tag.setAttribute(`data-outofsight-${attr}`, count)
      count++

    } else {

      tag.setAttribute(`data-outofsight-${attr}`, '')

    }

  })

  return styles

}

Is that element horizontally overflowed on the left and/or right side?

// Horizontal Overflow
function overflow(selector, option, stylesheet) {

  let styles = ''
  let count = 0
  const features = {
    left: tag => 0 < tag.scrollLeft,
    right: tag => (tag.scrollLeft + tag.offsetWidth) !== tag.scrollWidth
  }

  Array.from(document.querySelectorAll(selector)).forEach(tag => {

    const attr = (selector+option).replace(/\W/g, '')

    if (features[option](tag)) {

      styles += stylesheet.replace(/:self|\$this/g, `[data-overflow-${attr}="${count}"]`)
      tag.setAttribute(`data-overflow-${attr}`, count)
      count++

    } else {

      tag.setAttribute(`data-overflow-${attr}`, '')

    }

  })

  return styles

}

Demo: http://staticresource.com/overflow.html

eeeps commented 6 years ago

@keithjgrant and @tomhodgins —

I think both of your examples focus on querying boxes whose height is being determined by their contents. In other words, they are intrinsically sized.

The container queries use cases that seem to come up when people talk about “width” queries are all about elements whose width is entirely dependent on their context. Which is to say, they are extrinsically-sized.

You can, of course, have an element whose “height” is extrinsically-sized. Think, for instance, of a height-constrained, left-to-right-scrolling image carousel. That height might be fluid and dependent on context, just as the widths of components that we normally talk about are - in this case, I think “height” queries make all the sense in the world.

But the examples in this thread are of the other thing: querying elements based on their intrinsic size. Which seems like a whole other kettle of fish...

Container queries help break circularity w/r/t extrinsically-sized boxes because they limit the ways in which a boxe's contents (children) can affect the thing-queried (container size).

When dealing with intrincally-sized boxes, you’d... almost need the opposite constraint? You could only query children and apply styles on containers!?

...aaand it looks like once again, I have strayed far afield from use cases, and jumped ahead to the how-could-this-work talk. Doh. Back to buisness...

@keithjgrant , it seems as if you explored the use cases for intrinsic-size queries a bit, and found what you actually wanted was an is-this-overflowing-query? @tomhodgins – that button example looks like it could be the start of something promising. Have you actually used this sort of query anywhere, or wanted to, or seen it used?

andykirk commented 6 years ago

@eeeps beat me to my main point, but here's my 2p:

In most scenarios, and by popular convention, we choose width as the limiting factor, opting to allow vertical scrolling if content > screen height.

However, it's perfectly possible, though unconventional to choose height as the limiting factor, opting to allow horizontal scrolling if content > screen width. If we choose this, we're flipping the axis so it seems logical to me that many use-cases for width-based queries (and there are plenty) becomes use-cases for height-based queries.

Example:

Vasilis presents a horizontal layout in his talks (18:48) and a live example

I've also mocked up a similar kind of thing.

Where there's enough height, I want 2 rows, where there isn't, only 1. My understanding of Grid may be underwhelming here, but it doesn't seem possible to me to do this without querying the height of the container. I've used buttons to switch between the two sets of layouts, but you should get the idea - these really should be adjusting in response to the height of their containers.

No doubt this example could be greatly improved if need be.

keithjgrant commented 6 years ago

@eeeps that sounds like a fair summary. Technically, I guess the overflowing query is a little bit both intrinsic and extrensic: does the intrinsic size exceed some extrinsic constraint.

I basically want to short-circuit any circularity. If element X overflows, I want to take mitigating steps to prevent it. I would most likely shrink the content, hopefully making the overflow query evaluate to false; but of course I wouldn’t want it to then revert back to the initial layout. I don’t want it to evaluate circularly. In this case, if there’s any circularity, I want it to be disregarded and for my query to take effect.

I’m not sure whether that is the same behaviour we want in a width-based query or not.

eeeps commented 6 years ago

An interesting, possibly-related use case, re: intrinsic size queries: https://github.com/marcj/css-element-queries/issues/223#issuecomment-378139991

@7iomka wanted to make the intrinsic size of some text depend on... the text's intrinsic size. Which introduced an infinite loop.

I get the use case (shrink the text rather than have it linebreak); dunno if container queries are the hammer for that nail.

beep commented 6 years ago

Oh, hm!

So personally, text sizing and weight are two of the biggest things I’d use container queries for. (Just for example, if an image+title pattern appears in both, say, a main content well and in a sidebar, I’d expect the title to be more prominent in the former.)

That said, I’d think about that as something under the domain of container queries—here’s the space available for this module, let’s adjust the internals accordingly—rather than querying the property itself. If that makes sense? [sips coffee]

andykirk commented 6 years ago

Off topic? This thread is about height-based queries, no?

eeeps commented 6 years ago

@beep makes total sense. And yep! I'm not trying to say that adjusting text styles is problematic. Rather, I'm just (weirdly?) excited to find a real-life example of someone trying to do something related to @keithjgrant’s use case – adjusting the style of some content so that it doesn’t overflow (well, line break within) a container.

Can you use a tool like container queries to mitigate overflows/line breaks? I don't know yet! But I was interested to see somebody trying and failing in real life.

As you suggest, as long as the query target can't be influenced by the style target, there's no problem.

@andykirk, as I see it, the thread’s a place for @keithjgrant to hash out a use case. In my head, that use case started as “height-based queries” and has developed into something along the lines of overflow queries or intrinsic size queries. But I may very well be wandering off on my own, there. At the very least, given your + @beep’s responses, I should have been more explicit about how I thought the linked example tied back to Keith’s use-case-in-progress.

andykirk commented 6 years ago

@eeeps don’t mind me. I was just pointing it out in case...

eeeps commented 6 years ago

Re: detecting overflow – I can't wait to see the video, but from what I can gather from just the slides, the IntersectionObserver section of Greg Whitworth’s CSS Day talk container queries, here, is surely relevant: https://noti.st/gregwhitworth/UDul7E/over-the-moon-for-container-queries

Stepping back – @keithjgrant , given your explorations and all of the discussion they spurred, where are you at with all of this? Seems like we forked off into a few different sub-discussions about how various concepts related to your original exploration could work... but may not really any closer to a use case. Is there enough here for you (or anyone else!) to turn into a PR? If not I’ll go ahead and close the issue.