Background

I’ve never liked having to solve a problem in a different way for different browsers. Yes, I’m talking about you Internet Explorer. We’ve dropped support for IE6 at Rally (it still has to work, but is far from pixel perfect), but we still have many customers using IE7+.

Before version 4, ExtJS catered to the lowest common denominator. That meant relying on images for rounded corners, shadows, etc. Even in modern browsers that support CSS3 styles for border radius and the like ExtJS used cross-browser compatible solutions. In ExtJS 4 this changed for the better, using the capabilities of CSS3 when available and falling back to images when not.

What if you want a different look and feel than what comes for free out of the box? Will you have to override styles for both modern and legacy browsers? Yes, you could do that. Thankfully ExtJS 4 introduced some theming shortcuts using SASS and Compass.

By overriding the ExtJS default theme variables or by creating your own Component UIs (used in the ‘ui’ option of component configs), the styles for modern and legacy browsers are generated for us. The best part? The images required for older browsers can be generated too!

It took me a bit of time to track down exactly what was possible with theming. Sencha’s guide gets you most of the way. Here’s the quick and dirty.

Guide to Theming

Note: I’m running on a mac. The steps are pretty similar for other systems. This is also based on ExtJS 4.0.6.

Setup

1. Install Compass

# gem install compass

This will install Compass and its dependencies, including SASS.

2. Download and Install Sencha SDK Tools

http://www.sencha.com/products/sdk-tools/

This includes the command line utilities used to generate images.

WARNING: It’s still in Beta (currently version 1.2.3). I’ve run into several rough edges.

3. Copy theme structure from ExtJS source

I’m starting with the Application Structure as recommended by the Getting Started Guide, section 2:

+ MyApp
    + app
    + extjs
    + resources
        + css
        + images
    - app.js
    - index.html

Copy everything from extjs/resources/themes/templates/resources/sass to MyApp/resources/sass.

Copy everything from extjs/resources/themes/images/default to MyApp/resources/images.

If you don’t follow the structure above for your app, update MyApp/resources/sass/config.rb to point to the correct location of the extjs source:

$ext_path = “../../extjs”

4. Make sure we can compile the styles.

Go to MyApp/resources/sass. Run:

# compass compile

This should generate MyApp/resources/css/my-ext-theme.css.

Add Customizations

We have two ways to customize the look and feel. We can change global SASS variables to affect the UI of every ExtJS component (easy), or we can create custom Component UIs (harder, but more flexible).

We’re going to start with a plain Ext.window.Window (a modal dialog) and modify it with both global variables and then a custom UI.

Theming with Global SASS Variables

In MyApp/index.html:

<html>
<head>
    <link rel="stylesheet" type="text/css" href="resources/css/my-ext-theme.css">
    <script type="text/javascript" src="extjs/ext.js"></script>
	<script type="text/javascript" src="app.js"></script>
</head>
<body></body>
</html>

And in MyApp/app.js:

Ext.application({
    name: 'My App',
    launch: function() {

        Ext.create('Ext.window.Window',{
            title: 'Custom Window',
            width  : 200,
            height : 150,
             modal: true,
             layout: 'fit',
             items: {
                xtype: 'form',
                html: 'Form Content',
                border: 0,
                buttons: [{
                   text: 'close'
               }]
            }
        }).show();

    }
});

You’ll see this window when you open index.html:

Out of the Box Ext Window

1. Update the $base-color

In MyApp/resources/sass/my-ext-theme.scss you’ll find this line:

// Insert your custom variables here.

Add a line below the comment with something like this:

$base-color: red;

2. From MyApp/resources/sass, run:

# compass compile

And we end up with this in Chrome:

Ext Window with red global style

You’re thinking “red? really?” Go find yourself a better color scheme: http://colorschemedesigner.com/.

Unfortunately it doesn’t look as great in IE.

Ext Window with red global style in IE8

3. To fix this, we need to generate the images for the corners. First, you have to fix a bug in Ext so that the images are pulled from the correct place.

Change line 62 of MyApp/extjs/resources/themes/lib/utils.rb from this:

images_path = File.join($ext_path, ‘resources’, ‘themes’, ‘images’, theme)

to this:

images_path = relative_path

4. Next, from the MyApp directory, run this command (Sencha SDK Tools must be installed):

# sencha slice theme -d ./extjs -c ./resources/css/my-ext-theme.css -o ./resources/images

