I've been moving a bunch of old markup over to Flexbox layout recently, because it makes so many things so much easier to deal with. One place I did this with is this blog – previously I was using a left float with absolutely positioned headers and containers, which worked Ok but was a nightmare to maintain. Moving that code to use a simple flexbox grid to manage the alignment greatly simplified the layout. The complexity of the CSS is greatly reduced and the page behavior is much more consistent across viewport sizes.
Big win!
Flexbox and min-width
Eeeeeh, hold on there doc – not so fast. After I pushed up the new layout I noticed that a number of posts with wide code blocks were breaking at various sizes. Which was odd because the code that controls code blocks using pre tags and overflow restraints has been there forever and has always worked.
The layout for the main blog content is responsive, so it resizes based on viewport width along with some media queries to redefine the layout at smaller sizes. But after moving to Flexbox I noticed that all code snippets that were longer were causing the posts to cut off the right side of the content. This wasn't affecting just the actual code snippets, but ended up pushing the entire container content off the page.
Here's what this looked like:
The bar on the right is the browser scrollbar which shows the edge of the window and you can see that the content is pushing out beyond the window's boundaries. Notice that even though I have overflow-x: auto set on the <pre> block there are no scrollbars on the code block.
I've been using the following CSS:
pre
{
max-width: 100%;
overflow-x: auto;
background: #eee;
}
Simple and obvious, right? Should just work. And it used to. Except it doesn't inside of a flex element.
Notice the overflow-x: auto which should and has in the past managed to handle the overflow for the <pre> tag properly. However after adding a Flexbox container in which the content is now rendered, the overflow on the PRE tag no longer works.
Flex Container Differences: min-width
It turns out there are a few CSS default values that Flexbox sets differently on Flexbox containers than standard block layout. The one specifically that affects my above layout is min-width which in Flexbox is set to auto instead of the value of 0 which is the block display default. min-width can affect how a container manages resizing itself when a flex element is set to flex (flex: 1 for example).
The default value for min-width in a flex element is auto, which is new value for min-width that is valid only inside of a flex element. Here's the info in the spec. To say this is a confusing and unexpected default value is an understatement.
In the example above max-width and min-width values are in conflict and it appears that min-width is winning out resulting in the pre tag overflowing (in an odd way actually – it overflows by a small amount not even to its full length because the scrollbars eventually kick in from the overflow-x setting).
In other words the behavior is a bit unexpected and min-width is the culprit.
To be clear the min-width value is not always a problem – in many cases you can get by with various overflow hidden/auto/scroll settings and the element that has the overflow will manage its sizing correctly. However, the pre tag in particular is tricky because of its special white-space: pre setting which causes content to automatically overflow unless you explicitly constrain it. It turns out that the container's min-width affects this behavior and hence the problem. You may see this with other elements that use overflow or overflow-x but in my testing it's much less of a problem with standard elements than it is with <pre>.
So how do we fix this?
The solution to this particular problem is to set min-width: 0 on the flex element, which in my case is the container that wraps my blog article content which in turn contains the <pre> tags. I can set the styling on the post content (either inline or via a CSS class).
Here's the simplified layout (with the relative inline styles to demonstrate):
<div class="post-container" style="display: flex">
<aside class="post-sidebar" style="flex: none">
...
</aside>
<main class="post-content" style="flex: 1; min-width: 0">
…
<pre style="overflow: auto; max-width: 100%">
</pre>
</main>
</div>
The key element is:
<main class="post-content" style="min-width: 0">
which is the flex child of the Flexbox container that manages the two vertical panes of content. Note it's not sufficient to slap the min-width: 0 on the errand <pre> tag – that has no effect. It has to be the flex element that receives the setting which is the post-content container in this case.
With that setting in place content now properly respects the overflow and margins and behaves the same as it did prior to using Flexbox containers:
Global Fix
The above fix by slapping min-width: 0 on to the flex element works, but it's one of those things that's too easy to forget about and run into trouble with again later. While I ran into this problem with <pre> tags this problem can affect any content that has overflow restrictions because the overflow is controlled by the width of the container, and without explicitly setting min-width that width often can behave quite differently than you might expect.
This is annoying to say the least so in most cases I've now taken to overriding min-width as part of my Normalize CSS:
html, body, div, span, object, iframe, table, caption, tbody, tfoot, thead, tr, th, td,
del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var,
h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code,
dl, dt, dd, ol, ul, li, fieldset, form, label, legend, article, main, nav, ins {
padding: 0;
margin: 0;
border: 0;
outline: 0;
line-height: inherit;
vertical-align: baseline;
font-family: inherit;
font-size: 100%;
min-width: 0;
}
And this works to set the min-width for local fix the problem as well. I'm not quite sure why Flexbox forces min-width to auto but I haven't seen any problems with min-width:0 in flex elements, so I think this is a relatively safe tweak to make.
Internet Explorer
On a side note, it appears that Internet Explorer doesn't set min-width value to some indeterminate value but explicitly uses 0, so I didn't see the funky wrapping behavior in IE initially. All other browsers I tested (Chrome, Safari, FireFox and Opera) however exhibit the same Flexbox behavior with an indeterminate min-width. One more reason to be explicit about setting the value globally perhaps.
Summary
Problems like this make me wish I was in a different line of work. It took me a while to track this nasty issue down. Finding a behavior problem in a pre tag that was actually controlled based on the behavior of the parent container which in turn is controlled by differing behavior of Flexbox is ridiculously difficult to debug and track down. It took about 20 different StackOverflow issues to correlate onto this particular problem which is not surprising given the causal relationship between the element in question and the container that caused the actual issue.
Hopefully this post will make this easier to figure out in the future. I just hope I'll remember I wrote this post when I run into this problem again which is likely given the default browser behavior.
Other Posts you might also like