Mobile Zone is brought to you in partnership with:

Jorge is the author of three software development books: "Building a Sencha Touch Application", "How to Build a jQuery Mobile Application", and the "Ext JS 3.0 Cookbook". He runs a software development and developer education shop that focuses on mobile, web and desktop technologies. Jorge is a DZone MVB and is not an employee of DZone and has posted 50 posts at DZone. You can read more from them at their website. View Full User Profile

Creating a Custom Theme for a Sencha Touch Application

07.20.2013
| 4803 views |
  • submit to reddit

Changing the look and feel of your Sencha Touch app is an important step that you can take to give its users a unique experience. In this tutorial you will learn how Sencha Touch themes work, and what approaches you can take to create a custom theme. Then, you will use one of these approaches to change the look and feel of a sample application.

theming-8

The number of requests that I receive from readers of my Sencha Touch book and this blog suggests that creating Sencha Touch themes is a popular but not well understood subject. A Sencha Touch theme is essentially a number of cascading styles that are applied to the visual elements of the Sencha Touch Framework. When you generate an application using the Sencha Cmd tools, these styles are defined in a number of Saas files. When you create a build of the application, the styles are merged into a single css file, along with any application-specific styles that you defined.

Approaches to Creating a Custom Theme in Sencha Touch

Sencha Touch ships with a default theme (and a couple of themes specific to the BlackBerry and Windows Phone platforms) that provides the look and feel for your app if you do not use a custom theme. This is the look that most of Sencha’s samples feature, and the look that you get when you initially create an application. If you haven’t, you can easily notice it when you see its default “Triton blue” base color:

Sencha Touch NestedList

For your apps, you can use the default theme or venture into creating your own. If you want to create a custom theme, you can take two basic approaches.

  1. The simplest approach is to make modifications on top of the default theme that ships with Sencha Touch. This allows you to re-use the styles in the default theme, and only change those elements that you are interested in. For example, you can change the background color, font family, or base gradient used by the app at a global level. This approach requires a minimal amount of work on your part., and it is easy to pick up if you are already familiar with Saas and CSS.
  2. A more complex and arguably flexible approach is to create a theme from the ground up. While it allows you to control every aspect of the look and feel, it requires significant effort because you need to yourself define the styles of all the elements used by your app. Obviously, it also requires more specialized graphic design skills that you may or may not have.

In this tutorial you will use the first approach to create a custom look for an application. The app that you will use is based on the Adding a Login Screen to a Sencha Touch Application tutorial published on this blog.

Let’s get started.

Generating the Sample Application

You are going to use the Sencha Cmd tools to generate the skeleton for the application. Assuming you have already installed Sencha Cmd, let’s navigate to the directory where Sencha Touch is deployed, open a command window and type the following:

sencha generate app -path c:\stsamples\theme1 -name Theme1

This will give you an application directory just like so:

theming-1

At this point the Theme1 app is simply the boilerplate application that Sencha Cmd generates. Before diving into creating a custom theme, you will make a few changes to the app so it resembles the one created in the Adding a Login Screen to a Sencha Touch Application tutorial.

First, navigate to the app/view directory and create a Login.js file. In the file, you will define the Login view as follows.

