HTML & CSS
Article

Migrating to Flexbox by Cutting the Mustard

By Bashkim Isai

As front-end developers, the time has come to move away from using CSS floats and dive into the new and exciting world of flexbox. CSS floats are a dated approach to styling layouts; they have been available in Internet Explorer since version 4.0 and workarounds are required to make them malleable (including the clearfix hack and using the nth-child pseudo-class for wrapping columns).

The main topic of this article is how to implement flexbox across multiple browsers considering its fragmentation. If you want to become more familiar with flexbox, there are many good resources available, and I highly recommend the following:

By the end of this article, you should be able to:

  • Understand which versions of flexbox to target for a responsive website.
  • Utilise flexbox via feature detection (cutting the mustard) and provide a fallback for legacy browsers.
  • Move away from using IE conditional comments in most situations.
  • Demonstrate a practical use for flexbox by creating a basic 2×2 grid with a legacy fallback.

A Brief History of Flexbox

The Flexible Box Layout Module (a.k.a. flexbox) is a new way of structuring layouts in CSS. It has undergone multiple revisions and has evolved significantly in its relatively short existence. At the time of writing flexbox is still a W3C working draft, but, like other standards, that shouldn’t make it unappealing in a production environment.

There are three iterations of the standard, commonly referred to as the old syntax, the tweener syntax and the new syntax.

The limitations of flexbox are well documented:

  • The old syntax does not support flex-wrap.
  • The tweener syntax is only supported in IE 10 (including mobile).
  • The new syntax is not fully implemented in Firefox (22-27) as it is missing the flex-wrap and flex-flow properties.

Wrapping (flex-wrap) is an important feature of the specification, which is required to create a responsive grid. For this reason, it’s best to target only the tweener syntax and browser versions that fully implement the new syntax.

This leaves us with the following browser versions:

  • Internet Explorer 10 (tweener syntax with the -ms- prefix)
  • Internet Explorer 11 and Edge (new syntax)
  • Firefox 28+ (new syntax)
  • Chrome 21-28 (new syntax with the -webkit- prefix)
  • Chrome 29+ (new syntax)
  • Safari 6.1+ (new syntax with the -webkit- prefix)
  • iOS Safari 7.0+ (new syntax with the -webkit- prefix)

As there are browsers with a significant market share that do not support flexbox, these should fallback to using CSS floats. But how can this be expressed in code? What’s the best way to differentiate between browser versions that should receive CSS with floats instead of flexbox? What strategy can be used to ensure versions of Firefox that support the new syntax but don’t support wrapping are identified as legacy?

Introducing: Cutting the mustard.

Cutting the Mustard (Feature Detection)

If you haven’t heard it as a technical term before, “Cutting the mustard” was coined by the development team at BBC News. The term stemmed from the fact that the BBC website must cater to a vast international audience and targeting browser versions and devices specifically would have been a cumbersome solution.

The crux of the concept is identifying which browser/device/user agent is being used and serving polyfills to get the site to work. The existence of specific features is detected on the client side and therefore the most appropriate solution for the available technology is delivered.

Feature detection is not new. The aforementioned BBC article was published in March 2012 and while it has grown in popularity, it’s surprising to see websites still implementing IE-specific conditional classes as popularised by Paul Irish in 2008.

Modernizr (contributed to by Paul Irish) is all about feature detection:

Taking advantage of cool new web technologies is great fun, until you have to support browsers that lag behind. Modernizr makes it easy for you to write conditional JavaScript and CSS to handle each situation, whether a browser supports a feature or not. It’s perfect for doing progressive enhancement easily.

Although CSS now has native feature detection, it currently does not have enough market share to be viable for real-world use. The remainder of this article will discuss how to ditch the IE conditional comments in favour of feature detection in JavaScript.

Identifying Features and Browsers

Every project requires a different set of features in order to function. There are multiple ways feature detection can be realised, the easiest of which includes:

The most efficient approach is the vanilla JavaScript implementation. It’s fast (as it doesn’t require the client to download any additional libraries) and does not require any additional processing. This approach is far from perfect as there are known issues; however there are ways to overcome common feature detection problems.

[B]rowser detection has become an impossible tangle, and has largely fallen out of use, to be superseded by something far better — feature detection.

[…] feature detection isn’t completely reliable either — there are times where it fails.

James Edwards

