Sunday, September 11, 2011

APEX CSS Repository: How to include background images easily using Data URIs

One of the challenges with loading CSS files into the APEX CSS Repository is dealing with references to background images.
In the screenshot from my demo site, I'm using a series of background images for the toolbar items.

Typically when you are hosting your own web-server, your web assets (images, CSS and JavaScript files) are loaded onto the web-server.
The CSS file will contain references to background images like:
.ico-add {
    background-image: url( ../images/icon/add.gif ) !important;
}
.ico-delete {
    background-image: url( ../images/icon/delete.gif ) !important;
}

The CSS rules reference the background-image by specifying a relative path to the image location.

If you loaded the CSS file into the APEX CSS Repository, you need to adjust the path to the image location to  use an absolute path like:
.ico-add {
    background-image: url( /ux/playpen/resources/images/icon/add.gif ) !important;
}
.ico-delete {
    background-image: url( /ux/playpen/resources/images/icon/delete.gif ) !important;
}

Adding a path alias to your web-server config to resolve the path on the server completes the task:
<Context path="/ux"       docBase="C:\playpen\web\ux" reloadable="true" crossContext="true" />

It's more complicated on Hosted APEX websites
On a hosted APEX website like apex.oracle.com you don't have access to the web-server so you need to load the files into the APEX Repository. Let's also assume you don't have access to 3rd party web-server to host the files either :)

So now your images, JavaScript and CSS files are loaded into the Repository and the page templates have been updated to include the JavaScript and CSS files by using the #APP_IMAGES# or #WORKSPACE_IMAGES# substitution tags:

<html>
<head>
    <title>#TITLE#</title>
    #HEAD#
    <link rel="stylesheet" href="#WORKSPACE_IMAGES#playpen.css" type="text/css">
