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

Need a section outlining limitations of current techniques #53

Closed
eeeps opened this issue Jan 28, 2018 · 36 comments
Closed

Need a section outlining limitations of current techniques #53

eeeps opened this issue Jan 28, 2018 · 36 comments
Assignees

Comments

@eeeps
Copy link
Collaborator

eeeps commented Jan 28, 2018

Like this, but for Container Queries.

@andykirk, who wrote an excellent article outlining a bunch of use cases and current-workaround-techniques, has agreed to give this a go, soonish (I’ll assign him as soon as he accepts the collaborator invite).

@ZeeCoder
Copy link
Collaborator

That's great!
I'll explore this area myself a little more as I prepare for my talk, so I'll leave here ideas for @andykirk .

@andykirk
Copy link
Collaborator

Thanks both.

@andykirk
Copy link
Collaborator

andykirk commented Feb 4, 2018

Hi,
Apologies for the delay in this. I hope to have a draft in the next few days.
Ok to post it here for comment?

@ZeeCoder
Copy link
Collaborator

ZeeCoder commented Feb 4, 2018

Yeah, I'll get a notification then

@marcoscaceres
Copy link
Contributor

Once we have some initial text, we should quickly translate it to "bikeshed" and start doing detailed review as a Pull Request. BikeShed's syntax is a bit of a burden of entry, so the quicker one of the folks editing the translate the incoming text, the better.

@tomhodgins
Copy link
Collaborator

I can get the text we come up with into a bikeshed document, but I might need a little help with Github PR stuff as we get going :D

@marcoscaceres
Copy link
Contributor

Sure, no problem. Happy to provide help with the PR stuff. Key is that we have a clean commit history and make sure we track who made the contributions.

When sending the PRs, particularly for ones like this one, we need to make sure that the author of the text is captured correctly, so:

git commit --author="Andy Kirk <andys_real_email_here@somwhere.com>"

@tomhodgins
Copy link
Collaborator

@andykirk When you are ready to submit text to be added, it seems I'll need some information for proper authorship attribution ➸ how would you like me to reference you?

@andykirk
Copy link
Collaborator

andykirk commented Feb 5, 2018

Thanks Tommy.

Andy Kirk andy@andykirk.net

@andykirk
Copy link
Collaborator

andykirk commented Feb 7, 2018

Hi,
Here's a start. Apologies for the delay - I've struggled with this somewhat (both with time and subject). I've taken a few liberties with the definition - it's more difficult working outside of the context of the full document - but anything here can be changed, of course. I hope this is somewhere along the right lines.


Limitations of current techniques

There is currently no native CSS mechanism available to developers that achieves the primary function of container queries, which is to employ the full capabilities of CSS to a specific element tree based on the state and properties of their container. Therefore developers must look to workarounds to achieve their goals. These workarounds and their limitations are outlined here:

The use of Media Queries can only affect CSS changes based on the state and properties of the viewport. The viewport is not a good indicator of the state and properties of a particular container within the viewport.

The use of CSS layout methods such as Flexbox and Grid, alongside CSS-based workarounds (such as the Fab Four technique) cannot employ the full capabilities of CSS on an element tree.

The use of iframes produces the closest effect to what Container Queries is intended to achieve, but their use has two significant drawbacks:

  1. Without the use of JavaScript, iframes require that the contents of the iframe are stored in a separate HTML document rather than being part of the current document. This necessitates a new document composition paradigm that's very different from the methods currently employed by developers.

  2. The contents of an iframe do not natively affect the dimensions of the iframe itself. The contents of a container that is the subject of a Container Query are still expected to be able to affect the layout of its parents in the opposite dimension of the query. For example, in the case of a Container Query that is based on a containers width, it is still expected that the container's contents affect the height of a container, subject to any overflow rules.

JavaScript-based solutions (for example EQCSS) should not be considered an adequate replacement for a native CSS solution for all the same reasons any other layout- or appearance-based declarations are the domain of CSS, not JavaScript, and the usual caveats of JavaScript availability and failure still apply.

@jpamental
Copy link

This is an excellent overview @andykirk !

@andykirk
Copy link
Collaborator

andykirk commented Feb 7, 2018

Thanks @jpamental