Choosing Modernizr for cutting the mustard may not be as efficient (as it requires downloading and client processing), but manually detecting flex-wrap support is not a straightforward task. It’s also important to note that although Modernizr version 2 doesn’t detect flex-wrap, version 3 does! The feature is labelled as Flex Line Wrapping.

Although the option exists to use the CSS classes attached to the document root produced by Modernizr (e.g.: html.flexwrap), it’s better to serve separate CSS files for each experience to reduce the download size of the site.

The BBC News developers refer to two types of browsers:

Someone on the team started referring to them as “HTML4 browsers” and “HTML5 browsers”, which we find is easier to communicate the sentiment to non-technical people.

BBC Responsive News

This rationale was perfectly valid when you consider the climate of the browser landscape in 2012; however as new features become available, the division is not necessarily as clear. Flexbox, for example, isn’t fully supported in all “HTML5” browsers.

A robust approach is to differentiate between “legacy” and “modern” browser versions. In addition, some projects may require multiple divisions where half-way (or “transitional”) browsers can be identified.

Implementing the Approach

Start by creating the following files:

  • index.html – the main HTML file
  • stylesheets/modern.css – styles for modern browsers (media queries, flexbox with wrapping)
  • stylesheets/legacy.css – styles for legacy browsers (no media queries, no flexbox)
  • scripts/dependencies.js – performs feature detection

Here’s how our index.html file will look:

<!DOCTYPE html>
<html class="no-js">
  <head>
    <title>Cutting the mustard</title>
    <script src="javascripts/dependencies.js"></script>
    <noscript>
    <link rel="stylesheet" href="stylesheets/legacy.css">
    </noscript>
    <!-- ... -->
  </head>
  <body>
    <div class="container">
      <div class="cell cell-1">Cell 1</div>
      <div class="cell cell-2">Cell 2</div>
      <div class="cell cell-3">Cell 3</div>
      <div class="cell cell-4">Cell 4</div>
    </div>
  </body>
</html>

Notice that there are no IE conditional comments? Just clean and valid HTML code. And if the browser does not have JavaScript enabled, it will fall back to using legacy.css regardless of its level of support.

You may also notice that the script tags are at the top of the HTML page. This is because Modernizr should process and inject the stylesheets before the browser paints for the first time. This reduces repaint and helps to avoid a Flash Of Unstyled Content (FOUC). But remember that most script tags would be at the bottom of the page.

Our legacy.css file will contain the following:

.container {
}

/* clearfix */
.container:after {
  content: "";
  display: table;
  clear: both;
}

.cell {
  width: 50%;
  float: left;
}

/* wrapping */
.cell:nth-child(2n+1) {
  clear: left;
}