Ext.define('Theme1.view.Login', {
    extend: 'Ext.Panel',
    alias: "widget.loginview",
    requires: ['Ext.form.FieldSet', 'Ext.form.Password', 'Ext.Label', 'Ext.Img', 'Ext.util.DelayedTask'],
    config: {
        title: 'Login',
        items: [
            {
                xtype: 'image',
                src: '../../../../img/login.png',
                style: 'width:80px;height:80px;margin:20px auto'
            },
            {
                xtype: 'label',
                html: 'Login failed. Please enter the correct credentials.',
                itemId: 'signInFailedLabel',
                hidden: true,
                margin: '0 10px',
                hideAnimation: 'fadeOut',
                showAnimation: 'fadeIn',
                style: 'color:#990000;margin:5px 0px;'
            },
            {
                xtype: 'fieldset',
                title: 'Login Example',
                items: [
                    {
                        xtype: 'textfield',
                        placeHolder: 'Username',
                        itemId: 'userNameTextField',
                        name: 'userNameTextField',
                        required: true
                    },
                    {
                        xtype: 'passwordfield',
                        placeHolder: 'Password',
                        itemId: 'passwordTextField',
                        name: 'passwordTextField',
                        required: true
                    }
                ]
            },
            {
                xtype: 'button',
                itemId: 'logInButton',
                ui: 'action',
                margin: '0 10px',
                text: 'Log In'
            }
         ],
        listeners: [{
            delegate: '#logInButton',
            event: 'tap',
            fn: 'onLogInButtonTap'
        }]
    },
    onLogInButtonTap: function () {

        var me = this,
            usernameField = me.down('#userNameTextField'),
            passwordField = me.down('#passwordTextField'),
            label = me.down('#signInFailedLabel'),
            username = usernameField.getValue(),
            password = passwordField.getValue();

        label.hide();

        // Using a delayed task in order to give the hide animation above
        // time to finish before executing the next steps.
        var task = Ext.create('Ext.util.DelayedTask', function () {

            label.setHtml('');

            me.fireEvent('signInCommand', me, username, password);

            usernameField.setValue('');
            passwordField.setValue('');
        });

        task.delay(500);

    },
    showSignInFailedMessage: function (message) {
        var label = this.down('#signInFailedLabel');
        label.setHtml(message);
        label.show();
    }
});

This view presents a Login form to the user. Since our goal in this tutorial is to create a custom theme, we will not go through the view in detail. You can learn all about it in the tutorial that this app is based on.

While in the app/view directory, go ahead and delete the Main.js file. It belongs to the boilerplate app template created by Sencha Cmd and we will not use it here.

In the same directory you will also create the MainMenu.js file, where you will define the MainMenu view as shown below.

Ext.define('Theme1.view.MainMenu', {
    extend: 'Ext.Panel',
    requires: ['Ext.TitleBar'],
    alias: 'widget.mainmenuview',
    config: {
        layout: {
            type: 'fit'
        },
        items: [{
            xtype: 'titlebar',
            title: 'Main Menu',
            docked: 'top',
            items: [
                {
                    xtype: 'button',
                    text: 'Log Off',
                    itemId: 'logOffButton',
                    align: 'right'
                }
            ]
            },
            {
                xtype:'container',
                layout:'vbox',
                items:[
                    {
                        xtype: 'button',
                        margin: '10px',
                        text: 'Option 1'
                    }, {
                        xtype: 'button',
                        margin: '10px',
                        text: 'Option 2'
                    },{
                        xtype: 'button',
                        margin: '10px',
                        text: 'Option 3'
                    }
                ]
            }
        ],
        listeners: [{
            delegate: '#logOffButton',
            event: 'tap',
            fn: 'onLogOffButtonTap'
        }]
    },
    onLogOffButtonTap: function () {
        this.fireEvent('onSignOffCommand');
    }
});

Next, you will move to the app/controller directory. There you will create the Login.js file, where you will define the Login controller as follows.

