Feature Detecting CSS Flexbox Gap Support

In my post on Flexbox Gap I wanted to feature detect support for it using @supports. That however failed. As I then noted:

In the demo above I wanted to show a warning in browsers that don’t support flexbox gap. For that I tried using @supports, which has proven to be successful before.

.warning {
    display: block;
}

/* Hide warning in case browser supports flexbox gap */
@supports (display: flex) and (gap: 1em) { /* DOES NOT WORK AS INTENDED! */
    .warning {
        display: none;
    }
}

But that’s not really working as intended. In Chromium < 85 – which does not support flexbox gap – the warning is hidden, this because both conditions are evaluated separately:

  • Does Chromium < 85 support display: flex? Yes
  • Does Chromium < 85 support gap: 1em? Yes (from CSS Grid)

This makes me wonder whether re-using property names remains a good idea or not. Perhaps an extension to css-conditional should be made so that it supports a combination of properties — e.g. @supports (display: flex; gap: 1em)?

I’ve filed an issue for the latter.

~

Recently Ahmad Shadeed has encountered the same issue. Digging further into the issue – and looking for a way to detect it now – he finally turned to Feature Detection using JavaScript, with this little snippet borrowed from Modernizr:

function checkFlexGap() {
    // create flex container with row-gap set
    var flex = document.createElement("div");
    flex.style.display = "flex";
    flex.style.flexDirection = "column";
    flex.style.rowGap = "1px";

    // create two, elements inside it
    flex.appendChild(document.createElement("div"));
    flex.appendChild(document.createElement("div"));

    // append to the DOM (needed to obtain scrollHeight)
    document.body.appendChild(flex);
    var isSupported = flex.scrollHeight === 1; // flex container should be 1px high from the row-gap
    flex.parentNode.removeChild(flex);

    return isSupported;
}

With said function in place, you can then add a CSS class onto the html element to indicate if the combination of both Flexbox and Gap is supported or not. With some clever selectors in place inside your CSS, you can then act upon that.

How to detect browser support for Flexbox Gap →

Spacing grid/flexbox items in CSS with the gap property

Flexbox layout with CSS gap applied

The gap property for Flexbox about to land in Chromium 85. It allows you to define the size of the gutters between Grid/Flexbox children.

CSS Grid brought a delightful spacing feature called grid-gap, which quickly became a popular way to put space into layouts. It was easy to fall in love with because it took upon itself so much responsibility with such elegance. Starting in Chromium 85 grid-gap is now just gap, and you can use it with Flexbox. 💪

So no more grid-gap, but simply gap:

.layout {
-  grid-gap: 1em;
+  gap: 1em;
}

~

Why use gap (and not margin)?

Margins in flexbox don’t collapse, so when working with margin you’ll have to adjust the value you’re using. Because of this you’ll also get edges on the inside of your container, as the margin applied on the children is pushing the children inwards. Compare the screenshot below (using margin) with the one at the top of this post (using gap).

Flexbox layout with CSS margin applied
A margin of 1em is applied. Note the broader width between items and the extra blue edge on the container, all due to margins not collapsing.

~

Demo

See the Pen
Flexbox Gap
by Bramus (@bramus)
on CodePen.

Sidenote: @supports vs. Flexbox Gap

In the demo above I wanted to show a warning in browsers that don’t support flexbox gap. For that I tried using @supports, which has proven to be successful before.

.warning {
    display: block;
}

/* Hide warning in case browser supports flexbox gap */
@supports (display: flex) and (gap: 1em) { /* DOES NOT WORK AS INTENDED! */
    .warning {
        display: none;
    }
}

But that’s not really working as intended. In Chromium < 85 – which does not support flexbox gap – the warning is hidden, this because both conditions are evaluated separately:

  • Does Chromium < 85 support display: flex? Yes
  • Does Chromium < 85 support gap: 1em? Yes (from CSS Grid)

This makes me wonder whether re-using property names remains a good idea or not

Perhaps an extension to css-conditional should be made so that it supports a combination of properties — e.g. @supports (display: flex; gap: 1em)?

I’ve filed an issue for the latter.

~

Shorthand

gap is a shorthand for row-gap and column-gap:

