Skip to content
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

positioned elements provide the context #16

Open
scottkellum opened this issue Jul 16, 2020 · 3 comments
Open

positioned elements provide the context #16

scottkellum opened this issue Jul 16, 2020 · 3 comments

Comments

@scottkellum
Copy link

Element queries (and other element logic) should only be relative to the nearest positioned element. This gives authors more control over what the context is. It also slightly reduces, but not eliminates, issues of shifting contexts causing styling thrashing.

This is particularly useful for a few different scenarios:

  • It allows you to style inline and inline-block elements that might not return a reliable width to be queried.
  • It allows you to create a component and style the internal layout based on that component’s sizing properties. Especially helpful if the internal layout is split into two or more columns but you still want to style based on the component width.
@tomhodgins
Copy link
Owner

tomhodgins commented Jul 16, 2020

Hi @scottkellum, thanks for the suggestion. I don't think I've seen this idea or an implementation that works like this before so it seems like you've discovered something useful :D

I'm not sure of the use cases or demos, or if this is related to element queries or a slightly different kind of query, but I have some brainstorms about prototyping it so we could play around with it. Do you have any implementations of this already?

One thing I'm not 100% sure how I'll do is resolve which element is the containing block for a given element, I'm not sure if this exists somewhere JS can check already, or if we'd have to describe logic like this.

Would this explain what you're looking for, if it worked?

<!DOCTYPE html>
<body>
  <sometimes-container>
    <absolute-child></absolute-child>
  </sometimes-container>
</body>
* {
  box-sizing-border-box;
}
body {
  margin: 0;
}
sometimes-container {
  display: block;
  margin: 100px;
  padding: 100px;
  border: 5px dotted red;
}
absolute-child {
  position: absolute;
  display: block;
  width: 50px;
  height: 50px;
  top: 0;
  left: 0;
  border: 5px dotted green;
}

@media (min-width: 800px) {
  sometimes-container {
    position: relative;
  }
}

/* when the containing block of <absolute-child> is 700px+ wide */

/* custom vanilla CSS (must parse yourself) */
@--containing-block absolute-child and (min-width: 700px) {
  :--self {
    background: green;
  }
}

/* off-label browser-supported CSS (parser-free client-side) */
@supports (--containing-block(absolute-child and (min-width: 700px))) {
  [--self] {
    background: green;
  }
}

/*
  In this stylesheet, the <absolute-child> would have a green background when the browser was
  700px -> 800px wide when the containing block would be the <body> tag. But once the media
  query kicks in, the containing block becomes <sometimes-container>. The containing block
  query would be active again when the browser was 900px+ wide when <sometimes-container>
  is wide enough.
*/

I bet I could write a CSS parser plugin that would extract any @--containing-block or @supports (--containing-block()) queries, and extract the selector and media query conditions, as well as the rules inside with :--self or [--self] selectors included for scoped styles.

Since this feature can only be supported at runtime I think instead of parsing the media queries myself, to prototype it I'd re-use window.matchMedia's ability to test media queries, so for each element in the document matching one of these queries I'd locate its containing block element and resize a hidden <iframe> to the same dimensions, and then check the extracted media query syntax against the same <iframe> for all queries (changing its size each time something new needs to be measured).

Then in cases where all media queries came back true, we can populate a <style> tag in the document with a copy of the rules contained inside, and we'll add an attribute to the matching element like data-containing-block-absolute-child-min-width-700px=0 and replace :--self or [--self] in the CSS output with [data-containing-block-absolute-child-min-width-700px=0] so those rules are scoped only to the relevant elements on the page.

You could also work with CSS rules like this client-side in browsers today the @supports way, and also skip the parsing step and just look for these rules in CSSOM, and then all you need to do is split the selector and the media queries apart and feed it to the same runtime :)

Would having a custom prototype like this make it easier to explore and build use cases for a feature like this?

@scottkellum
Copy link
Author

I was thinking of it a little differently. Rather than an extension of what you have in your spec, it could be something like this:

<div>
  <h1>hello world!</h1>
</div>
div {
  position: relative;
}
@element (min-width: 500px) {
  div:self {
    background: blue;
    padding: 3rem; /* ignored */
  }
  h1 {
    float: left;
    width: 50%;
  }
}
  • Contexts are defined by positioning an element. This is similar to how z-index stacked contexts work as well as top, bottom, left, right positioning works. We already have the mental model of these being contexts in CSS.
  • No box model properties can be modified within an element query on :self elements. This avoids most of the recursive issues the w3c has been complaining about.
  • Children styles are more portable to other components. The h1 defined here responds to any context that is 500px.

Additionally, I have mostly been thinking about this in the context of writing a spec for Typetura. Introducing a new property called flow or something that binds CSS keyframes to a property of a container. This binds to width, height, or scroll (similar scroll spec, that I don’t particularly like as it stands now).

flow: animation name bound to maximum minimum:0 ease:linear fill-mode:both repeat:1;

h1 {
  flow: h1 width 1200px ease-out;
}

@keyframes h1 {
  0% {
    font-size: 1rem;
    color: #000;
  }
  100% {
    font-size: 5rem;
    color: blue;
  }
}

@scottkellum
Copy link
Author

Also I’m happy to have a video chat about this as I know it’s sometimes hard to talk through conceptual ideas in threads. And then bring notes back to the thread.

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

No branches or pull requests

2 participants