Ext.define('Theme1.controller.Login', {
    extend: 'Ext.app.Controller',
    requires:['Ext.Ajax'],
    config: {
        refs: {
            loginView: 'loginview',
            mainMenuView: 'mainmenuview'
        },
        control: {
            loginView: {
                signInCommand: 'onSignInCommand'
            },
            mainMenuView: {
                onSignOffCommand: 'onSignOffCommand'
            }
        }
    },

    // Session token

    sessionToken: null,

    // Transitions
    getSlideLeftTransition: function () {
        return { type: 'slide', direction: 'left' };
    },

    getSlideRightTransition: function () {
        return { type: 'slide', direction: 'right' };
    },

    onSignInCommand: function (view, username, password) {

        console.log('Username: ' + username + '\n' + 'Password: ' + password);

        var me = this,
            loginView = me.getLoginView();

        if (username.length === 0 || password.length === 0) {

            loginView.showSignInFailedMessage('Please enter your username and password.');
            return;
        }

        loginView.setMasked({
            xtype: 'loadmask',
            message: 'Signing In...'
        });

        Ext.Ajax.request({
            url: '../../../services/login.ashx',
            method: 'post',
            params: {
                user: username,
                pwd: password
            },
            success: function (response) {

                var loginResponse = Ext.JSON.decode(response.responseText);

                if (loginResponse.success === "true") {
                    // The server will send a token that can be used throughout the app to confirm that the user is authenticated.
                    me.sessionToken = loginResponse.sessionToken;
                    me.signInSuccess();     //Just simulating success.
                } else {
                    me.singInFailure(loginResponse.message);
                }
            },
            failure: function (response) {
                me.sessionToken = null;
                me.singInFailure('Login failed. Please try again later.');
            }
        });
    },

    signInSuccess: function () {
        console.log('Signed in.');
        var loginView = this.getLoginView(),
            mainMenuView = this.getMainMenuView();
        loginView.setMasked(false);

        Ext.Viewport.animateActiveItem(mainMenuView, this.getSlideLeftTransition());
    },

    singInFailure: function (message) {
        var loginView = this.getLoginView();
        loginView.showSignInFailedMessage(message);
        loginView.setMasked(false);
    },

    onSignOffCommand: function () {

        var me = this;

        Ext.Ajax.request({
            url: '../../services/logoff.ashx',
            method: 'post',
            params: {
                sessionToken: me.sessionToken
            },
            success: function (response) {

                // TODO: Implementation.
            },
            failure: function (response) {

                // TODO: Implementation.
            }
        });

        Ext.Viewport.animateActiveItem(this.getLoginView(), this.getSlideRightTransition());
    }
});

At this point you have defined the main components of the application. The last module on which you need to work is the application definition itself. In the theme1 directory, open the app.js file and modify it so it looks like the code below.

//<debug>
Ext.Loader.setPath({
    'Ext': 'touch/src'
});
//</debug>

Ext.application({
    name: 'Theme1',

    views: ['Login', 'MainMenu'],
    controllers: ['Login'],

    icon: {
        '57': 'resources/icons/Icon.png',
        '72': 'resources/icons/Icon~ipad.png',
        '114': 'resources/icons/Icon@2x.png',
        '144': 'resources/icons/Icon~ipad@2x.png'
    },

    isIconPrecomposed: true,

    startupImage: {
        '320x460': 'resources/startup/320x460.jpg',
        '640x920': 'resources/startup/640x920.png',
        '768x1004': 'resources/startup/768x1004.png',
        '748x1024': 'resources/startup/748x1024.png',
        '1536x2008': 'resources/startup/1536x2008.png',
        '1496x2048': 'resources/startup/1496x2048.png'
    },

    launch: function () {

        Ext.Viewport.add([
            { xtype: 'loginview' },
            { xtype: 'mainmenuview' }
        ]);
    },

    onUpdated: function () {
        Ext.Msg.confirm(
            "Application Update",
            "This application has just successfully been updated to the latest version. Reload now?",
            function (buttonId) {
                if (buttonId === 'yes') {
                    window.location.reload();
                }
            }
        );
    }
});

Now the application is finished and you can move on to creating a custom theme for it. But, where do you start?

Using Sencha Touch’s Global Sass Variables and Mixins

If you want to create custom themes, it is important that you become familiar with Sass and tools such as Compass, as Sencha Touch uses Sass to define the styles of the visual elements of an application.

The default Sencha Touch theme has a number of global Sass variables and mixins that you can modify in order to create a custom theme. They are defined in the Global_CSS Sass class, which is stored in the _Class.scss file generated by the Sencha Cmd tool when you create an application.

Sencha Cmd creates different versions of this file:

  • \theme1\touch\resources\themes\stylesheets\sencha-touch\default\var
  • \theme1\touch\resources\themes\stylesheets\sencha-touch\windows\var
  • \theme1\touch\resources\themes\stylesheets\sencha-touch\bb10\var