.layout {
   display: flex;
   gap: 1em 2em;
}

The first value targets row-gap, the second value column-gap.

~

Browser Support

Firefox has supported it ever since version 63. Chromium is the first engine to follow its lead.

Data on support for the flexbox-gap feature across the major browsers from caniuse.com

💡 Shown above is a dynamic CanIUse.com image, showing an always up-to-date support table. By the time you are reading this browser support might have become better.

“Designing Intrinsic Layouts” by Jen Simmons

Twenty-five years after the web began, we finally have a real toolkit for creating layouts. Combining CSS Grid, Flexbox, Multicolumn, Flow layout and Writing Modes gives us the technical ability to build layouts today without the horrible hacks and compromises of the past.

In this hour-long talk captured live at An Event Apart DC 2019, Jen Simmons walks you through the thinking process of creating accessible & reusable page and component layouts.

Her YouTube Channel Layout Land, which is mentioned at the start, is worth a closer look if you’re looking for more CSS Grid/Flexbox/etc. related videos.

“Designing Intrinsic Layouts” by Jen Simmons – An Event Apart →

Relearn CSS layout with “Every Layout”

As just announced (on stage) at CSS Day by Heydon himself:

If you find yourself wrestling with CSS layout, it’s likely you’re making decisions for browsers they should be making themselves. Through a series of simple, composable layouts, Every Layout will teach you how to better harness the built-in algorithms that power browsers and CSS.

Employing algorithmic layout design means doing away with @media breakpoints, “magic numbers”, and other hacks, to create context-independent layout components. Your future design systems will be more consistent, terser in code, and more malleable in the hands of your users and their devices.

Relearn CSS layout: Every Layout →

CSS masonry with flexbox, :nth-child(), and order

Tobias Ahlin:

On the surface it seems fairly easy to create a masonry layout with flexbox; all you need to do is set flex-flow to column wrap and voilà, you have a masonry layout. Sort of. The problem with this approach is that it produces a grid with a seemingly shuffled and obscure order.

Flexbox has no easy way of rendering items with a column layout while using a row order, but we can build a masonry layout with CSS only — no JavaScript needed — by using :nth-child() and the order property.

Beware though: this technique requires you to set a fixed height on the wrapper, which is not always possible 😕

CSS masonry with flexbox, :nth-child(), and order

The Flexbox Holy Albatross

Great work by Heydon Pickering, in which he lets a flexbox layout respond to the size of its container, and not the viewport

Sometimes you want your items to wrap in a very particular way. For instance, when you have three items, you’ll be happy with the three-abreast layout and accepting of the single-column configuration. But you might like to avoid the intermediary part where you get a pair of elements on one line followed by a longer element underneath.

How do we skip this intermediary layout state and switch directly from a horizontal to vertical triptych? What’s the solution?

The solution he knocked up involves flex-basis being changed thanks to calc() and CSS Custom Properties (“CSS Variables”). The code behaves nicely when the viewport becomes too narrow, or when the container becomes too narrow (set the max-width of the body to 37em to see it in action)

💁‍♂️ To get a good understanding of how exactly this works, see Jonathan Snook’s Understanding the Albatross

Now, are these “Container Queries” we want? Not quite imho: whilst is does tackle the issue of layout for flexed children, it’s still not possible to, for example, change the font-size or, another example, switch from flexbox to grid using this technique.

Sidenote: I think one could hack the font-size thing together if CSS calc() were to support the modulo operator, but that’s not the case unfortunately. Also, it would still leave switching from flexbox to grid out of the loop.

The Flexbox Holy Albatross →
Understanding the Albatross →

Counting With CSS Counters and CSS Grid

Thanks to Counting With CSS Counters and CSS Grid my attention was directed to this fact comes to using CSS Counters:

The effectiveness of counter() wanes when we realize that an element displaying the total count can only appear after all the elements to be counted in the source code. This is because the browser first needs the chance to count all the elements, before showing the total.

So how can one fix this? In said article the author resorts to CSS Grid Layout to position the counters visually above the stuff it’s counting.

.wrapper {
  display: grid;
  …
}