In MyApp/resources/images/window and MyApp/resources/images/window-header we should have images with the color we set above. IE should now look like Chrome.

What types of variables can we set? They’re all listed in the extjs/resources/themes/stylesheets/ext4/default/variables directory.

Theming with Custom Component UIs

Let’s make the ‘close’ button on the window green. Instead of changing all ExtJS buttons to be green, we’ll create a component UI to do it.

Downside: The image generation support using “sencha slice theme” isn’t quite there yet. I’ve been able to generate images for custom panel UIs, but not custom buttons nor custom windows. You’ll have to revert to creating the images yourself.

1. Add a ui property to the button

The new app code:

Ext.application({
    name: 'My App',
    launch: function() {

        Ext.create('Ext.window.Window',{
            title: 'Custom Window',
            width  : 200,
            height : 150,
             modal: true,
             layout: 'fit',
             items: {
                xtype: 'form',
                html: 'Form Content',
                border: 0,
                buttons: [{
                   text: 'close',
                   ui: 'green'
               }]
            }
        }).show();

    }
});

The only change from what we had before was to add ui: ‘green’ to the button config.

2. Make a green button theme

in MyApp/resources/sass, create a new file called _myapp-buttons.scss, with the following content:

@include extjs-button-ui(
    /* UI + Scale */
    'green-small',

    $border-radius: $button-small-border-radius,
    $border-width: $button-small-border-width,  

    $border-color: green,
    $border-color-over: green,
    $border-color-focus: green,
    $border-color-pressed: green,
    $border-color-disabled: green,

    $padding: $button-small-padding,
    $text-padding: $button-small-text-padding,

    $background-color: green,
    $background-color-over: green,
    $background-color-focus: green,
    $background-color-pressed: green,
    $background-color-disabled: green,

    $background-gradient: $button-default-background-gradient,
    $background-gradient-over: $button-default-background-gradient-over,
    $background-gradient-focus: $button-default-background-gradient-focus,
    $background-gradient-pressed: $button-default-background-gradient-pressed,
    $background-gradient-disabled: $button-default-background-gradient-disabled,

    $color: $button-default-color,
    $color-over: $button-default-color-over,
    $color-focus: $button-default-color-focus,
    $color-pressed: $button-default-color-pressed,
    $color-disabled: $button-default-color-disabled,

    $font-size: $button-small-font-size,
    $font-size-over: $button-small-font-size-over,
    $font-size-focus: $button-small-font-size-focus,
    $font-size-pressed: $button-small-font-size-pressed,
    $font-size-disabled: $button-small-font-size-disabled,

    $font-weight: $button-small-font-weight,
    $font-weight-over: $button-small-font-weight-over,
    $font-weight-focus: $button-small-font-weight-focus,
    $font-weight-pressed: $button-small-font-weight-pressed,
    $font-weight-disabled: $button-small-font-weight-disabled,

    $font-family: $button-small-font-family,
    $font-family-over: $button-small-font-family-over,
    $font-family-focus: $button-small-font-family-focus,
    $font-family-pressed: $button-small-font-family-pressed,
    $font-family-disabled: $button-small-font-family-disabled,

    $icon-size: $button-small-icon-size
);

Notice also that the name of the component UI is ‘green-small’, and we reference it as just ui: green. This is only for buttons, where we could define ‘green-small’, ‘green-medium’, and ‘green-large’. Most component UIs don’t have multiple sizes.

How do you know all the options? Grep the ExtJS source for their usage:

grep -rl ‘\@include extjs-.*-ui’ extjs

For example, in the results you’ll see extjs/resources/themes /stylesheets/ext4/default/widgets/_window.scss, and in that file you’ll find a usage of “@include extjs-window-ui“. I use these as a starting point for any custom UIs I create. I haven’t found them to be documented anywhere else.

3. Add to main stylesheet

In MyApp/resources/sass/my-ext-theme.scss, add this to the bottom:

@import ‘myapp-buttons’;

4. Compile SASS

Go to MyApp/resources/sass, and run:

# compass compile

(You can also run “compass watch“, which will continually monitor the SASS files for changes).

Here’s what we end up with:

Ext custom component UI for a green button

(I know you love the green on red color scheme, admit it.)

And there we have it! Like I noted before, I haven’t been able to generate images for button UIs. If you’ve had better luck, please let me know!