wonko.com

Hi! I'm Ryan Grove: Sorcerer at SmugMug, lover of movies, eater of pie, connoisseur of awesome.

Posts tagged with “css”

Displaying items 1 - 9 of 9

Simple makefile to minify CSS and JS

I recently needed a quick and easy way to minify CSS and JS for the new YUI Library website (launching soon!). In the past I’ve written powerful and complicated tools for doing static asset management and minification, but this time I wanted something simple.

A good old-fashioned makefile turned out to be the perfect tool for the job. Here’s what I came up with. Feel free to use it in your own projects. This version requires the YUI Compressor, but that can easily be replaced with Closure Compiler, Uglify, or any other tool of your choice.

# Patterns matching CSS files that should be minified. Files with a -min.css
# suffix will be ignored.
CSS_FILES = $(filter-out %-min.css,$(wildcard \
	public/css/*.css \
	public/css/**/*.css \
))

# Patterns matching JS files that should be minified. Files with a -min.js
# suffix will be ignored.
JS_FILES = $(filter-out %-min.js,$(wildcard \
	public/js/*.js \
	public/js/**/*.js \
))

# Command to run to execute the YUI Compressor.
YUI_COMPRESSOR = java -jar yuicompressor-2.4.6.jar

# Flags to pass to the YUI Compressor for both CSS and JS.
YUI_COMPRESSOR_FLAGS = --charset utf-8 --verbose

CSS_MINIFIED = $(CSS_FILES:.css=-min.css)
JS_MINIFIED = $(JS_FILES:.js=-min.js)

# target: minify - Minifies CSS and JS.
minify: minify-css minify-js

# target: minify-css - Minifies CSS.
minify-css: $(CSS_FILES) $(CSS_MINIFIED)

# target: minify-js - Minifies JS.
minify-js: $(JS_FILES) $(JS_MINIFIED)

%-min.css: %.css
	@echo '==> Minifying $<'
	$(YUI_COMPRESSOR) $(YUI_COMPRESSOR_FLAGS) --type css $< >$@
	@echo

%-min.js: %.js
	@echo '==> Minifying $<'
	$(YUI_COMPRESSOR) $(YUI_COMPRESSOR_FLAGS) --type js $< >$@
	@echo

# target: clean - Removes minified CSS and JS files.
clean:
	rm -f $(CSS_MINIFIED) $(JS_MINIFIED)

# target: help - Displays help.
help:
	@egrep "^# target:" Makefile

To use this, save it as a makefile, customize it as necessary, and then run make minify to minify your .js and .css files. Minified files will be saved with a -min suffix alongside the originals. Only files that have changed since the last time you minified them will be processed.

This file is also available as a gist if you’d like to fork it and improve it. Enjoy!

LazyLoad 2.0.0 released

After quite a while without updates, I’ve finally released version 2.0.0 of LazyLoad.

LazyLoad is a tiny (only 1,541 bytes minified), dependency-free JavaScript library that makes it super easy to load external JavaScript and (new in this version) CSS files on demand. It’s ideal for quickly and unobtrusively loading large external scripts and stylesheets either lazily after the rest of the page has finished loading or on demand as needed.

In addition to CSS support, this version of LazyLoad also adds support for parallel loading of multiple resources in browsers that support it. To load multiple resources in parallel, simply pass an array of URLs in a single LazyLoad call.

Downloads

Usage

Using LazyLoad is simple. Just call the appropriate method — css() to load CSS, js() to load JavaScript — and pass in a URL or array of URLs to load. You can also provide a callback function if you’d like to be notified when the resources have finished loading, as well as an argument to pass to the callback and a scope in which to execute the callback.

// Load a single JavaScript file and execute a callback when it finishes loading.
LazyLoad.js('http://example.com/foo.js', function () {
  alert('foo.js has been loaded');
});

// Load multiple JS files and execute a callback when they've all finished.
LazyLoad.js(['foo.js', 'bar.js', 'baz.js'], function () {
  alert('all files have been loaded');
});

// Load a CSS file and pass an argument to the callback function.
LazyLoad.css('foo.css', function (arg) {
  alert(arg);
}, 'foo.css has been loaded');

// Load a CSS file and execute the callback in a different scope.
LazyLoad.css('foo.css', function () {
  alert(this.foo); // displays 'bar'
}, null, {foo: 'bar'});

Supported Browsers

  • Firefox 2+
  • Google Chrome (all versions)
  • Internet Explorer 6+
  • Opera 9+
  • Safari 3+
  • Mobile Safari (all versions)

Other browsers may work, but haven’t been tested. It’s a safe bet that anything based on a recent version of Gecko or WebKit will probably work.

Caveats

All browsers support parallel loading of CSS. However, only Firefox and Opera currently support parallel script loading while preserving execution order. To ensure that scripts are always executed in the correct order, LazyLoad will load all scripts sequentially in browsers other than Firefox and Opera. Hopefully other browsers will improve their parallel script loading behavior soon.