.counters { 
  grid-row: 1; /* places the counters on the first row */
  grid-column: 1 / 3;  /* ensures the counters span the full grid width, forcing other content below */
}

This trick can of course also be reproduced with Flexbox, by reordering the items using its order property.

.wrapper {
  display: flex;
  flex-direction: column;
  …
}

.counters { 
  order: 1; /* places the counters on the first row */
}

💁‍♂️ CSS Counters is one of the 9 Underutilized CSS Features

Vertical margins/paddings and Flexbox, a quirky combination

In CSS, percentage-based paddings are – as per spec – calculated with respect to the width of an element. However, for flex items (e.g. items whose parent have display: flex; applied) that’s not always the case.

Depending on which browser you are using the percentage-based padding of a flex item will be resolved against its height instead of its width … and the spec is totally fine with that.

Until browser vendors agree on one behavior, be advised to not use vertical margins/paddings on flex items … and know that the CSS Aspect Ratio Hack won’t work on them.

UPDATE 2018-01-24: Microsoft has announced that they are going to implement the Blink/Webkit behavior for compat reasons and ship it in the next version of Edge.

As a result Firefox – the only browser left with the “odd” behavior, once the change in Edge ships – also decided to follow the same path. Work to implement this has already started.

Soon, this quirk will be resolved and all (modern) browsers will have the same behavior.

UPDATE 2018-01-31: The relating Firefox bug has been closed and got marked as resolved. The changes are planned to ship with the release of Firefox 60. 🎉

As detailed in “Aspect Ratios in CSS are a Hack” we can use percentage-based padding to force a box to have a fixed aspect ratio.

However, In a recent project I was working on I noticed that my aspect ratio boxes weren’t working as expected in Firefox. Where other browsers would nicely render boxes with their set aspect ratio, Firefox would do render the box with a fixed height, independent of the box’s defined width.

Digging deeper into the problem – in order to flesh out what exactly triggers this rendering quirk – I knocked up a testcase in which it became clear to me that the CSS Aspect Ratio Hack didn’t seem to work on flex items (e.g. elements that are contained inside a parent element that has display: flex; applied) — Uh oh!

See the Pen Aspect-Ratio box inside flexboxed wrapper? by Bramus (@bramus) on CodePen.

~

Interpreting the results


The result in Chrome, yay 16:9


The result in Firefox, not 16:9

In Chrome/Edge/Safari the green box acts as expected, and has a 16:9 aspect ratio thanks to a 56.25% vertical padding. Firefox however renders the box with a height of 281.25px, independent of its width. Running some numbers this 281.25px turns out to be exactly 56.25% of 500px, which is the height of its parent.

This must be a bug, right?

~

Filing a bug

So I set out to file a bug report for Firefox. To my surprise the issue got closed, concluding that Firefox renders things correctly … as do all the other browsers.

Huh? How can two different results both be correct? Turns out there’s some wiggle room in the spec:

Percentage margins and paddings on flex items can be resolved against either:

  1. their own axis (left/right percentages resolve against width, top/bottom resolve against height)
  2. the inline axis (left/right/top/bottom percentages all resolve against width)

A User Agent must choose one of these two behaviors.

CSS Flexible Box Layout Module Level 1: 4.2. Flex Item Margins and Paddings.

Now letting browsers choose which behavior they want to implement of course isn’t ideal, and that’s also what spec writer Tab Atkins has added as a note:

Note: This behavior sucks, but it accurately captures the current state of the world. It is the CSSWG’s intention that browsers will converge on one of the behaviors, at which time the spec will be amended to require that.

~

Down the Rabbit Hole: How come Firefox behaves differently?

The “culprit” for this quirk is that an older version of the spec read this on margin/padding behavior for flex items:

Percentage margins and paddings on flex items are always resolved against their own axis: left and right margins resolve against the containing block’s width, and top and bottom margins resolve against the containing block’s height. Unlike blocks, block-axis margins do not resolve against the inline dimension of their containing block.

One might wonder why this initially specced behavior behaves different from what we are used to. As Tab Atkins put it:

The logic is that the behavior of percentage vertical padding in existing layout modes is document-focused, where width is *always* the dominant measurement; height is nearly never an input to anything. The newer layout modes (Flexbox and Grid) are different – in them, height and width are on a more equal playing field, and code written that uses percentage padding in the main axis of a flexbox should work equally well for row and column flexboxes. Similar arguments apply for Grid.

So Firefox had implemented this behavior from the start on, and kept in place as a change in the spec still accepted it.

Sidenote: In an archived mailing list message – again by Tab Atkins, the man truly is/was the thriving force behind this spec – I also found this note on the fact that speccing it in this way would nullify the existing aspect ratio hacks:

We recognize that there are some drawbacks, notably the inconsistency with existing document-focused display modes, and the loss of the ability to employ the common “aspect-ratio” hack involving vertical padding. We believe that the first is not too relevant […]. The second is unfortunate, but we plan to address aspect ratios directly in the future, and so consider the loss of this hack for now to be not significant enough to sway our opinion.

… but we plan to address aspect ratios directly in the future … — More on that in a later post 😉

~

Feeling Curious

But why was the original spec changed in the first place? Looking at one of the responses in my initial bug report the adjustment was made because Chrome refused to implement the spec-mandated behavior.

I can follow Chrom(e/ium)’s decision here, as us developers are already used to a vertical padding being calculated with respect to the width:

Part of the reason we’re arguing to change the spec is that all the developers we’ve heard from want the Blink/WebKit behavior because they want to do the aspect ratio hack. We’ve had no developers asking for the specced behavior.

On the other hand, one must admit that the behavior we’re used to is an acquired taste. The first time I ever set a vertical padding I thought it’d be resolved against the height and not the width.

The easy fix would be for Firefox to adjust its behavior to what other browsers do, right? In the long run however – given the history laid out here – I personally thing it might be better to follow the initially specced behavior. Why cling on to an acquired taste, and possibly limit our future possibilities?

That might explain why the spec authors didn’t just switch from one behavior to the other, but added a second behavior instead. By adding a second behavior, they left the door open for future decisions on the matter. For example: the day native aspect ratio methods arrive in browsers, this part of the spec can be reevaluated (and hopefully one of both behaviors can be chosen).

~

What now?

For now, be advised to not use percentage-based values paddings/margins when it comes to flex items as there’s still some ongoing discussion going on about this. This is also noted in the spec:

Advisement: Authors should avoid using percentages in paddings or margins on flex items entirely, as they will get different behavior in different browsers.

The fact that the bug reports on this are still open for both Chromium and Firefox tell me that there’s more to come concerning this. The longer they remain open though, the more sites that might break though once a consensus has been reached.

In case you do want to use any of the current hacks to creating aspect ratios with CSS in combination with flex items, don’t apply the styles on one and the same element but use separate elements for them.

See the Pen Flex Items + Aspect Ratios by Bramus (@bramus) on CodePen.

It’s things like this that make the simple thing called CSS not easy 😉

UPDATE 2017-07-31: Today I came to discover that there’s an open issue on Flexbugs about this. I’ve appended my findings to said issue.

Did this help you out? Like what you see?
Consider donating.

I don’t run ads on my blog nor do I do this for profit. A donation however would always put a smile on my face though. Thanks!

☕️ Buy me a Coffee ($3)

justify-content: space-evenly; for Flexbox

A new alignment mode for Flex-containers is justify-content: space-evenly;

The alignment subjects are distributed so that the spacing between any two adjacent alignment subjects, before the first alignment subject, and after the last alignment subject is the same.

Currently supported in Firefox, and in Chrome Canary.

Box Alignment justify-content: space-evenly; for Flex-Containers →

The flex-grow: 9999; hack

sketch2_w_700

Imagine a flex container (display: flex) with two flex items in a row (flex-direction: row). Item A on the left, and item B on the right. I would like the flex items to be stacked on top of each other when necessary. Item B has to jump onto the second line, if there’s not enough space for it to be at least 20 ems wide.

Now comes the tricky part. I want item A to stretch to the entire width of the container, only if the items are wrapped into multiple lines.

The – clever! – trick to getting this working is to allow both items to flex-grow, but to give item B a ridiculously high value (viz. flex-grow: 9999;)

The flex-grow: 9999; hack →