Web Content Optimization: CSS Autoprefixer

CSS vendor prefixes are both a blessing and a curse.

They are a blessing because, as originally designed, they allow browser vendors to implement new CSS features that were not part of any final specification in a way that could be easily changed when the specification changes or is withdrawn; and, once the specification is finalized, vendors can drop the prefix and developers can use the new properties as they would any other CSS property.

They are a curse because, as good as the theory was, it never really worked that way. The web is littered with prefixed selectors long after the specification in question was finalized. In order to maintain backwards compatibility developers have to do multiple prefixed versions of a property even when the final version has been released.

For example, depending on how far back you need to support browsers, the code for rounded corners look like this:

.round {
  -webkit-border-radius: 12px; 
  -moz-border-radius: 12px; 
  border-radius: 12px;

  /* 
  Prevent background color leak outs 
  See: 
    
border-radius
http://tumble.sneak.co.nz/post/928998513/fixing-the-background-bleed */ -webkit-background-clip: padding-box; -moz-background-clip: padding-box; background-clip: padding-box; }

We can all agree that doing this kind of repetitive tasks is a pain. Fortunately there are several ways in which we can eliminate the duplication of work. We can create SASS mixins and place holder selectors where we can hardcode the prefixed versions. That would be good for the short term but doesn’t address the bloat problem in our CSS… eventually we will no longer need the prefixed versions but the CSS code will still be littered with prefixes that no one but older browsers really need or want.

A second alternative is use tools like Autoprefixer. It is another Node based tool that installs with the NPM command, like so:

[23:49:19] [email protected] npm install -g autoprefixer

and provides a command line tool by default. To get and idea of the options available you can use the autoprefixer --help command that produces a result like the one below.

[23:49:19] [email protected] typography-sass 16563$ autoprefixer --help
Usage: autoprefixer [OPTION...] FILES

Parse CSS files and add prefixed properties and values.

Options:
  -b, --browsers BROWSERS  add prefixes for selected browsers
  -o, --output FILE        set output file
  -d, --dir DIR            set output dir
  -m, --map                generate source map
      --no-map             skip source map even if previous map exists
  -I, --inline-map         inline map by data:uri to annotation comment
      --annotation PATH    change map location relative from CSS file
      --no-map-annotation  skip source map annotation comment is CSS
      --sources-content    Include origin CSS into map
      --no-cascade         do not create nice visual cascade of prefixes
      --safe               try to fix CSS syntax errors
  -i, --info               show selected browsers and properties
  -h, --help               show help text
  -v, --version            print program version

Given a list of browsers to support and a list of CSS files to inspect it will query Caniuse.com data to determine what, if any, vendor prefixes need to be added to your code and will insert those prefixes where appropriate.

Take for example the following CSS code:

a {
  width: calc(50% - 2em);
  transition: transform 1s;
}

and running Autoprefixer to add prefixes for the last 2 versions of major browsers using this command:

autoprefixer -b "last 2 versions"

produces:

a {
  width: calc(50% - 2em);
  -webkit-transition: -webkit-transform 1s;
          transition: transform 1s
}

While SASS mixins may be easier to work with if you’re just writing CSS; Autoprefixer makes a nice addition to a development toolchain. You don’t have to remember what supported browser need an extension for what property, particularly if you consider that different versions of a browser may have different prefixes for a property or none at all.

Web Content Optimization: CSS Triming with UNCSS

Unless you already work with a customizer or are familiar with SASS to know what imports to comment out in order to remove a feature from your CSS framework you are bound to have unused features bloating your CSS and making it bigger than it need to be.

Or it may be that your CSS has grown too large after accommodating feature after feature… without removing them your file will bloat to unmanageable sizes.

Tools like UnCSS allow you to remove unussed CSS selectors by following these steps:

  1. The HTML files are loaded by PhantomJS and JavaScript is executed.
  2. Used stylesheets are extracted from the resulting HTML.
  3. The stylesheets are concatenated and the rules are parsed by css-parse.
  4. document.querySelector filters out selectors that are not found in the HTML files.
  5. The remaining rules are converted back to CSS.

Installing UnCSS

UnCSS requires Node.js already in your system. To install UnCSS globally for all your applications, run the following command:

npm install -g uncss

Command Line Version

Installing UnCSS globally gives you the uncss command to work with UnCSS from the command line. Using this tool you can indicate the file or URL that contains the HTML and CSS.

Usage: uncss [options] <file or URL, ...>
    e.g. 
        uncss http://getbootstrap.com/examples/jumbotron/ > stylesheet.css
        uncss index.html > stylesheet.css

Options:

  -h, --help                            output usage information
  -V, --version                         output the version number
  -i, --ignore <selector, ...>          Do not remove given selectors
  -m, --media <media_query, ...>        Process additional media queries
  -C, --csspath <path>                  Relative path where the CSS files are located
  -s, --stylesheets <file, ...>         Specify additional stylesheets to process
  -S, --ignoreSheets <selector, ...>    Do not include specified stylesheets
  -r, --raw <string>                    Pass in a raw string of CSS
  -t, --timeout <milliseconds>          Wait for JS evaluation
  -H, --htmlroot <folder>               Absolute paths' root location
  -u, --uncssrc <file>                  Load these options from <file>

