Home >Web Front-end >CSS Tutorial >Migrating to Flexbox by Cutting the Mustard
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:
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:
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:
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.
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.
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.
Start by creating the following files:
Here’s how our index.html file will look:
<span><span><!DOCTYPE html></span> </span><span><span><span><html</span> class<span>="no-js"</span>></span> </span> <span><span><span><head</span>></span> </span> <span><span><span><title</span>></span>Cutting the mustard<span><span></title</span>></span> </span> <span><span><span><script</span> src<span>="javascripts/dependencies.js"</span>></span><span><span></script</span>></span> </span> <span><span><span><noscript</span>></span> </span> <span><span><span><link</span> rel<span>="stylesheet"</span> href<span>="stylesheets/legacy.css"</span>></span> </span> <span><span><span></noscript</span>></span> </span> <span><!-- ... --> </span> <span><span><span></head</span>></span> </span> <span><span><span><body</span>></span> </span> <span><span><span><div</span> class<span>="container"</span>></span> </span> <span><span><span><div</span> class<span>="cell cell-1"</span>></span>Cell 1<span><span></div</span>></span> </span> <span><span><span><div</span> class<span>="cell cell-2"</span>></span>Cell 2<span><span></div</span>></span> </span> <span><span><span><div</span> class<span>="cell cell-3"</span>></span>Cell 3<span><span></div</span>></span> </span> <span><span><span><div</span> class<span>="cell cell-4"</span>></span>Cell 4<span><span></div</span>></span> </span> <span><span><span></div</span>></span> </span> <span><span><span></body</span>></span> </span><span><span><span></html</span>></span></span>
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:
<span><span>.container</span> { </span><span>} </span> <span>/* clearfix */ </span><span><span>.container:after</span> { </span> <span>content: ""; </span> <span>display: table; </span> <span>clear: both; </span><span>} </span> <span><span>.cell</span> { </span> <span>width: 50%; </span> <span>float: left; </span><span>} </span> <span>/* wrapping */ </span><span><span>.cell:nth-child(<span>2n+1</span>)</span> { </span> <span>clear: left; </span><span>} </span> <span>/* for visiblity */ </span><span><span>.cell-1</span> { background-color: #000; color: #fff; } </span><span><span>.cell-2</span> { background-color: #666; color: #fff; } </span><span><span>.cell-3</span> { background-color: #ccc; color: #000; } </span><span><span>.cell-4</span> { background-color: #fff; color: #000; }</span>
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:
<span><span>.container</span> { </span> <span>/* Internet Explorer 10 </span><span> */ </span> <span>display: -ms-flexbox; </span> <span>-ms-flex-wrap: wrap; </span> <span>/* Chrome 21-28 </span><span> * Safari 6.1+ </span><span> * Opera 15-16 </span><span> * iOS 7.0+ </span><span> */ </span> <span>display: -webkit-flex; </span> <span>-webkit-flex-wrap: wrap; </span> <span>/* Chrome 29+ </span><span> * Firefox 28+ </span><span> * Internet Explorer 11+ </span><span> * Opera 12.1 & 17+ </span><span> * Android 4.4+ </span><span> */ </span> <span>display: flex; </span> <span>flex-wrap: wrap; </span><span>} </span> <span><span>.cell</span> { </span> <span>-webkit-flex: 1 0 50%; </span> <span>-ms-flex: 1 0 50%; </span> <span>flex: 1 0 50%; </span><span>} </span> <span>/* for visiblity */ </span><span><span>.cell-1</span> { background-color: #000; color: #fff; } </span><span><span>.cell-2</span> { background-color: #666; color: #fff; } </span><span><span>.cell-3</span> { background-color: #ccc; color: #000; } </span><span><span>.cell-4</span> { background-color: #fff; color: #000; }</span>
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.
<span>/* Include Modernizr v3 with 'Flex line wrapping' here */ </span> <span>(function() { </span> <span>var isModern = Modernizr.flexwrap; </span> <span>var link = document.createElement('link'); </span> link<span>.rel = 'stylesheet'; </span> link<span>.type = 'text/css'; </span> link<span>.href = 'stylesheets/' + (isModern ? 'modern' : 'legacy') + '.css'; </span> <span>document.getElementsByTagName('head')[0].appendChild(link); </span><span>})();</span>
You can optionally increase the requirements for a modern experience by adding to the isModern Boolean. For example:
<span>var isModern = Modernizr.flexwrap && 'querySelector' in document;</span>
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:
<span><span><!DOCTYPE html></span> </span><span><span><span><html</span> class<span>="no-js"</span>></span> </span> <span><span><span><head</span>></span> </span> <span><span><span><title</span>></span>Cutting the mustard<span><span></title</span>></span> </span> <span><span><span><script</span> src<span>="javascripts/dependencies.js"</span>></span><span><span></script</span>></span> </span> <span><span><span><noscript</span>></span> </span> <span><span><span><link</span> rel<span>="stylesheet"</span> href<span>="stylesheets/legacy.css"</span>></span> </span> <span><span><span></noscript</span>></span> </span> <span><!-- ... --> </span> <span><span><span></head</span>></span> </span> <span><span><span><body</span>></span> </span> <span><span><span><div</span> class<span>="container"</span>></span> </span> <span><span><span><div</span> class<span>="cell cell-1"</span>></span>Cell 1<span><span></div</span>></span> </span> <span><span><span><div</span> class<span>="cell cell-2"</span>></span>Cell 2<span><span></div</span>></span> </span> <span><span><span><div</span> class<span>="cell cell-3"</span>></span>Cell 3<span><span></div</span>></span> </span> <span><span><span><div</span> class<span>="cell cell-4"</span>></span>Cell 4<span><span></div</span>></span> </span> <span><span><span></div</span>></span> </span> <span><span><span></body</span>></span> </span><span><span><span></html</span>></span></span>
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:
<span><span>.container</span> { </span><span>} </span> <span>/* clearfix */ </span><span><span>.container:after</span> { </span> <span>content: ""; </span> <span>display: table; </span> <span>clear: both; </span><span>} </span> <span><span>.cell</span> { </span> <span>width: 50%; </span> <span>float: left; </span><span>} </span> <span>/* wrapping */ </span><span><span>.cell:nth-child(<span>2n+1</span>)</span> { </span> <span>clear: left; </span><span>} </span> <span>/* for visiblity */ </span><span><span>.cell-1</span> { background-color: #000; color: #fff; } </span><span><span>.cell-2</span> { background-color: #666; color: #fff; } </span><span><span>.cell-3</span> { background-color: #ccc; color: #000; } </span><span><span>.cell-4</span> { background-color: #fff; color: #000; }</span>
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?
Migrating to Flexbox is a significant step in modern web development. Flexbox, or Flexible Box Layout, is a CSS3 web layout model that allows responsive elements within a container to be automatically arranged depending upon screen size. This means that the layout of your web pages can easily adapt to different screen sizes, ensuring a seamless user experience across various devices. It also simplifies the process of designing a flexible, responsive layout structure without using float or positioning.
Serving legacy JavaScript to modern browsers can significantly impact your website’s performance. Legacy JavaScript is often bloated with unnecessary code that modern browsers do not need. This extra code can slow down your website as the browser needs to download, parse, compile, and execute it. By serving modern JavaScript to modern browsers, you can improve your website’s load time and overall performance.
To avoid serving legacy JavaScript to modern browsers, you can use the module/nomodule pattern. This pattern allows you to create two separate bundles of your JavaScript – a modern, ‘module’ bundle and a legacy, ‘nomodule’ bundle. Modern browsers that understand the ‘type=”module”‘ attribute will download the modern bundle, while older browsers will ignore it and download the legacy bundle instead.
Flexbox offers several advantages over traditional layout methods. It allows for flexible layout structures, making it easier to design responsive websites. It also simplifies the alignment, distribution, and ordering of elements within a container. With Flexbox, you can easily achieve complex layouts and alignments that would be difficult with traditional CSS.
Migrating to Flexbox can be a complex process, especially for large, existing projects. It’s important to thoroughly understand the Flexbox model before starting the migration. Start by experimenting with simple layouts, gradually moving to more complex ones. Use a step-by-step approach, migrating one component at a time and thoroughly testing each change.
Tools like Google Lighthouse, GTMetrix, and WebPageTest can help you identify if you’re serving legacy JavaScript to modern browsers. These tools provide detailed performance reports, highlighting areas where your website’s performance can be improved.
While Flexbox is a modern layout model, it does have some level of support in older browsers. However, there may be some inconsistencies and bugs. It’s recommended to use tools like Autoprefixer, which can add necessary vendor prefixes to your CSS, ensuring compatibility with older browsers.
Migrating to Flexbox can positively impact SEO. A responsive, fast-loading website provides a better user experience, which is a key factor in SEO. Additionally, websites that load faster are more likely to be crawled and indexed by search engines.
Some common challenges when migrating to Flexbox include understanding the new layout model, dealing with browser inconsistencies, and ensuring backward compatibility. It’s important to thoroughly test your website in various browsers and devices to ensure a consistent user experience.
There are numerous online resources to learn about Flexbox and modern JavaScript. Websites like CSS-Tricks, MDN Web Docs, and SitePoint offer comprehensive guides and tutorials. Additionally, online coding platforms like Codecademy and freeCodeCamp provide interactive lessons on these topics.
The above is the detailed content of Migrating to Flexbox by Cutting the Mustard. For more information, please follow other related articles on the PHP Chinese website!