@beep
Copy link
Collaborator

beep commented Feb 7, 2018

@andykirk This is excellent. Thanks so much for writing this up!

Out of curiosity, have you considered mentioning any performance drawbacks of JS-driven solutions? (I know @eeeps has done a little research into this.) I’m only asking because @marcoscaceres mentioned during the kickoff call that issues impacting end users are really compelling to implementers, and speed/performance seems like it could be a good one to hone in on.

What do you think?

@andykirk
Copy link
Collaborator

andykirk commented Feb 7, 2018

Thanks @beep - no, actually I hadn't. It's not an area I'm strong in, tbh.
It's a good shout though - perhaps someone with more knowledge of that are than myself can add something along those lines?

@ausi
Copy link
Collaborator

ausi commented Feb 7, 2018

@eeeps and I did some performance research on my container query script in ausi/cq-prolyfill#39. I can try to write something (short) up that describes the minimum performance costs of a javascript solution.

@beep beep assigned ausi Feb 7, 2018
@beep
Copy link
Collaborator

beep commented Feb 7, 2018

@ausi Marvelous, thank you! Adding you as a co-assignee.

@ausi
Copy link
Collaborator

ausi commented Feb 7, 2018

Minimum Performance Cost of a Container Query Solution via JavaScript

On first render:

  • About 3KB or more additional network traffic.
  • About 6KB or more (minified) JavaScript code to parse.
  • Downloading and parsing needs to be render blocking to avoid FOUC.
  • At least one additional compute style step.
  • At least one additional layout step.

After every subsequent render that affects the matching state of at least one container query:

  • At least one additional compute style step.
  • At least one additional layout step.

Or shorter: The minimum performance cost of a JavaScript solution is about 6KB more render blocking JavaScript and a reflow after every significant layout.


According to my research these should be the absolute minimum costs of any JavaScript solution (most scripts cost more performance-wise). The KB numbers are taken from the most popular scripts.

Does this help so far, or should it me more detailed?

@jpamental
Copy link

I think that including @ausi 's addition is good, but I think that the stronger point is that layout is the domain of CSS, not JS (which is well known, and by design). So adding the 'and JS would be bad for performance too' should be a secondary reinforcing case.

@ausi
Copy link
Collaborator

ausi commented Feb 7, 2018

I fully agree.
Layout should not depend on JavaScript. This should be the main concern.

@tomhodgins
Copy link
Collaborator

I'm curious about these numbers:

  • About 3KB or more additional network traffic.
  • About 6KB or more (minified) JavaScript code to parse.

Is that 3kb for the gzipped plugin code? Are stylesheets included in that? And for 6kb are we saying the minimum size of a plugin for this is 6kb minified (but uncompressed)?

I just tossed my most recent stylesheet loader and element query plugin into Closure Compiler and minified it and I get <2kb uncompressed, 764 bytes gzipped, and this wasn't written with the intention of being small. Should these numbers be adjusted downward, or better explain what they cover in their estimate?

@jpamental
Copy link

You make a good point @tomhodgins - I think that if we are going to include a performance consideration it's probably worth not talking specific numbers, but rather focus on the fact that it does require additional request, download, and processing resources as @ausi laid out above. Best to not get specific where not needed.

@ausi
Copy link
Collaborator

ausi commented Feb 7, 2018

Great point.

I'm curious about these numbers

They are an average size of some general purpose container query scripts. There is no real minimum for the size because we didn’t specify the use cases yet and solutions can be very different.

it's probably worth not talking specific numbers, but rather focus on the fact that it does require additional request, download, and processing resources

I think you are right, we should probably remove these numbers. My intention was to note that the required scripts are huge in many cases ☺️.

How about this:

  • About 3KB or more additional network traffic for JavaScript.
  • About 6KB or more (minified)Additional JavaScript code to parse.

@beep
Copy link
Collaborator

beep commented Feb 7, 2018

I think that if we are going to include a performance consideration it's probably worth not talking specific numbers, but rather focus on the fact that it does require additional request, download, and processing resources as @ausi laid out above. Best to not get specific where not needed.

Strong agree!

@ausi, I love your condensed list. Just me, but I think that’s the perfect level of detail for now.

@marcoscaceres
Copy link
Contributor