</head>
Ideally, you would like to use the #APP_IMAGES# or #WORKSPACE_IMAGES# substitution tags within the CSS file:
.ico-add {
    background-image: url( #WORKSPACE_IMAGES#add.gif ) !important;
}
.ico-delete {
    background-image: url( &WORKSPACE_IMAGES.delete.gif ) !important;
}
But APEX 4.1 and earlier versions do not substitute either the hash tag or substitution variable alternatives shown.

This leaves two less desirable options:
  • embed the stylesheet directly in the page template, which resolves the substitution tags but adds overhead to every page, or,
  • replace the #WORKSPACE_IMAGES# reference with it's resolved name in the CSS file
    e.g. #WORKSPACE_IMAGES#add.gif becomes http://apex.oracle.com/pls/otn/wwv_flow_file_mgr.get_file?p_security_group_id=441224701954687600&p_fname=add.gif
    This makes the application less transportable.

Data URIs to the Rescue
Data URIs provide a way for data to be included inline in a web page as though it was an external resource. The data URI format RFC 2397 is pretty simple:
data:[<MIME-type>][;charset=<encoding>][;base64],<data>

Encoding our previous example looks like:
.ico-add {
    background-image: url(data:image/gif;base64,R0lGODlhEAAQAOZ3AHe8YHO6XDSBLIe4cF+eUKDZlq/fppfVi6vUnLTZparUnW61WFaYSJbUi4/CfTmFMH2+Z73ktpbTiXeuY0CJNnnBZNTrz4e4cbXbqI7MhHzDa7jdr1OWRZXUimyqWkKNNrvcr3i0ZIm+eIrCec7pyVCZQXy6ZKbQloXNeZPNiKDZlWirU3DBY0KLOODy3LbdrESMOrLbqInIf42+e53YknG+X3rGa2WrVYHMdXS3XLfgr7PZpZfMhTSCLMXowJTTirfaqrTapqXbm2+1WLXfrKbPlm+1WX25a3WsYT2INGajVmuqWH6yaXyxZzeEL0OPOHqvZUeOPLPdqYO1bZnRi5jMinm9bsvmw37Hb6TOlGmlWH25an28Zn7BabTdqobJejyHMozLgV2cTY7Sg5LFgICzatzx2K/fpX+5bXS3W5jMh4rPfYbJfNfu0oS2bmqsW77itleeRXK5W5jLhpfHhXC4Wf///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAHcALAAAAAAQABAAAAengHeCg4SFhnduZU0Th4MXWVcWJBtoWocDIC4RQgVnPiMEhQNAZjQpGQ0dYzpHDIR0bSo/MnZ2KBpYRHGDUy8GYWxWtW8AAVRkUYJMcAdftc92dV0KLYJQUms40LV1EAhJgkhVEjYsN9x1cjwzD4MrXhU1ANwLOUFPhEpbMcV1RkNpdnjoUUhMCAxquJiYk2CJgEMcSjg4UUTEh4eN7sCgAMZJxo+DAgEAOw==) !important;
}
.ico-delete {
    background-image: url(data:image/gif;base64,R0lGODlhEAAQAOZwAPaEbPqdi7dKK+6Sj+BpavGdlvCMd/eTfvmSfvnLxPm5r9pkYfJ3Y99qavqjke2TjupcUMZVQsxbTPWmofi2qONuY8tZSbxQMviMdvSOgbxLMNFeU+l/eOl7cL5PNsFNM/3c2PNzXfupnPiUf/qah/q6rulbT/KtqvzHuupmXOqDff3b1PCRf89hU8RQNNpjX+R4cfivpOZaUu5mUvCFcNZgVPjDvvWjn/aroverocxSPPqTf/SinvN5Y++GfuljW/iGcPqrneNrYeh6deBlXPzUzfSgnuJwZrhKKuZWTOJ7dcBTOvaCaPCDe+6MgddiXN1qaPBkVu6KdfnTzPGcluZYTNxkY7pLLtBPPt5qZ8dXRfWvpb5TONddUfOno71RNeZWUO6EffeRfrhOLttpZPWsofGOevS0qttWS+ZZTtljXeddWe5qXuxfVOlZTf///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAHAALAAAAAAQABAAAAeigHCCg4SFhnBQVgtqh4MNDzZTCWdHG4cEJyAlDgEiKB0ShQReKyQsZiMIGBQVWoQqRQEHUgC1AD0xWINZW0EGbcDBDBkcS4JkCjs0b8zNbmwFHoIvOUBMzc4pVBeCT05iIW7i4yZNSleDaDhRM+NuVTI3LoQtQmVtEG5pSWA8NUiFLBCZ4OPHmjBGugg4FEHHkAEDYHxY2AgOly8axlTcOCgQADs=) !important;
}

While it looks much more complicated and bloated, Data URIs provide a performance benefit because they reduce the number of HTTP requests, allowing pages to load faster.


Most modern browsers support data URIs:
  • Firefox 2+
  • Opera 7.2+ - data URIs must not be longer than 4100 characters
  • Chrome (all versions)
  • Safari (all versions)
  • Internet Explorer 8+ - data URIs must be smaller than 32k
For IE6 and IE7 you need to use a separate browser dependent CSS file, but the rapid decline in usage for these legacy browsers will remove the issue over time.

How to encode Data URIs in your CSS file
I'm using CSSEmbed by Nicholas C. Zakas, a Java command line utility that reads in a CSS file, identifies the images referenced within, converts them to data URIs, and outputs the resulting style sheet.
The basic syntax is:

java -jar cssembed-x.y.z.jar -o <output filename> <input filename>
To learn more about the utility see Nicholas's blog on Automatic data URI embedding in CSS files.

3 comments:

web design company said...

I always get suck with the including of background images that too using the data URL's.Ur blog was helpful to me for some extent.Thank you !

Pete M said...

I learned a lot as a result of this post - all about Apache Ant, cssembed and generally making life easier for my other developers also.

Thanks very much, very relevant article!

Anonymous said...

Very good post.
I was just reaching this conclusion after trial and error, and was amazed I found a topic explaining exactly this.
Thanks!