Inspired by Mark Otto's tour of GitHub's CSS and Ian Feather's tour of Lonely Planet's CSS, I thought I would join the party and talk about how we do CSS at CodePen.
The following is a guest post by Katy Decorah (edited by Jason Morris). Katy was researching CSS columns and how they can work responsively, as there wasn't much information she could find out there on the subject. So, like a hero, she wrote it all up. Here's Katy:
With CSS columns you can create a print-inspired layout with little added markup that can adapt beyond a fixed canvas. A supported browser will make calculations to wrap and balance content into tidy columns. If you're already working with a fluid layout, the columns will reflow automatically. With the right combination of properties, CSS columns can be an interesting layout option that is responsive-friendly while degrading gracefully.
Where to declare columns
You can declare columns on any block level element. Columns can be applied to a single element or applied to multiple elements by targeting their parent.
In the image below, the left side depicts CSS column rules being applied to the second paragraph to transform just that content into columns. The right side depicts the rules being applied to a container element such as a
or
to transform the elements into a multi-column layout.
How to declare columns
There are three different ways to declare columns:
Declare column-count.
Declare column-width.
Declare both (recommended).
Let's explore the different ways to declare columns.
1. Declare column-count
Use column-count to declare the number of columns.
The number of columns remains consistent even as the browser width changes as demonstrated in the gif below.
2. Declare column-width
Use column-width to declare the width of the columns.
The specified value is not an absolute value, but rather a minimum width. Given the column-width, the browser will decide how many columns of at least that width can fit in the space. The browser will also take into account the gutter, or gap between columns in this calculation (more on that later). Then the browser will expand the columns to evenly distribute to fill the width of the container.
For example, if you inspect the paragraph in the live demo below, you will find that the width of the column is actually greater than 150px, which is the set value of column-width.
Once the browser cannot fit at least two columns as wide as the column-width then no columns will appear and the layout will return to a single column layout.
The gif below demonstrates how the columns release as the browser width narrows. Unlike column-count this property is inherently responsive.
3. Declare both (recommended)
Use column-count and column-width together for the best control over CSS columns. You can declare each property or use the shorthand columns.
When both properties are declared, column-count is the maximum number of columns and column-width is the minimum width for those columns.
Like column-gap, the vertical line will exit once the browser width is too narrow as demonstrated in the gif below.
column-span
To break the column flow temporarily, declare column-span on a child element. Currently, Firefox does not support this feature (but you can go vote for it on Bugzilla).
The image below contains a heading that indicates the start of a new chapter in the story, but it continues with column flow.
To break the heading out of the column flow, add column-span: all to the element. This declaration will temporarily stop the column flow to allow the element to span the columns, but will restart the columns with the next element.
To change how the content fills the columns, declare column-fill. Currently, this property is only available in Firefox.
When height is added to a columned element, Firefox behaves differently than the other browsers. Firefox will automatically balance the content, while the other browser will fill the columns sequentially.
The image below demonstrates how Firefox behaves compared to the other browsers when height is added to a columned element.
In Firefox, you can change this behavior by declaring column-fill: auto. This rule will make the columns fill sequentially, as shown in the live demo below.
Since a height must be declared for Firefox to enable column-fill, the height constraint breaks the fluid pattern. The content expands horizontally because it cannot flow vertically as demonstrated in the gif below. In this case, a media query could be added to manage the height (more on that later).
The other browsers don't support column-fill and will fill columns sequentially when a height is declared on a columned element. It's worth noting that when height is added to any columned element, regardless of browser or use of column-fill, the constraint will break the fluid pattern.
Limitations
Columns can be an elegant way to deliver content, so long as the content remains readable. A multi-column layout can become difficult to read when the content overflows horizontally or the columns become taller than the viewport.
Content overflows horizontally
As shown in the column-fill section, if height is added to a columned element, then the element will expand horizontally to fit the content. The overflow will break the layout and send users in a different direction.
Possible Solution: create a min-width media query to declare the height rule.
In the demo below, I narrowed the browser window to find when the columns began to overflow and wrote down that number. Next, I wrote a min-width media query using the value of when the columns overflow and moved the height rule to the media query.
As demonstrated in the gif below, when the columns release the height rule is out of bounds and the columns flow vertically.
Columns become taller than the viewport
If your column height extends beyond the viewport then users will need to scroll to follow the column flow.
Possible Solution: create a min-height media query to activate the columns.
In the example below, the columns will only activate if the columned element has a min-height: 400px;. I came to this number by shrinking the browser width to the point just before the columns release. Next, I adjusted the height of the browser to find where the columns began to fall under the viewport. I used that number for the value of the min-height media query.
The gif below demonstrates that the columned element must be at least 400px tall to display the content in columns.
Support
CSS columns have decent support (IE10+) and still require prefixes on all properties. Find out more details about the multi-column properties on Can I Use....
Even if your favorite browser doesn't support multi-column layouts, keep in mind that they degrade gracefully into a single column layout. CSS columns are like the escalators of content delivery (as so eloquently put by Mitch Hedberg):
A multi-column layout cannot break, it can only become a single column layout.
JavaScript Event Madness! Capturing *all* events without interference
The following is a guest post by Matthias Christen and Florian Müller from Ghostlab. Ghostlab is cross-browser cross-device testing software for Mac and PC. One of the things I'm very impressed Ghostlab can do is sync the events from one browser to all the others. Scroll one page, the others you are testing scroll. Click somewhere on one page, that same click happens on the others. I asked them about how the heck it does that when there is so much that could interfere. Matthias and Florian explain:
We have been developing Ghostlab, a tool for cross-device and cross-browser testing your websites. In essence, it syncs any number of clients that view a website and makes it easy to go through that site and make sure it works great and looks good on all viewports and platforms. A central component is the replication of events across clients - when the user clicks on a button, scrolls, or enters text into a form field on one client, we have to make sure that the very same thing happens on all others.
Capturing missed events
The client side script component of Ghostlab is listening for all sorts of events that happen, tries to catch them, and replicate them to all the other clients. At some point, we noted that we didn't quite catch all events. We had to figure out what the problem was, and came up with a solution that allows you to catch any events that happen on your site, no matter how they are handled by any custom JavaScript.
How can it be that you are listening for an event, but don't catch it? That's because any event handler has the option of doing several things with an event. You'll know the ability to prevent the default action usually taken by the browser (preventDefault()). It allows you, for example, to have a link (<a>) for which the user does not go to its href on a click event.
In addition to telling the browser to not do the default action whenever the event occurs, an event handler can also stop the propagation of an event. When an event is triggered on an element, say a link, any event handler that is attached to this specific element will be allowed to handle the event first. After it is done, the event will bubble up until it reaches the document level. Every listener for this event at any parent of the original element will be able to react to the event - that is, unless a lower event handler decides to stop propagation, in which case the event will no longer go further up in the DOM.
Our example 1 demonstrates this. When you click on the inner div (Level 3), the click handler for this element will handle the event first. Unless it prevents propagation, the parent elements (Level 2, Level 1) will afterwards be able to react to the event in order. In case you tick the "Stop Propagation" checkbox, the event handler will prevent further propagation - so, click events on Level 3 will no longer reach Levels 1 and 2, and click events on Level 2 will no longer reach Level 1.
In example 2, we demonstrate the effect of stopping immediate propagation. This method implicitly stops the bubbling up of the event, so if there were any parent elements, we would observe the same behavior as in example 1. In addition, it also prevents any additional handlers of the same event on the same element from being executed. In our example, we have to click event handlers registered on our element. If we choose to stop immediate propagation, only the first responder will be able to handle the event, and after calling stopImmediatePropagation, no other handler will be called.
So if you would want to listen to all the events that happen in the DOM, that's quite difficult. To prevent missing events due to cancelled bubbling up, you'd have to register all the event handlers on every single element of the DOM. And even then, in case a developer chooses to stop immediate propagation, this would only work if you were the first one to register for the event.
If we want to be absolutely sure that we are informed of any event, no matter what its handlers do with it, we have to get in the loop at the very beginning of event registration. For that purpose, we override the addEventListener function of the EventTarget object. The basic idea is simple: every event handler registration will, in the end, call this method. If we override it, we have full control of what happens when any event handler registers.
The original addEventListener function takes an event handler function (the "original event handler") as its second argument. If we don't override the addEventListener function, the original event handler function will be called whenever the specified event occurs. Now, in our custom addEventListener function, we simply wrap the original event handler in our own event handler (the "wrapper function"). The wrapper function contains any logic we require, and can ultimatelly call the original event handler - if we desire to do so.
Example 3 demonstrates this. The three click events attached to the "Level" elements are registered through our custom addEventListener function, so any time a click event occurs on these elements, our wrapper function is called. There, we observe the status of the checkbox - if it is ticked, we simply do not call the original event handler, thereby preventing any click events to trigger any original event handler. A little side note: if you want to make sure that you take control of all events, you have to make sure that you override the addEventListener function before any event is registered.
While this solution has helped us improve Ghostlab, you may ask yourself what this could be good for? Well, there are several possibilities. We have sketched two possible use cases below - if you can come up with any other, please share!
Possible Application 1: Event Visualizer
There are tools to help you visualize events on your website (we love, for example, VisualEvent Allan Jardine). Using our technique we can quickly implement such a tool ourselves. In our example, for every registered event, we simply draw a little square on top of the element for which the event was registered. On hover, we display the source code of the (original) registered event handler function.
Instead of drawing event indicators onto the screen, you can also display that information in another manner. This example shows you a tabular overview of the registered events on any page (given that you inject the code into it), and updates the triggered events in real-time. This can, for example, be helpful when you are experiencing performance issues and are suspecting it might be because there are too many event handlers.
Events are just a small part of the huge complex world of Front End Development. At Vanamco we are excited to bring you tools that help simplifiy and streamline your processes whilst keeping you upto date with bets practice.
Take our quiz and win a free Ghostlab license
We've put together a JavaScript quiz to test your knowledge! Two winners will be chosen to win a free Ghostlab license and more.
When you change a CSS properties value, the browser needs to react to your change. Some values change the layout of the page. For instance, a change in width requires the browser to update layout, then "paint" any pixels that have changed, then "composite" them together. That's a lot of work. Some CSS properties can be changed more inexpensively. For instance, a change in background-image doesn't require any layout changes, but does require paint and composite.
Paul Lewis did all the research on which properties do what. A good reference to help you think about what kind of changes you can make that are easy for the browser (thus fast) and what kind of changes are hard (thus slow).
Dave Rupert does an in-depth analysis of his own responsive site to figure out where he's at, identify places to improve, and evaluate the idea going around that responsive design is at fault for bloated websites.
Mark Otto takes us on a tour of the CSS of GitHub.com. So great to read a straight-talking bit like this, warts and all.
This is 90% the same as we do things at CodePen. One major difference is that they combine all their CSS into two files and just serve them on all pages. I suppose that way once you've visited any page of GitHub.com, you've browser-cached all the CSS you'll ever need there. I have tended to go with a global.css for everywhere and a section.css, which leverages browser cache for the bulk of it while keeping the total CSS weight per-page down.
The following is a guest post by Tobias Günther. Together with his team, Tobias creates Tower, a Git client for Mac. I personally use it every day. Just this week, they released version 2 of Tower, which I was a beta tester for. I'll let him explain all about Tower, why they created it, and what's new in this version.
Most people wouldn't call version control their hobby. But in the last few years, people have also become aware of how valuable it is. Nowadays, you won't find a top programmer, web developer, or web designer who doesn't use version control. In part because it helps you produce better results and makes collaboration easy. But also because it can save your life when things go wrong.
When we started out creating Tower in 2010, we had a goal clearly in mind: make Git, the best version control available, easier to use. Since then we've made this true for over 35,000 customers in companies like Apple, Google, Amazon, and Ebay.
When we started working on version 2 of Tower, we knew this was a chance to go one step further: to make Git not only easier, but indeed easy to use.
How to Solve a Conflict with Confidence
Merge conflicts have always scared me to death. Especially since I'm a "not so technical" person. I never really understood what had happened - and, more importantly, how I could fix it.
One of my main problems with conflicts is that the situation is so abstract and hard to grasp. That's why we introduced a visual way to solve conflicts in Tower 2.
Thereby, I can now understand the situation more easily - by clearly seeing which files clashed, how exactly they looked, who worked on them, and which commit introduced the changes. And I can then decide how the solution should look - by simply clicking the files I want in the final resolution.
How to Clone a Repository in No Time
I don't like to clone a repository. Because, although it's actually a simple process, it's also a tedious one: first, I have to log into my service account on GitHub, Bitbucket, or Beanstalk. Troubles start right there: as if fumbling with usernames and passwords wasn't enough, new authentication procedures now require to create passcodes on your phone, generate personal access tokens, or jump through various other hoops. After that, I have to find my repository and copy the right URL - having to choose from various options like ssh, https, or git URLs.
A new service account manager in Tower 2 lets me configure my accounts on GitHub, Bitbucket, and Beanstalk once - and then never deal with authentication and repository URLs again.
I now get a neat listing of all my repositories in these accounts. I can clone a repository to my local machine with just a single click. Or, create a new one in the account right from within Tower.
Above all: I don't need to wrestle with usernames, passwords, tokens, or URLs anymore.
How to Understand What Happened in Your Project
Any project evolves in little steps. If I want to keep up with the project, it's these little steps that I need to understand. Therefore, it's vital for me that I get all the information I need in a digestible way. In the new version of Tower, we've put quite some effort into all things history:
You can see the commits that you would receive when pulling from a remote - so you can clearly understand what will happen when you finally do.
You can choose between multiple different view styles - so you have the visualization that's best for you.
You can directly access a commit's file tree - so you can inspect all of your project's files at any point in time.
And, finally, the search functionality now supports file paths - so you can easily see only the commits that changed a certain file.
How to Undo Mistakes
My attitude towards mistakes has changed - although I make just as many as I used to. What has changed is that I don't fear them as much. In the past, I was hesitant about making larger changes on a project, or trying out something new. Because I knew it was hard to recover from a mistake if one should happen.
After using Git for a while, I began to understand that I could undo everything. Git's undo features allow you to recover from anything - but the corresponding commands are spread all over the system.
In Tower, we've made these things available really easily: I can fix my last commit, restore any historic version, revert a certain commit's effects, or discard local changes in my working copy. It's great to know that I can't mess up.
How to Create Meaningful Commits
When I began using version control with Subversion, I essentially abused it as a backup system: I crammed all my changes into a commit just to make sure they were safely stored in the repository. Accordingly, my commit messages read like this:
Fixed bug #312, implemented login feature, restructured navigation. Oh, and by the way, changed everything else, too…
When a teammate (or even myself) looked at this bloated commit, none of us knew what had actually happened.
I hadn't understood version control back then. And to be fair, I hadn't had the right tools at hand. Using Git, I understood that going through a project's commit history should allow you to see how the project evolved. But this will only work under one condition: only related changes should be committed together.
For example, fixing two different bugs should produce two separate commits. Small commits make it easier for other developers to understand the changes and roll them back if something went wrong.
Git helps create such granular commits with tools like the "Staging Area": it forces me to really craft a commit and determine which changes exactly I want to include. I can even decide on the level of a single changed line if this modification should be part of the next commit or not.
However, especially things like the latter are rather clunky to use on the command line. That's where Tower fits in nicely, making such powerful features easily accessible with a clean interface.
How to Optimize Your Workflows
Using Git can be complex. But the increase in quality, confidence, and reliability that you gain by using it is definitely worth the effort. Reducing this effort is exactly what we wanted to do with Tower 2. We want to make using Git faster and easier. Let's take some examples of where this shows in Tower:
You don't have to download new changes for a project manually. Instead, Tower now automatically and regularly performs a "Fetch" operation in the background. Thereby, you instantly know if something new is available on the remote server.
The app automatically saves your current changes to the Stash (a kind of clipboard) when you're switching branches, pulling, or rebasing. Actions like these are best performed with a clean working copy - and Tower saves you the hassle of remembering and doing it.
A new "Open Quickly" dialog lets you find and open any project in seconds. After typing just a few characters from the project's name, Tower will offer you any matching repositories.
We've made committing much quicker. The UI for the commit message is now neatly integrated into the main interface; amending a commit can be done by simply holding the ALT key.
On popular demand, we've integrated the "git-flow" workflow into Tower. git-flow fans can now use it directly from within Tower.
You can use drag & drop to achieve rather complex Git tasks very simply in Tower: from creating and merging branches, to cherry-picking commits or applying even parts of a Stash.
We've implemented quite a list of new features in Tower 2. But above all, we focused on ease-of-use: a good tool, we believe, should help you become a better professional - easily.
These are overview articles covering lots of stuff relating to SVG. Why to use it and the basics of how to use it. Mostly: <img>, background-image, <svg>, <iframe>, and <object>/<embed>.
SVG is particularly good for icons. Besides the resolution independence and styling possibilities, the shapes for icons are generally simple and thus the files size is tiny and can be combined into a single web request like a sprite or icon font.
In SVG you can fill shapes with colors and gradients, but you can also specify a <pattern> to fill with. <pattern>s are groups of other SVG elements that repeat, so it's kinda like putting some <svg> in your <svg>.
The browser window you are looking at right now is a viewport. If there was an <iframe> on the page, that would have its own viewport. So too does <svg>! It's a little tricky though, because SVG also has a viewBox which is the actual space in which the SVG elements are drawn. That, combined with preserveAspectRatio, determine how and where the SVG are drawn within that viewport.
Whereas a fill in SVG is just an attribute (or applied in CSS), SVG gradients are literally a special element like <linearGradient>. CSS has gradients as well, but because SVG gradients (can be) in the DOM, there might be cases where they are easier to manipulate.
Plus they work in IE 9 where CSS gradients do not.
The <use> element in SVG allows you to copy a bit of SVG for use again elsewhere. But that only works for inline SVG. If you wanted to use just particular bits inside an SVG file in CSS (background-image) or HTML (<img>), you'll need fragment identifiers.
SVG has a <text> element which allows you to add (you guessed it) text to a graphic. Real, native web text, not outlines of characters, which 1) makes it more useful (selectable, searchable) 2) makes it more accessible 3) keeps the size down. Just like regular web text, the fonts you have available are limited to what is available on a given browser/platform/version or what is loaded via @font-face.
There are a variety of ways to animate SVG. SVG syntax has an tag specifically for it, although it's a touch complicated. That's often referred to as SMIL (Synchronized Multimedia Integration Language). If you're using inline SVG, you can also target the SVG (or shapes within) with CSS and animate using CSS animations. You can also use JavaScript to manipulate (and thus animate) SVG.
Fabric.js (SVG-to-canvas & canvas-to-SVG) by Juriy Zaytsev
Organizing SVG
Much like there are HTML tags to organize content, there are SVG tags to organize graphics. For instance, The <g> (group) tag is analogous to <div>. It doesn't have much meaning all by itself, but it's useful because it means you can style it and let those styles affect the entire group.
A common way to work with SVG on a site is to have a "folder full of SVGs" that you intend to use throughout the site. For performance reasons, you wouldn't want to request each one individually. These build tools help combine them together in one way or another.
IcoMoon by Keyamoon (select icons you want to use, output just that as SVG sprite. Drag and drop on your own SVG. Save project to update later.)
Learn to use vector editing software. (see section)
Download vector stuff from the internet. Remember anything vector can be turned into SVG (e.g. you might find have an .eps or .pdf, but it has vector stuff in it, so open/manipulate in Illustator, Save As .svg).
Stock photo sites typically have a way to return only vector search results. (e.g. Shutterstock or Vecteezy)
Remember fonts are vector, thus icon fonts are vector, thus any icon in an icon font set can be SVG. (Download and activate font, type character in Illustrator document in that font, convert to outlines, Save As .svg) Here's a big list of icons fonts by me.
There are CSS filters, which allow for familiar things like blur. SVG has its own set of filters (which is where the CSS ones came from) that include those familiar things, but also much more, including weird and unique things more reminiscent of Photoshop filters. SVG filters can be applied to HTML content as well as SVG content.
One of the advantages of inline SVG can be the fact that no web request needs to be made. The information to draw that SVG is right there. Data URI's (sometimes "Base 64" encodings, sometimes actual <svg> syntax) also do no-request rendering (that's the whole point, really.)
It's very smart to optimize images before use on the web. For raster images, even after you've saved at the smallest, lowest quality you are comfortable with, tools like ImageOptim can trim off even more file size without further affecting quality.
That same thing applies to SVG!
SVGO is a command line tool that optimizes SVG. And it has a Grunt task.
Kyle Foster has a lot of information about SVG optimization including two videos (one, two) and a slide deck.
I'm not an accessibility expert, but from what I can tell, SVG gets a decent nod from the accessibility community. Of course, you still have to do it right.
One of the advantages of SVG is that the shapes are in the DOM. For instance, perhaps a <svg> has a <rect> and a <circle>. Through JavaScript, you could bind an event handler to just the <rect> and a different one to just the <circle>.
SVG is supported in all the current versions of all browsers and several versions back. The biggest concern is generally IE 8 and down, and Android 2.3 and down. But that's just basic SVG support (inline, <img>, CSS background). There are sub-features of SVG that have different levels of browser support. Can I Use... does a good job of tracking that stuff.
If you need to support browsers old enough to not support SVG (see the Browser Support section), there are plenty of ways to handle fallbacks to resources that do work (e.g. replacing an image.svg with an image.png or the like).
SVG for Everybody by Jon Neal. Syntax: <svg><use xlink:href="spritemap.svg#icon"></use></svg>. Either just works, or Ajaxs and replaces the
SVG injector from the Iconic folks (a really nice icon set as inline SVG and every kind of fallback). Syntax: <img data-src="svg/thumb-up.svg" data-fallback="png/thumb-up-green.png">. Will inject inline SVG or a fallback.
SVG Tiny 1.2 (Not really used as far as I know, even though you can export as it from Illustrator. It was intended for stuff like old Blackberry's)
SVG 1.2 Tiny is a profile of SVG intended for implementation on a range of devices, from cellphones and PDAs to laptop and desktop computers, and thus includes a subset of the features included in SVG 1.1 Full
SVG 2 (Editor's Draft Status) Apparently will have stuff like stroke positioning (inside, outside, middle).
This version of SVG builds upon SVG 1.1 Second Edition by improving the usability of the language and by adding new features commonly requested by authors.
Misc
Obligatory random things!
Your .svg files not showing up even though you linked them correctly? Your server might be sending the wrong content-type (text/xml instead of image/svg+xml). Fix with .htaccess.
The overall point here by Harry Roberts is to keep the specificity on your selectors as low as you can. The end result of that effort is:
More reusable styles.
When you need to override that style, you have opportunities to nudge that specificity a bit higher while still being reusable and not fighting yourself.
I'm a big fan of resetting box-sizing to border-box, so much that we have a special day of the year around here. But there is a little adjustment to setting it that seems like a pretty good idea.
Credit on the inheritence idea to Jon Neal here, who says:
This will give you the same result, and make it easier to change the box-sizing in plugins or other components that leverage other behavior.
Explaining further, let's say you have a component that was just designed to work with the default content-boxbox-sizing. You just wanted to use it and not mess with it.
.component { /* designed to work in default box-sizing */ /* in your page, you could reset it to normal */ box-sizing: content-box; }
The trouble is, this doesn't actually reset the entire component. Perhaps there is a <header> inside that component that expects to be in a content-box world. If this selector is in your CSS, in "the old way" of doing a box-sizing reset...
/* This selector is in most "old way" box-sizing resets */ * { box-sizing: border-box; }
Then that header isn't content-box as you might expect, it's border-box. Like:
This isn't a majorly huge thing. You might already be using the box-sizing reset the "old way" and never have gotten bit by it. That's the case for me. But as long as we're promoting a "best practice" style snippet, we might as well hone it to be the best it can be.
The following is a guest post by Ben Edwards. I saw Ben tweet about a simple Sass @mixin that allowed you designate bits of CSS as being "critical" - the idea being to load that critical CSS first and defer the loading of the rest of the CSS until later. A clever idea, and one that is getting very popular in the web performance crowd. I thought I'd get Ben to introduce these ideas in more detail for us.
Google PageSpeed Insights and my web pages; it was a match made in heaven, until things changed... PageSpeed started telling me I needed to optimise my CSS delivery, that my CSS files were render-blocking, that none of the above-the-fold content of my page could render without waiting for those files to load, and that I should in-line the critical portions of those files directly into my HTML.
Go home PageSpeed, I cried, who in their right mind wants a mass of CSS in their HTML? I'm a legitimate professional, I have a workflow don't you know? I scoffed.
This had long been my stand point until I read the following tweet:
I'd like to see a site like CSS Zen Garden, but where developers try to make
I've long committed myself to getting my web pages the best possible scores from webpagetest.org, and that required a change of workflow, so why shouldn't I change it for PageSpeed? Now, if you're already using Google's mod_pagespeed module, put your feet up and give yourself a pat on the back as the module has you covered. For those of you like myself who aren't, here's how I went about it.
Here comes the science
To solve the problem, I first needed to understand what PageSpeed was telling me. External stylesheets (read those included via link tags) are render-blocking. This means that the browser won't paint content to the screen until all of your CSS has been downloaded. Couple this with the fact that if the amount of data required to render the page exceeds the initial congestion window (typically 14.6kB compressed) it will required additional round trips between the server and the user's browser. This all adds up to additional network latency, and for users on high latency networks, such as mobile, can cause significant delays to page loading.
PageSpeed's recommendation is to split your CSS into two parts; an in-line part that's responsible for styling the above-the-fold portion of the content, and the rest, which can be deferred. Now before we get hung up on whether the fold exists or not, let's just agree that anything we can do to get our data to our users as quickly as possible is a good thing, right?
Determining what is critical
Determining which portions of our CSS are critical required inspecting my web pages at "mobile" and "desktop" sizes, then taking a snapshot of the CSS rules applied to the elements visible in the viewport. This seemed like a daunting task, but fear not, some very smart people were there to help:
Pair this with Filament Group's loadCSS I can asynchronously load the remaining below-the-fold CSS like this:
... <div class="thing2"> Hey, I'm totally below-the-fold </div> <script> /*! Modified for brevity from https://github.com/filamentgroup/loadCSS loadCSS: load a CSS file asynchronously. [c]2014 @scottjehl, Filament Group, Inc. Licensed MIT */ function loadCSS(href){ var ss = window.document.createElement('link'), ref = window.document.getElementsByTagName('head')[0];
ss.rel = 'stylesheet'; ss.href = href;
// temporarily, set media to something non-matching to ensure it'll // fetch without blocking render ss.media = 'only x';
ref.parentNode.insertBefore(ss, ref);
setTimeout( function(){ // set media back to `all` so that the stylesheet applies once it loads ss.media = 'all'; },0); } loadCss('things.css'); </script> <noscript> <!-- Let's not assume anything --> <link rel="stylesheet" href="things.css"> </noscript> </body> </html>
A workflow for the future
Excellent news! PageSpeed is elated! It no longer complains of render-blocking CSS and is satisfied that above-the-fold content has been given the priority it deserves, but in this modern world of CSS preprocessors and front-end tooling, a manual process like the one above just isn't going to hack it.
An automated approach
Those of you looking for an automated mod_pagespeed style approach, and also familiar with Node (Apologies to those who aren't, but here at Clock it's a massive part of everything we do) will definitely want to look into Penthouse and Addy Osmani'sexperimental Node module, Critical, both of which provide means for in-lining or manipulating critical CSS as determined via the PageSpeed API. Now while a fully automated workflow sounds like heaven the one thing that irks me with the current tools is that they don't take address the fact that any CSS rules that are in-lined are served again once the below-the-fold CSS is downloaded. And in the spirit of sending as little data as needed to our users, this feels like an unnecessary duplication.
CSS preprocessors to the rescue
Making use of your favourite CSS preprocessor for authoring above and below-the-fold CSS seems like a no-brainer to me and is something the Front-end team is currently experimenting with at Clock.
New projects lend themselves very well to this approach, and critical and noncritical CSS could be authored via some well structured @import rules:
/* critical.scss - to be in-lined */ @import "header";
/* non-critical.scss - to be asynchronously loaded */ @import "web-fonts"; @import "footer";
Should you're partials not lend themselves to this sort of structuring, Team Sass's conditional styles Compass plug-in Jacket can come in very handy. For example if your partial _shared.scss contained rules for both above and below-the-fold elements, the critical and noncritical rules could be wrapped by Jacket like so:
Then critical.css and non-critical.css could be edited as follows to result in the same CSS:
/* critical.scss - to be in-lined */ $jacket: critical; @import "shared";
/* non-critical.scss - to be asynchronously loaded */ $jacket: non-critical; @import "shared";
This approach also feels in-keeping with the way lots of the community is authoring media queries at a component level rather than in a global location, and could feasibly be used to define critical and noncritical CSS rules at a component level.
We're still working this stuff out
While the update to the web version of PageSpeed Insights is almost a year old now, I feel that the topic of critical CSS and prioritising above-the-fold content has only gained significant traction in the past few months.
I hope by giving you some insight into the way I've handled its authoring will entice you into incorporate it into your workflow. And make sure to keep a close eye on the tools outline above, as most are in the early stages of development and I expect exciting changes ahead.
That can be a pain, but you can usually find the offending element by surfing around the ol' DevTools and selecting elements until you find something that extends too far over to to the right (off-page to the left doesn't tend to trigger a scrollbar the same way) and adjusting it.
Sometimes I use the "Delete Node" feature of DevTools to remove stuff from the page until the scrollbar goes away. Then I know which element caused it and can drill down from there. Here's a super quick video of that kind of troubleshooting:
In some cases, there might be an element that is literally wider than the document is, which might cause horizontal overflow scrolling. You could use a little JavaScript to help you find the culprit.
var docWidth = document.documentElement.offsetWidth;
Another minor little trick that helps me sometimes is to scroll over so you can see offending overflow area, and then click and Inspect Element in that space to see if you can target the offending element.
Hidden Horizontal Overflow
Sometimes horizontal overflow is more elusive. Like when it doesn't trigger a horizontal scrollbar, but you can still expose the overflow by swiping with a touchpad or select-and-dragging.
I've made a basic demo of that scenario, see this GIF:
What's going on there is that there is an element that is positioned there, offscreen, with opacity: 0; so you can't see it. Normally that would trigger horizontal overflow and a horizontal scrollbar, but we're explicitly hiding it:
In most scenarios, when an element is hidden outside of the bounds of an element with hidden overflow, it's just kinda lost to the visual world. But with the document itself, you can still force a scroll over there. Weird but true. It's likely even a bug, since if you do overflow: hidden; rather than overflow-x: hidden; - it stops it. It's just more common and practical to use overflow-x.
Note this is an issue in desktop Blink or WebKit based browsers. Not an issue in Gecko or anything mobile that I've seen.
Having hidden offscreen elements isn't particularly rare. I think it's getting more and more common with, you know, animations! transitions! 3D fancy! material design! transitional interfaces! I ran into this issue designing the search form on CodePen that kinda slides out when you click a thing. Simplified, that would be like this:
The solution, in this case, is to hide the overflow on a parent element, rather than relying on the hidden overflow on the body.
Clever work by Alex Sexton to analyze CSS and find colors that are so close to each other they should probably be combined. You know, for efficiency and consistency.