/* for visiblity */
.cell-1 { background-color: #000; color: #fff; }
.cell-2 { background-color: #666; color: #fff; }
.cell-3 { background-color: #ccc; color: #000; }
.cell-4 { background-color: #fff; color: #000; }

This implementation includes a clearfix hack and the :nth-child pseudo-class for wrapping. It works in most browsers; however Internet Explorer 8 requires Selectivizr or an equivalent solution to get the selector working.

Next, our modern.css file:

.container {
  /* Internet Explorer 10
   */
  display: -ms-flexbox;
  -ms-flex-wrap: wrap;

  /* Chrome 21-28
   * Safari 6.1+
   * Opera 15-16
   * iOS 7.0+
   */
  display: -webkit-flex;
  -webkit-flex-wrap: wrap;

  /* Chrome 29+
   * Firefox 28+
   * Internet Explorer 11+
   * Opera 12.1 & 17+
   * Android 4.4+
   */
  display: flex;
  flex-wrap: wrap;
}

.cell {
  -webkit-flex: 1 0 50%;
      -ms-flex: 1 0 50%;
          flex: 1 0 50%;
}

/* for visiblity */
.cell-1 { background-color: #000; color: #fff; }
.cell-2 { background-color: #666; color: #fff; }
.cell-3 { background-color: #ccc; color: #000; }
.cell-4 { background-color: #fff; color: #000; }

Don’t be put off by the size of this file. The comments make it appear larger, but these make it easier in development to understand what each section is targeting.

Next we will write the code for dependencies.js.

As mentioned, we need to generate a version of Modernizr (version 3) which detects the support of the flex-wrap property. Include the code at the top of the JavaScript file.

/* Include Modernizr v3 with 'Flex line wrapping' here */

(function() {
  var isModern = Modernizr.flexwrap;

  var link = document.createElement('link');
  link.rel = 'stylesheet';
  link.type = 'text/css';
  link.href = 'stylesheets/' + (isModern ? 'modern' : 'legacy') + '.css';

  document.getElementsByTagName('head')[0].appendChild(link);
})();

You can optionally increase the requirements for a modern experience by adding to the isModern Boolean. For example:

var isModern = Modernizr.flexwrap && 'querySelector' in document;

Sass Solutions

You can use Sass to abstract your approach to implementing flexbox. This reduces the size of the CSS output and makes it easier to maintain:

%flexbox {
  display: -ms-flexbox;
  -ms-flex-wrap: wrap;
  display: -webkit-flex;
  -webkit-flex-wrap: wrap;
  display: flex;
  flex-wrap: wrap;
}

.container1 {
  @extend %flexbox;
}

.container2 {
  @extend %flexbox;
}

Progressive Enhancement and Browser Testing

It’s important to understand the differences between flexbox and CSS floats. Your implementation will not look exactly the same in each of the experiences — but the notion of progressive enhancement means that it doesn’t necessarily have to.

For example, by default, flexbox will stretch all cells on the same row to have the same height. Therefore, if one cell is 3 lines long and the adjacent row is 10 lines long, the background will stretch on both cells to 10 lines. The fallback for CSS floats will not do this and both cells will have uneven heights.

Testing the layout in multiple browsers is still a requirement, but remember that forcing the value of isModern to false in JavaScript can help test legacy solutions in any browser:

var isModern = false; // Modernizr.flexwrap;

Conclusion

In this article, I’ve provided the basics for using feature detection to serve two different stylesheets on the same HTML code base. This is an extremely effective way of beginning the upgrade process away from CSS floats and reducing the dependency on IE conditional comments.

Although there has been a strong focus on detecting support for flexbox, it’s important to note that as new features are developed for browsers, this approach to cutting the mustard can be adapted and evolved to suit future requirements.

Once Internet Explorer 10 falls out of popularity with the browser market share in your target sector, you may be able to ditch the tweener syntax and deliver leaner code solely through the use of the new syntax.

So now that you have all of the theory, why not get well acquainted with flexbox in your next project?

  • ubu2600

    I’m not sure how to square caniuse’s nearly all-green report of flexbox support (http://caniuse.com/#feat=flexbox) with your statement, “As there are browsers with a significant market share that do not support flexbox, these should fallback to using CSS floats.” Do you have any insight? If I just look at caniuse, then I’m inclined to think that I’m clear to code with flexbox without spending time creating the detection & fallback IF the project is for most recent browsers only, which is my agency’s default. What do you think?

  • LouisLazaris

    @ubu2600:disqus The problem is, if you go by NetMarketShare, then you have over 20% of users that have browsers not supporting flexbox. I’m not saying NMS is correct, but it just shows that there are pockets of demographics in certain areas that have higher percentages for IE8-10. I think the best thing to do is code to your own users, based on real data, and not necessarily to a global trend. The global stats are just starting guides, and shouldn’t really be used for final decisions on what to support.

  • Daniel Tonon

    I’ve actually taken it a step further in my projects and use display:table as the default for legacy browsers and only using the floats backup if I need flexbox wrapping on a desktop sized screen. We don’t support legacy browsers on anything other than desktop sized screens.

    display:table can replicate the equal heights, flex-grow, and easy vertical alignment aspects of flexbox in older browsers. The main thing it can’t do is wrap the cells onto new lines.

  • Fernando Claussen

    If you guys are looking to take a step further, I started a project yesterday that I would love to see becoming something really awesome.

    https://github.com/fclaussen/Flexbox-Framework

  • http://ricardozea.design/ Ricardo Zea

    What a great article Bashkim, love the simplicity of the explanations :)

    All in all, we shouldn’t be using any IE-specific conditional classes or feature detection scripts in our work, but that’s not how reality works, and we need to use either technique regardless if we like it or not.

    With that being said, here’s a different perspective to accomplish the same thing by using IE-specific conditional classes rather than feature detection:

    Using IE-specific conditional classes is totally fine, absolutely no hurt in doing so, and to me the benefits outweigh any negatives. To be perfectly honest, I still haven’t experienced any negative effects of using IE-specific conditional classes. And also, who cares how long it was proposed by Paul, that should NEVER be an argument to drop a technique.

    To put it in perspective: There aren’t any writings or comments from the famous Spanish painter Salvador Dali stating that Surrealism was an old technique created 34 years before he painted the Crucifixion.

    Here are the benefits of using IE-specific conditional classes to target old IEs:

    1. No JavaScript dependency is by far the most important benefit. If you don’t know JavaScript, it doesn’t matter you can still accomplish the exact same things if using feature detection with Modernizr.

    2. One less request. This is a HUGE deal, maybe just as important as #1.

    3. One less file to manage, one less file to maintain. WAY less headaches.

    4. Learning curve is practically zero. IE-specific conditional classes are straight up easy to understand. Which leads me back to point #1: No JS dependency.

    5. Writing the IE specific styles in a single stylesheet is easier to troubleshoot and maintain because you don’t have to deal with several files to update, upload, commit, etc. Which brings me back t point #2: one less file to manage.

    If you could point out the negatives in IE-specific conditional classes, that’d be great because it’ll give us a comparison point to see where can do better :)

    Additionally, I think that if you’re showing the benefits of feature detection showing succinct code, you should also intrinsically show the need for a vendor prefixing technique thus your code would look clean of vendor prefixed properties and values.

    On this note, on your Sass code using the %flexbox placeholder, using comma separated selectors is a better practice:

    .container1, .container2 {
    @extend %flexbox;
    }

    Again, great article and Thanks for sharing :)

    • Bashkim Isai

      Thank you for your in-depth response to this article, Ricardo. I appreciate the healthy and constructive feedback – especially when it makes better developers of us all.

      Naturally I agree with your point in reference to Paul Irish – doing something because one individual “says so” isn’t an affective premise. I think it may have just been the tone of how I conveyed this part of the article and I thank you for pointing it out. The point that I was making was more of a commentary on how the ways of the internet change – Paul Irish himself suggested using IE-conditional comments to target the browser by overloading the tag; yet a year later, he contributed to Modernizr which was effectively a move away from this approach.

      One of the main purposes of this article was to identify that it’s not just Internet Explorer which can have feature support issues. Different versions of Firefox, Safari and Chrome (as outlined in the article) have varying flexbox and flex-wrap support. Now of course, an auto-prefixer can solve flexbox issues, but as more and more non-standardised or non-standard implementations of features appear in browsers, we need to consider how we approach feature detection.

      Take CSS motion paths in SVGs: at the time of writing this comment, it is an unofficial status and is only supported by Chrome and Opera, but not by Internet Explorer, Edge and Firefox. In these situations, IE-conditional comments are not an effective solution – neither are CSS Feature Queries as the market share of Internet Explorer 11 is currently greater than both 1% and 0.5%.

      I think the important thing here is identifying the right solution for the implementation of each project. For example: there may be cases where IE-conditionals may be a completely appropriate solution; however, in practice I am seeing fewer uses for it in production as the browser landscape modernises.

      I also agree with you completely that using IE-conditional comments can be far more effective in terms of load time, especially on older browsers. However; with the advent of HTTP2 and the consideration that putting Modernizr at the bottom of your HTML page will force the browser to potentially redraw your entire page, is this still as much of an issue?

      As a little side note, one solution that I personally use throughout my projects is to use Breakpoint to render a specific media query version of a site for legacy browsers.

      For example, the legacy.scss file would be:


      @import "/path/to/bower/breakpoint-sass/stylesheets/breakpoint";
      @include breakpoint-set('no queries', true);
      @include breakpoint-set('no query fallbacks', true);
      @import "modern";

      And a very simplified version of the modern.scss could be:


      @import "/path/to/bower/breakpoint-sass/stylesheets/breakpoint";
      $media-mobile: screen (max-width 767px);
      $media-tablet: screen (min-width 768px) (max-width 1279px), print, 'no-query';
      $media-desktop: screen (min-width 1280px);

      p {
      @include breakpoint($media-mobile) { font-size: 14px; }
      @include breakpoint($media-tablet) { font-size: 13px; }
      @include breakpoint($media-desktop) { font-size: 12px; }
      }

      This solution provides you with legacy.css which resembles the tablet version of the site for use in non-supported versions, and a modern version which includes all features you may require. Couple a modern version with CSS Feature Detection and you’ve got yourself on to a winner. I use a number of media queries (mobile, not-mobile, mobile-portrait, mobile-landscape, etc.) and target non-supported browsers to use the tablet-portrait version of the site. I then use a media query combiner to reduce the size of the output.

      I hope this resolves some of the details of the article for you. Happy coding!

      • http://ricardozea.design/ Ricardo Zea

        Hey Bashkim, Thanks for a detailed reply. 100% agree with you, conversations like this greatly helps us better developers… and designers ;)

        The way I like to see what Paul did when he suggested IE-specific conditional classes and then contributing to Modernizr, is that he didn’t “move away” from the IE-specific conditional classes method, he just contributed to a different web development technique, because to me using either approach is valid in their own ways, not one is better than the other.

        Now, I’ve used IE-specific conditional classes ONLY to target old IEs’ CSS problems, that’s it. The great thing about Modernizr, on top of allowing us to do that as well, is allowing us to deal with OTHER browsers’ CSS issues, which obviously IE-specific conditional classes can’t do.

        And I think this is the clarification I missed in my first comment :p. So I apologize for that >_<

        Also, when you say “…overloading the tag”, I think that’s a bit overreacting :). Indeed the <html> element is bit crowded up there but I wouldn’t say overloaded.

        Want to talk about overloading? What about Bootstrap’s and Foundation’s class-stacking mayhem on an element to make it responsive? Or BEM where every single element has a class. Every single element! Hehe, yeah :)

        Regarding the examples of CSS motion paths in SVGs and CSS Feature Queries, using Modernizr is hands down THE way to go about providing support/fallbacks for those old browsers out there, or modern ones that haven’t yet implemented those advanced features.

        And that’s the thing, with HTTP/2 many of our current practices for optimization will not be necessary anymore. However, at the moment of writing this comment HTTP/2 isn’t popular just yet so repaint is considered an issue, so placing Modernizr at the bottom of the page isn’t probably the best idea.

        Now, to not get too off-topic, when it comes to working with media queries for responsive I do not, under any circumstance, declare breakpoints based on device width. Content defines the breakpoints in my book :p (I did write a book actually >_<).

        With this being said, I don't see the great benefit of naming breakpoints because when you have too much of them it's very, very difficult to keep track of which width a certain breakpoint targets. Remember, the content defines the breakpoints so you will end up with MANY breakpoints.

        Albeit I haven't used Breakpoint, I've use many other techniques to deal with media queries (which basically do the same thing Breakpoint does) and the results haven't been what I've really hoped for, just more scalability issues than I needed and wanted to deal with.

        So since I've been using this basic simple Sass mixin, you have no idea how much my RWD life has improved:

        Mixin:

        @mixin goingLarge($width) {
        @media (min-width: $width/16+em) {
        @content
        }
        }

        Usage:

        //Usage
        .selector {
        width: 100%; //Properties for small screens
        @include goingLarge(725) {
        width: 50%; //Properties for large screens
        }
        }

        Output:

        .selector {
        width: 100%;
        }
        @media (min-width: 45.3125em) {
        .selector {
        width: 50%;
        }
        }

        One of the greatest things about this mixin is by far the fact that it allows you to keep thinking in pixels but the output is in ems. Understanding how wide 725px is waaaay easier than trying to understand how wide 45.3125em is =]

        And browsers that don’t support media queries get the small screen layout and done. And if you want to go the extra step, just serve Respond.js to those oldies.

        No need to deal with IE-specific conditional classes or feature detection (although you could argue that polyfilling is way of feature detection? :p, hehe).

        The point I mainly want to get across in this great conversation is that at the end of the day we are accomplishing EXACTLY the same objective but with different methods. And that in itself I think it’s awesome.

        Each method has its pros and cons, and that’s ok, just like everything in life.

        Thanks again Bashkim :)

        • http://www.team-space.co.uk Charlie Francis

          Totally agree Ricardo, very valid points!

    • Alexander

      I hear you Ricardo. But I fear your rationale is based in a too, too limited mode of thinking. Dare to dream a little will you?

  • http://u-marudy-i-leniucha.pl/ sourire09

    hi, i wanted to clear in my head differences between whose 3 flexbox syntaxes:
    – syntax obviously
    – support for different browsers
    – different functionalities like “old syntax does not support flex-wrap”
    Am I right? What other differencies in functionalities they have? And I understand that any kind of backward compatibility is not possible here?

Recommended

Learn Coding Online
Learn Web Development

Start learning web development and design for free with SitePoint Premium!

Get the latest in Front-end, once a week, for free.