Using the command will produce a new css file (stylesheet.css) containing only the CSS rules used on the page. You can also use a wildcard match to pick all the html files in a directory (uncss dist/*.html > stylesheet.)

For more information refer to UnCSS’s Readme file

Automating the process

There are UnCSS plugins from several task runners and build systems that would allow developers to incorporate UnCSS into their development workflows:

You can incorporate the plugin into your workflow where it makes sense to you. Working with Grunt you can do the following to create a new css files with only the selectors used in your application html files:

uncss: {
  dist: {
    files: {
      'dist/css/tidy.css': ['app/*.html']
    }
  }
}

an equivalent Gulp task looks like this:

var gulp = require('gulp');
var uncss = require('gulp-uncss');

gulp.task('default', function () {
    return gulp.src('site.css')
        .pipe(uncss({
            html: ['app/*.html']
        }))
        .pipe(gulp.dest('./out'));
});

And in Broccoli:

var uncss = require('broccoli-uncss');
tree = uncss(tree, {html: ['index.html']});

For more detailed information, refere to the respective plugin documentation.

Web Content Optimization: CSS Using SASS

CSS can become very large and very convoluted if we’re not careful. This is particularly important when using third party libraries like Bootstrap or Zurb Foundation where most users download the full framework with large chunks of the library that they will never use but still load and push over the wire bloating the application and slowing everything down.

When you work with your own code it’s easier to slim down but it still makes sense to build a default set of CSS stylesheets for our applications and then selectively choose which ones to use based on the page or pages I’m currently working on.

I’ve chosen to address these size issues in the following ways:

  • Use SASS/SCSS as my CSS pre-processor to be able to compress the resulting CSS if needed
  • Autoprefixer to eliminate the need to prefix elements if not needed
  • UnCSS will remove CSS selectors that are not used in your HTML documents
  • Creating stylesheets inline to render a page’s critical path as fast as possible will also reduce the size of the CSS we bring from the network

Using SASS

SASS (syntactically awesome style sheets) is a superset of CSS 2 and 3 with added scripting features such as:

  • Program flow control (@if/@then/@else, @for, @while)
  • Convenience functions to automate CSS work (lighten/darken, saturate/desaturate colors)
  • Ability to nest selectors to keep our code DRY
  • Use of variables and placeholder variables
  • Mixins
  • The ability to work with partial files that we can import into our main SASS

For purposes of performance optimization we’ll worry about SASS output. Out of the box SASS provides multiple formats for the transformed CSS:

  • :nested
  • :compact
  • :expanded
  • :compressed

To understand the difference let’s look at the following example:

.widget-social {
  text-align: right;

  a,
  a:visited {
    padding: 0 3px;
    color: #222222;
    color: rgba(34, 34, 34, 0.77);
   }

  a:hover {
      color: #B00909;
  }
}

:nested

.widget-social {
  text-align: right; }
  .widget-social a,
  .widget-social a:visited {
    padding: 0 3px;
    color: #222222;
    color: rgba(34, 34, 34, 0.77); }
  .widget-social a:hover {
    color: #B00909; }

:expanded

.widget-social {
  text-align: right;
}
.widget-social a,
.widget-social a:visited {
  padding: 0 3px;
  color: #222222;
  color: rgba(34, 34, 34, 0.77);
}
.widget-social a:hover {
  color: #B00909;
}

:compact

.widget-social { text-align: right; }
.widget-social a, .widget-social a:visited { padding: 0 3px; color: #222222; color: rgba(34, 34, 34, 0.77); }
.widget-social a:hover { color: #B00909; }

:compressed

Note that the compressed stylesheep appears in one line. It’s wrapped here for display purposes

.widget-social{text-align:right}.widget-social a,.widget-social a:visited{padding:0 3px;color:#222222;color:rgba(34,34,34,0.77)}.widget-social a:hover{color:#B00909}

Which one you use will depend on the situation. My build process has two versions of the SASS conversion task: One will create an expanded version to use during development and the other will create a compressed version suitable for development. Because I use partials for most of the styles the local content will be one stylesheet, we only need to worry about external resources such as normalize.css stored in separate stylesheets. Since there are versions of normalize written in SCSS so we should be able to convert it to a partial (by prepending an underscore to the name) and import it so we only have one stylesheet to work with.

See Different SASS output styles for more information.

Web Content Optimization: Introduction

The tools for making content performant have improved considerably in the last few years. As designers we can now influence the size and the type of resources that the end users will see when they view our content.

In this series we’ll discuss strategies and tools to minimize the size of our web content: CSS, Javascript, HTML, images, video and audio, along with best practices in serving the content.

Good overview of the topic are:

A final note: This covers my experience and research. It is by no means an exhaustive guide. For more and deeper research into tooling and performance check Addy Osmani’s blog and Totally Tooling Tips on Youtube. Ilya Grigorik’s blog is a good source of information on the subject

Web Components Conclusion: Should we use web components now?

It depends on your target platforms. The only desktop browser to fully implement web components is Chrome (and by extension Opera) so the webcomponent.js polyfill must be used for any kind of meaningful support. It’s also important to note that the polyfills don’t work particularly well on mobile devices and I haven’t tested them in any e-book reader (where I assume they will not work.)

It depends on the the scope of your planned components. If your planning to progressively upgrade elements on your page then it makes sense to take the plunge and use them now. However, if you’re planning full blown applications based on components you may be better off waiting until the specifications finalilze and browsers add native support.

I had initial concerns about the technologies and their support but after the Polymer Summit the concerns were calmed and it became intriguing to play with Polymer again.

I have a few apps and elements that have been updated to 1.0