Schalk Neethling

Scripting on Caffiene

Image Loading Helpers in the Bedrock, Aka mozilla.org, Codebase

| Comments

Developing for the modern web has a lot of challenges. Some has been overcome and addressed by the evolution of HTML, and there are lots still being actively discussed on the WHAT-WG mailing list, and more and more being implemented by the various browser vendors.

In fact it has grown way beyond just the evolution of HTML and is now an all encompassing goliath, we lovingly call The Open Web Platform. Then there is of course the device ecosystem. Growing and expanding at a dizzying pace. With larger form factors, devices with high density displays, audio and video capabilities that puts many DSLR cameras to shame.

This is great for users but, as mentioned earlier, presents real challenges for developers. One of these challenges is serving up different images based on the user's screen capabilities. When working on a large website such as mozilla.org, this is something that we face all the time. Because of all the platforms our products run on and, all the languages our content is localized in, we face an array of additional challenges.

In this post I will highlight some of the solutions that exist in the Bedrock, aka. mozilla.org, codebase. The hope is that you will find these useful in your own projects or, that it can act as a handy reference when you decide to contribute to the project, and for my own reference and learning. With no further ado, let's get right to it!

Python Helpers

A few of the helpers that we have is a combination of Python template tags, combined with JavaScript so, let's look at these first.

high_res_img()

As mentioned before, we often need to serve up two different versions of an image dependent upon the device's pixel denisty. When the image is not added as a background image using CSS, we need to dynamically swap them out using JavaScript.

The basic premise of how we do this is to have markup as follows:

Sample of generated image tag
1
2
3
<img class="js tab" src="img/new-tab-enhanced.png" data-processed="false"
     data-src="img/new-tab-enhanced.png" data-high-res="false"
     data-high-res-src="img/new-tab-enhanced-high-res.png" alt="Screenshot">

Writing all of that for each image is a laborious task so, we have a template helper that makes it simpler. You can have a look at the Python code that generates the above here on Github.

The JavaScript is as follows:

Image Helper JSSee it on Github
1
2
3
4
5
6
7
8
9
10
11
Mozilla.ImageHelper.initHighResImages = function() {
    $('img[data-src][data-high-res="true"][data-processed="false"]').each(function() {
        var $img = $(this);
        var src = $img.data('src');
        if (Mozilla.ImageHelper.isHighDpi()) {
            src = $img.data('high-res-src');
        }
        this.src = src;
        $img.attr('data-processed', 'true');
    });
};

All you have to write is the following:

Using the high_res_img helperDocumentation
1
2
3
high_res_img('img/new-tab-enhanced.png', {
    'alt': 'Descriptive text', 'width': '200', 'height': '100'
})

l10n_img()

Having text embedded in an image file is generally not a good idea. There are accessibility implications, usability issues, for example if an image does not load, the content that was embedded in it is lost as well. Then there is the l10n implications. The general rule of thumb therefore is, don't do it.

It sometimes does happen though, and in those instances it is useful to have a template helper to make the loading of images based on locale easy. In bedrock we have the l10n_img helper for this exact use case. In the template you simply write:

Using l10n_img
1
<img src="{{ l10n_img('firefox/os/have-it-all/messages.jpg') }}" />

The rest is handled by Python code. The only other detail you need to make sure of is that the images exist in the required location. This is further documented on Read The Docs.

If you need to load high res images as well, you can instead use the high_res_img helper above, setting the l10n parameter to true.

Using high_res_img with l10n
1
2
3
high_res_img('img/new-tab-enhanced.png', {
    'l10n': True, 'width': '200', 'height': '100'
})

platform_img()

Another scenario we run into every now and again is loading different images based on the users platform. Think for example screen shots. For this scenario we have the platform_img template helper. Again, you can see the Python code that handles loading the appropriate image here.

Using platform_img
1
platform_img('img/firefox/new/settings.png', {'alt': 'Firefox screenshot'})

As with the high_res_img helper, naming of the images are important. In the case of this helper, you need to append the platform to the image name, for example, settings-mac.png, settings-win.png etc. If those images also contain text that needs to be translated and, you need to load high res images, you simply need to pass two additional parameters:

Using platform_img with high_res and l10n
1
2
platform_img('img/firefox/new/settings.png', {'alt': 'Firefox screenshot',
        'l10n': True, 'high-res': True})

Update: The JavaSript side of all of this can be found here on Github.

Background Images in CSS

So far I only touched on helpers used inside HTML/Jinja templates. But what about loading high res images conditionally in CSS? For that we have a LESS mixin from RetinaJS that makes it dead simple:

1
.at2x('/media/img/firefox/personal/logo.png', 148px);

You can see the code for the mixin here or, checkout the RetinaJS repo for more.

I hope you find this post useful and that you can either reuse these directly or, use it as a springboard for your own helpers and mixins. Is there anything you use at work to make things like this easier across your codebase? Let me know in the comments.

Comments