What's the impact per element to which the JS is then applied (and what's a typical number of element to which CQ would be applied)? Does the browser then need to recalculate styles once or for each?

@tomhodgins
Copy link
Collaborator

tomhodgins commented Feb 8, 2018

This is where the separation between ' virtual stylesheets' and 'events' makes a difference.

In some plugins, all element queries are treated as a separate stylesheet, and all stylesheets are reprocessed upon each event (commonly load, resize, input, and click on the window object) In this instance, all styles get refreshed and trigger repaints any time any event that triggers a reprocess is fired. EQCSS works like this.

Other stylesheet loaders register specific queries to events on specific elements in the document (either through browser events, or observers like Resize Observer or Mutation Observer). In this case, only those stylesheets tied to specific events on specific elements in the document would be repopulated. ReproCSS is like this.

Lastly, it's possible to load event-driven virtual stylesheets on global events, or element-specific events, but only to repopulate the stylesheets if they are different than the current styles, which also saves repaints. My latest jsincss plugin works like this, so it should be a little better than one that updates that the same styles verbatim just because an event was triggered.

As for the number of elements affected by each element query - often it's one (the same being queried), sometimes it's a handful (the element plus some children), but rarely is it more than 5 different selectors in one query.

EDIT: ^ those numbers are for selectors, not # of elements matching those selectors in the document. Often 1 selector matches 1 element in the document, but it can be multiple. That's why 'style scoping' or 'selector scoping' is important, so you don't have to use ID's or unique class names to target a matching element :D

@ausi
Copy link
Collaborator

ausi commented Feb 8, 2018

What's the impact per element to which the JS is then applied?

An optimized CQ script should not have a significant impact per element. And for determining the minimum performance cost we should assume an optimal JS implementation I think.

(and what's a typical number of element to which CQ would be applied)

This can be anything from one single element up to thousands of elements.

Does the browser then need to recalculate styles once or for each?

This also depends on the actual JS implementation. But in the optimal case one recalc/reflow should be enough (even for thousands of elements). More recalcs/reflows might be needed if the CQ elements are nested, but this strongly depends on the actual use case.

This is where the separation between ' virtual stylesheets' and 'events' makes a difference.

Many CQ scripts don’t use “virtual stylesheets” at all, they just switch classes on and off (or custom attributes). In the case of “virtual stylesheets” CSS parsing would have to be added to the performance cost. But for the minimum cost this is not relevant I think.

@tomhodgins
Copy link
Collaborator

Iframes as Mini-Viewports

Iframes allow you to 'modularize' some HTML by embedding it in an HTML document as another HTML document. This lets you use media queries for 'modular' styles in a way similar to writing container queries.

There is at least one plugin that uses iframes to contain self-responsive 'modules' as part of a larger site layout.

Pros

  • great for quickly previewing parts of HTML layouts at different widths during design & development
  • excellent for previewing a 'module' at different widths in a document like a style guide: where performance matters little, the widgets most likely have dummy content and aren't fully interactive, and the page doesn't have many 'modules' on it at the same time

Cons

  • Iframes are more than just style containers—they are each entirely new documents of their own, with all events documents have. And that extra overhead adds up quickly
  • Iframed 'modules' either require additional JavaScript code to communicate with the outside page around them and other iframed 'modules' on the same page, or else they require a way to communicate with each other through a web server backend

Element Queries are not Syntactic Sugar for Media Queries

The most-used feature of media queries is the ability to set width-based breakpoints based on the browser's viewport. Most container queries are also based on an elements width. In non-dynamic layouts, where the width of 'modules' in the layout ultimately end up tied (even indirectly) to the viewport's width, it may be possible to predict or pre-render container queries and express the same styles as media queries.

There is at least one plugin that can convert container queries to media queries.

Pros

  • In some use cases, allows developers to build with container queries and convert to CSS media queries, avoiding the use of a JS plugin in the browser

Cons

  • Doesn't work for dynamic layouts (e.g. a hiding sidebar that affects the width of 'modules' in the layout without changing the viewport's width) or for layouts with new elements added after the container queries have been compiled

This is from the element query test file for Qompile, where a comment explains what kinds of element querying functionality (beyond element width) this feature might be suitable for: https://github.com/tomhodgins/qompile/blob/master/test/src/element.jic#L3&L18

