Container Query Round 2

July 16, 2020

If you're impatient, check out the CodePen.

Since I've last written about container query with width support - aka Unholy Albatross, I've been busy heads down working on my RazorGrid CSS Grid System that is based on CSS calc() and custom properties.

Self plug aside, I've decided to revisit container grid and see if I can integrate the functionality into RazorGrid, and discovered two things.

  1. The code can be further simplified.
  2. Vertical re-ordering is almost impossible.

Simplified Code - Power of clamp()

Why did I want to simply it? Because due to way the grid system is built I've already commandeered width and max-width which depends on selectively invalidating them to switch to default values - auto and 0 respectively. This means I cannot alter those two properties because any max() operator I added will be completely invalidated.

This is when I remembered clamp(), thus the even more compressed (and less intrusive) version is born:

min-width: clamp(var(--min-width, 0px), (var(--breakpoint) - 100%) * 999, 100%);

It bolts onto min-width to give you container query in one simple line. You are free to alter the width of the item without affecting container query functionality.

Vertical Ordering Woes

However, in the end this is only a hack, not a true query, we can only change the width, this means we cannot change the ordering of the columns when they stack.

A common use case is to raise the right hand column to the top when stacking columns vertically, but changing the order is not possible here.

The only value that acts as the trigger is the width value such as (40rem - 100%) * 999, we can't perform any operation with it regarding the order property because order only wants integer, and our trigger value is a width with units. No conversion can be done.

This limitation really hurts the usefulness of this technique.

Yet Another Hack

Not all hope is lost, however.

flex-direction:row-reverse has a curious property - the X direction is reversed, but the row still wraps downwards, this means when columns are 100% wide it behaves like flex-direction:column.

Now my first instinct is that I need to reverse the source ordering of the columns and apply flex-direction:row-reverse to it so it shows up normally, and when the column stacks the order is automatically reversed.

But touching source ordering feels dirty.

So as an alternative, I reversed the order via CSS:

.v-reverse {flex-direction:row-reverse}
.v-reverse > *:nth-child(1) {order:-1}
.v-reverse > *:nth-child(2) {order:-2}
.v-reverse > *:nth-child(3) {order:-3}
.v-reverse > *:nth-child(4) {order:-4}
.v-reverse > *:nth-child(5) {order:-5}
.v-reverse > *:nth-child(6) {order:-6}
.v-reverse > *:nth-child(7) {order:-7}
.v-reverse > *:nth-child(8) {order:-8}
.v-reverse > *:nth-child(9) {order:-9}
.v-reverse > *:nth-child(10) {order:-10}
.v-reverse > *:nth-child(11) {order:-11}
.v-reverse > *:nth-child(12) {order:-12}

By flipping the order twice, I was able to keep the original source order while also apply the flex-direction I need.

The downside is I need to manually set the order for the flex items, but realisticly I don't foresee container query, at least in its current limited iteration, being used for a container with grid-like layout, so setting the order for 12 children should be enough for most use cases.

This technique only solves one common use case and precise control of vertical order is still impossible, so caveat emptor.

Closing Thoughts

Container query in it's current form - the CSS calc/clamp() hack, is extremely limited.

There is no true trigger to switch styles, so you are at the mercy of one set of styles for both horizontal and vertical stacks, this severely limits it's applications.

Also, after trying out container query in a grid system I found that having too many container queries on a page will lead to funky proportions due to unpredictable mix of collapsed and uncollapsed columns that violates good design principles.

I think what everyone thought was they can define breakpoints for components and just be able to throw everything on a page and expect them to work.

Does it? Well it depends.

It works on the component level, but on a page level, you end up with chaotic, haphazard layout with unpleasing proportions, with the major problem being you cannot alter the container size when it collapses.

For example, for a nested column like so:

+-----------------+  +-----------------+
|                 |  | +-----+ +-----+ |
|                 |  | |     | |     | |
|                 |  | |     | |     | |
|                 |  | |     | |     | |
|                 |  | |     | |     | |
|                 |  | |     | |     | |
|                 |  | |     | |     | |
|                 |  | |     | |     | |
|                 |  | |     | |     | |
|                 |  | +-----+ +-----+ |
+-----------------+  +-----------------+

When the inner columns stack, ideally you would want this:

+-----------------------+  +-----------+
|                       |  | +-------+ |
|                       |  | |       | |
|                       |  | |       | |
|                       |  | |       | |
|                       |  | +-------+ |
|                       |  | +-------+ |
|                       |  | |       | |
|                       |  | |       | |
|                       |  | |       | |
|                       |  | +-------+ |
+-----------------------+  +-----------+

But without being able to affect the width, you can only get this:

+-----------------+  +-----------------+
|                 |  | +-------------+ |
|                 |  | |             | |
|                 |  | +-------------+ |
|                 |  | +-------------+ |
|                 |  | |             | |
|                 |  | +-------------+ |
|                 |  +-----------------+
|                 |
|                 | 
|                 |
+-----------------+

What ends up happening is the design isn't broken, but it isn't great either - it is stuck in a limbo of mediocrity.

It's very much a Catch-22 - you cannot ignore site width without also become decoupled from the overall site design.

Thus, it's much better applied sparingly to specific elements if design cohesiveness is a priority.