As you can see, besides the default theme, there are specific versions of the _Class.scss file for the Windows Phone and BlackBerry 10 themes that ship with the Framework.

In this tutorial you will focus on the version of the file located in the \theme1\touch\resources\themes\stylesheets\sencha-touch\default\var directory. This means that you will be creating a custom theme for all targeted devices except BlackBerry and Windows Phone ones. However, you can make changes to the three versions of the file if your application needs it.

Now that you know the concepts behind Sencha Touch themes, and have an app to experiment with, it is time to begin building your custom theme.

How to Change the Base Color of a Sencha Touch App

One simple modification that can drastically alter the look and feel of your application is to change the base color of the default theme. The theme’s base color is defined in the Global_CSS class through the $base-color variable. If you open the _Class.scss file located in the \theme1\touch\resources\themes\stylesheets\sencha-touch\default\var directory, you will find the following definition:

$base-color: #1985D0 !default; // Triton Blue

In order to change this base color, you will open the app.scss file located in the \theme1\resources\sass directory, and add an override for the $base-color value, right at the top of the file:

$base-color: #9ad019 !default;

// The following two lines import the default Sencha Touch theme. If you are building
// a new theme, remove them and the add your own CSS on top of the base CSS (which
// is already included in your app.json file).
@import 'sencha-touch/default';
@import 'sencha-touch/default/all';

// Custom code goes here...

// Examples of using the icon mixin:
// @include icon('user');

Now you will generate a build of the application by typing the following in the command window:

sencha app build

This will create a production build of the app in the build directory, \theme1\build\Theme1\production. If you run the application, you should be able to see the base color change as depicted in the following screenshot.

theming-5

How to Change the Font Family of a Sencha Touch App

Another simple but impactful change that you can make following the approach of overriding the default theme is to modify the app’s font family. To accomplish this you will take the same steps you took when changing the base color, only that the Sass variable you will change is called $font-family.

In the app.scss file located in the \theme1\resources\sass directory, you will add the $font-family override right after the base color override, as shown in the code below.

$base-color: #9ad019 !default;
$font-family: "cambria","serif"!default;

// The following two lines import the default Sencha Touch theme. If you are building
// a new theme, remove them and the add your own CSS on top of the base CSS (which
// is already included in your app.json file).
@import 'sencha-touch/default';
@import 'sencha-touch/default/all';

// Custom code goes here...

// Examples of using the icon mixin:
// @include icon('user');

Then, you will again generate a build of the application by typing the following in the command window:

sencha app build

You can check the results of the changes by running the app.

theming-6

How to Change the Background Color of a Sencha Touch App

The last change you will make in this tutorial relates to the background of the app’s full screen views. This background is defined through the $page-bg-color variable. You will override the value of this variable in order to switch the background from the default solid color to a nicer linear background.

In the app.scss you will override the $page-bg-color as follows.

$base-color: #9ad019 !default;
$font-family: "cambria","serif"!default;
$page-bg-color:linear-gradient(to bottom, rgba(252,255,244,1) 0%,rgba(233,233,206,1) 100%)!default;

// The following two lines import the default Sencha Touch theme. If you are building
// a new theme, remove them and the add your own CSS on top of the base CSS (which
// is already included in your app.json file).
@import 'sencha-touch/default';
@import 'sencha-touch/default/all';

// Custom code goes here...

// Examples of using the icon mixin:
// @include icon('user');

Then, one last build:

sencha app build

And the new background should be present when you run the app, as shown in the following screenshot.

theming-8

There you have it. In this article you learned the main concepts behind Sencha Touch theming features. You also learned that there are two approaches to creating a custom theme for your Sencha Touch application – Overriding elements of the default theme, or building a theme from the ground up. Finally, you used the first approach to create a custom theme for a sample application. Specifically, you learned how to change the base color of the app, the font-family, and the background of the full screen components.

Want to Show Off Your Themes?

I’d like to know what you have done with custom themes. If you’d like to share examples of custom themes you have created, please post the links in the comments section below.

Published at DZone with permission of Jorge Ramon, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)