Sadly, Firefox, Safari, and Google Chrome don’t provide any indication when a CSS file has finished loading. In these browsers, CSS load callbacks will execute after a short delay, but there’s no way to automatically guarantee that the CSS has finished loading before the callback is executed. Luckily, there’s a fairly painless manual workaround that you can use to detect when CSS has finished loading, but it’s not possible for LazyLoad to do it for you.

How to prevent race conditions when loading CSS with the YUI Get Utility

The YUI Get Utility makes it easy to dynamically load CSS and JavaScript on demand, but there’s one tiny gotcha: Firefox, Safari, and other Gecko/WebKit-based browsers don’t provide a reliable way to tell when a CSS file has finished loading. As a result, when you load CSS in one of these browsers using the Get.css() method, the onSuccess callback is fired instantly.

In many cases this isn’t a problem, but if you need to ensure that you don’t perform an action (such as appending HTML to the page) until after the CSS has loaded, this can cause a race condition. There’s no way for the Get Utility to automatically work around this problem, but with a little bit of extra effort on our part, we can work around it ourselves.

The trick is to include a rule at the end of the dynamically-loaded CSS file that will send a message to our script by setting a style property on a special element. Once we see that property change, we know the CSS has finished loading.

First, the contents of our CSS file:

/* ... imagine lots of other CSS here ... */

#css-done { display: none; }

And now the JavaScript that will load it:

(function () {
  var Y = YAHOO;

  Y.util.Get.css('example.css', {
    onSuccess: function () {
      var el, poll;

      function finished() {
        alert('CSS has finished loading.');
      }

      if (Y.env.ua.gecko || Y.env.ua.webkit) {
        el    = document.body.appendChild(document.createElement('div'));
        el.id = 'css-done';

        poll = function () {
          if (Y.util.Dom.getStyle(el, 'display') === 'none') {
            // Once the element's display property changes, we know the CSS has
            // finished loading.
            el.parentNode.removeChild(el);
            finished();
          } else {
            // The element's display property hasn't changed yet, so call this
            // function again in 50ms.
            setTimeout(poll, 50);
          }
        };

        poll();
      } else {
        finished();
      }
    }
  });
})();

Gaia Online is doing awesome things with LazyLoad

Jakob Heuser wrote to let me know about this big meaty blog post describing how he and the other folks at Gaia Online have implemented an impressive just-in-time CSS and JavaScript loader based in part on LazyLoad.

They've made some very nice improvements (such as replacing LazyLoad's queue-based loading with method chaining, which ends up being much faster) and fixed some bugs, all while keeping the library down to a slim 5.6K (minified) without any external dependencies. Very nice.

Minify combines, minifies, and caches JavaScript and CSS files on demand to speed up page loads

Web pages that refer to multiple CSS or JavaScript files often suffer from slower page loads, since the browser must request each referenced file individually. Most browsers will only make two simultaneous requests to a single server. The latency involved in opening multiple requests and waiting for them to finish before making new requests can result in a user-visible delay, and that can make your users sad.

Minify is a PHP library that attempts to fix this problem by combining multiple CSS or JavaScript files into one download. By default, it also removes comments and unnecessary whitespace to decrease the amount of data that must be sent to the browser. Most importantly, it does all of this on the fly and requires only a few simple changes to your existing web pages.

Before Minify:

<html>
  <head>
    <title>Example Page</title>
    <link rel="stylesheet" type="text/css" href="example.css" />
    <link rel="stylesheet" type="text/css" href="monkeys.css" />
    <script type="text/javascript" src="prototype.js"></script>
    <script type="text/javascript" src="example.js"></script>
  </head>
  <body>
    <p>
      Blah.
    </p>
  </body>
</html>

After Minify:

<html>
  <head>
    <title>Example Page</title>
    <link rel="stylesheet" type="text/css" href="example.css,monkeys.css" />
    <script type="text/javascript" src="prototype.js,example.js"></script>
  </head>
  <body>
    <p>
      Blah.
    </p>
  </body>
</html>

Minify takes about 15 seconds to download and install, has no dependencies other than PHP 5.2.1 or higher, works on any web server, and requires only minor modifications to your existing web pages. And it's free!

Visit the Minify website to learn more or, if you're already sold, download it now.

Remote JavaScript includes without the performance penalty (part 2)

In part 1 I showed you how to speed up page rendering by moving a remote JavaScript include (a Flickr badge in the example) into a hidden <div> at the bottom of the page and using a few lines of code to drop it into the desired location once it it's loaded. In this article, I'll show you how to use the Yahoo! UI Library's Animation Utility to make your dynamically-loaded content fade in smoothly so your visitors aren't distracted by its sudden appearance.

First we need to load the YUI libraries. To do this, we'll add the following lines just above our hidden "fakeflickr" <div> from part 1:

<script src="http://yui.yahooapis.com/2.2.2/build/yahoo-dom-event/yahoo-dom-event.js"></script>
<script src="http://yui.yahooapis.com/2.2.2/build/animation/animation-min.js"></script>

<div id="fakeflickr" style="display: none;">
  <script src="http://www.flickr.com/[...]"></script>
</div>

This will load the necessary libraries from Yahoo!'s own servers, which are so fast I heard they once did the Kessel Run in less than twelve parsecs.

Next, we'll need to add a teeny bit of CSS to give our "flickr" <dd> element a fixed height. This will keep the page layout from having to adjust when we populate that element with the Flickr badge content.

To figure out what height you should use, I recommend using Firebug to inspect the element and see what its height is once the badge is loaded. Then use that number in the CSS below, which you can either put inside a <style> tag in the page header or add to an external CSS file:

#flickr {
  background: url('loading.gif') no-repeat 48% 48%;
  height: 161px;
}

Notice the background image? You can grab that from ajaxload.info. It'll clue our visitors into the fact that the content in that area is being loaded in the background, so they'll be less surprised when they see it fade in.

Now all we need to do is make a few modifications to our JavaScript snippet from part 1 to make the Flickr badge fade in:

<script type="text/javascript">
  var flickr     = document.getElementById('flickr'),
      fakeflickr = document.getElementById('fakeflickr'),
      anim       = new YAHOO.util.Anim(flickr, {opacity: {to: 1.0}}, 1,
          YAHOO.util.Easing.easeBoth);

  YAHOO.util.Dom.setStyle(flickr, 'opacity', 0.0);
  YAHOO.util.Dom.setStyle(flickr, 'background', 'none');

  flickr.innerHTML = fakeflickr.innerHTML;
  fakeflickr.innerHTML = '';

  anim.animate();
</script>

And we're done. Easier than you thought it'd be, wasn't it?

Valid XHTML my arse

Just about every damn weblog on the Intertron these days has a stupid button, link, icon, or other doodad proclaiming that the site is XHTML or CSS compliant. Many weblog applications actually include these doodads in their default themes, and a surprising number of those default themes don’t even validate. Next time you notice one of these buttons, click on it and see if the site actually validates. Chances are it doesn’t.

Someday I’d like to put together a database of websites that claim to be compliant but actually aren’t. That’d be fun.

How not to win back the hearts of web developers

For years, Internet Explorer has gradually been losing popularity among web developers and designers. It’s been losing popularity among users too, but at a much slower rate. The reasons are simple: IE is chock full of quirky bugs and doesn’t even try to adhere to a wide range of web standards that other browsers have supported for years.

There’s been a lot of buzz lately about Internet Explorer 7. The developers have been very open about stating their intentions for the browser and soliciting feedback from web developers, designers, and users. The new IE team seemed to realize that Microsoft had made a lot of enemies over the years by neglecting their browser, and they really seemed to be working hard to reverse that. A lot of people started getting their hopes up. Would IE 7 finally make things easier on us poor developers and designers who spend ungodly amounts of time trying to make our sites work in IE without sucking in every other browser? Would Microsoft finally start caring about web standards?

IE 7 beta 1 was released last week to much fanfare, and then there was a huge backlash. Everyone and their dog wanted to know if IE 7’s support for web standards had improved, and everyone and their dog was disappointed when they discovered that nothing seems to have changed. Except the UI, that is. Everyone’s fears were confirmed: IE 7 is no less broken than IE 6. Oh, except that it now supports PNG alpha channels and has tabbed browsing. Woo.

Now the IE team is trying to deal with the backlash by saying that all the important bug fixes and CSS improvements are coming in beta 2. Their explanation, apparently, is that they’re on a tight schedule, have lots to do, and had to get the high priority stuff done for beta 1. Apparently the IE team’s priority list looks something like this:

  1. Implement tabbed browsing, because all the good browsers have it.
  2. Burn the official Windows UI guidelines and piss on the ashes.
  3. Move the menu bar under the toolbar, where nobody will think to look for it.
  4. Fuck around with the toolbar buttons and make it impossible for users to customize the toolbar layout.
  5. Pop up an annoying dialog on every other page the user visits, telling them they should enable Anti-Phishing Protection to make sure the website isn’t trying to kill their firstborn son.
  6. Make the Intarweb thingy work better.

The IE developers sure spout a lot of talk about fixing bugs and improving standards support, and I’m sure they’re good folks and they actually do care about these things, but releasing beta 1 with a bunch of stupid UI changes and virtually none of the improvements developers have been begging them to make for years was just stupid. For godssake, the beta was only available on MSDN! Who has access to MSDN? Developers. Not users.

They had an excellent chance to make a good first impression on developers with IE 7 beta 1 and they blew it. It doesn’t matter now if beta 2 has everything we ever asked for; beta 1 has already dashed our hopes.