These element queries have nothing to do with width, but extend the power of CSS selector - as long as they don't need to be used with interactive content. This means they aren't suitable for:

  • interaction-based pseudo-classes like :hover, :focus, :active, etc
  • queries on interactive content, like inside form elements
  • dynamic HTML that will in structure after the page loads
  • scroll position or dimensions

Where it might be useful:

  • select by text content length
  • select by regex search of text content
  • select by string search of text content
  • select direct parents of a CSS selector
  • select ancestors of a CSS selector
  • select previous sibling of a CSS selector
  • select first instance of CSS selector in DOM
  • The technique is not practical as a 'lossless' conversion, even for layouts where it works. To convert container queries to media queries in a 'lossless' way you would need to pre-render the page in a browser at 1px width increments, recording what you observe as you apply the container queries, making necessary changes to the HTML, and rendering out a CSS stylesheet that captures what you observed. Though this process can be optimized (e.g. CSS minification, media query consolidation, etc), the amount of CSS generated to convert container queries to media queries in a 'lossless' way is too large to be useful, in all but the absolute simplest use-cases

Note: for an extreme example of the impracticality of this technique for 'lossless' conversion, consider that this HTML:

<div>Hello</div>

And this stylesheet:

${containerQuery('div', el => el.offsetWidth <= 500, `
 :self {
   background: lime;
 }
`)}

When rendered in a 'lossless' way at every width between 0px wide and 1000px wide, turned into 66kb of rendered HTML & CSS: http://staticresource.com/compiled-demo.html

  • The technique is a 'lossy' conversion. The way that you can use this technique is by limiting the range of widths you observe. This leads to less 'fine' breakpoints the more 'modules' you have on the page

Note: For normal use-cases of this technique and a few examples of how the 'lossy' conversion from container queries to media queries might work, check out the Qompile examples: https://github.com/tomhodgins/qompile#examples

@marcoscaceres
Copy link
Contributor

This is starting to take shape, so it would be good to start moving some of the above into a couple of pull requests (otherwise, we risk this becoming a mega thread).

@andykirk
Copy link
Collaborator

Hi,
Just my opinion, but what @tomhodgins wrote above, though good, is quite verbose and to me it doesn’t look like it actually adds much in the way of new information to what I originally wrote. I’m not suggesting that what I wrote should be used as-is, but I based the length and level of detail on the responsive images equivalent that @eeeps linked to in the first post.

It would seem to me that we should be as concise as possible in order to make reading the doc as painless as as possible, and thus improve the chances of it being read and understood.

Just my 2p.

@marcoscaceres
Copy link
Contributor

Let's find a balance between the two. @andykirk let's use yours as the base of the pull request, and then @tomhodgins et. al can layer any additional information on top, if necessary... we can then chop chop chop words as needed.  

@beep
Copy link
Collaborator

beep commented Feb 12, 2018

Sounds great to me, @marcoscaceres.

@andykirk (and/or @ausi), feel free to open a PR to tackle this! We can continue the discussion there.

@andykirk
Copy link
Collaborator

Great, happy to. I’ll think add @ausi’s JS parsing and nework bits to the JS section.

Also, can I clarify; am I meant to be opening the PR on the bikeshed file? Sorry if that’s a dumb question. I haven’t used bikeshed before.

@marcoscaceres
Copy link
Contributor

Also, can I clarify; am I meant to be opening the PR on the bikeshed file? Sorry if that’s a dumb question. I haven’t used bikeshed before.

Not a dumb question at all! but yes, on the BikeShed file. Don't worry too much about getting the syntax right at this point. That's what we have @tomhodgins for :)

@andykirk
Copy link
Collaborator

Great, thanks.

@andykirk
Copy link
Collaborator

Hi,
Do we still need this issue open now the PR has been merged?
Cheers

@beep
Copy link
Collaborator

beep commented Mar 16, 2018

@andykirk Oops, good catch! I’ll close this out.

(Apologies for the oversight. I’m traveling this week, and prep for the trip has kept me away from the CQ work. I’ll be back on it next week.)

Thanks again.

@beep beep closed this as completed Mar 16, 2018
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

8 participants