Sencha Touch 2: Theming Apps for Multiple Devices

Note: This is part 2 of a 2-part series.

Recently we were approached by a customer who was interested in transitioning their application’s codebase from native ios/android over to Sencha Touch in order to support Microsoft devices. The thought of having to support the same app, developed using three separate and distinct codebases, was an expense that this enterprise was just not willing to bear. And, of course, even more fragmentation is on the horizon. While it’s still a bit unclear as to whether Blackberry will be able to gain market share, devices using the Tizen OS (supported by Intel and Samsung) should be hitting the market by year’s end. If there was ever a time to develop a viable “write once, run many” app development strategy, this is it.

Fortunately, Sencha Touch 2.2 supports all of the aforementioned operating systems (Tizen support is coming soon) and enables you to easily branch your CSS to theme your app for a particular mobile OS.

Creating a New Project

The first step towards theming your app is to create a new project using Sencha Command. Stylesheet branching requires you to develop your app using Sencha’s microloader framework that is installed when you create a new project. Note that at the time of this writing, projects created using Sencha Architect do not use the microloader framework.

You can create a new Sencha Touch project by completing the following steps:

  1. Open a Command Prompt
  2. Change directories to where you have Sencha Touch installed
  3. Type the following command:
    sencha generate app [appName] [path to app dir]

Branching to Device-Specific Stylesheets

You can branch to different stylesheets by modifying the “css” section of the app.json file that is present in the root of your Sencha Command-generated project as illustrated by the following code snippet which directs different CSS files to be loaded for blackberry 10, ie 10, and mobile safari/chrome:

"css": [
 {
  "path": "resources/css/bb.css",
  "platform": ["blackberry"],
  "theme": "Blackberry",
  "update": "delta"
 },
 {
   "path": "resources/css/ie10.css",
   "platform": ["ie10"],
   "theme": "Windows",
   "update": "delta"
  },
  {
    "path": "resources/css/sencha-touch.css",
    "platform": ["chrome", "safari", "ios", "android", "firefox"],
    "theme": "Default",
    "update": "delta"
  }
]

Note the following:

  • You can also designate different CSS files to be loaded for tablet vs. phone vs. desktop, although typically differences between form factors are handled through Sencha’s device profiles framework.
  • The theme names of Windows and Blackberry cause the Touch framework to branch internally. Therefore, you should not modify these names.

Keep Your CSS Files Under 400K!

When developing Sencha Touch themes, you must keep in mind that there is a significant tradeoff between performance and file size. In our testing, loading a CSS file of > 400K into an Android browser virtually destroyed app performance.

Your default .SCSS file (/resources/sass/app.scss file) will appear similar to the following:

@import 'sencha-touch/default';
@import 'sencha-touch/default/all';

prefs

There are three key techniques that you should use to reduce your CSS file size:

  1. Do not include the default Pictos Font
  2. Remove unused CSS definitions
  3. Optimize your images

Removing the Pictos Font

By default, Sencha Touch embeds a font containing commonly used icons into your stylesheet. You should replace this font with your own custom icon font that only includes the icons that your app will actually use. I like to use the web-based app at http://icomoon.io/app/ to generate custom fonts. Note that every custom font that you produce should include the following symbols mapped to the indicated characters so that your app will render back-button art, item disclosure buttons, and other commonly used icons correctly. Font files should be stored in your project’s /resources/sass/stylesheets/fonts folder.

basefont


// throw out default pictos font
$include-pictos-font: false;
$include-default-icons: false;

// load default styles
@import 'sencha-touch/default';
@import 'sencha-touch/default/all';

// embed custom pictos font
@include font-face(
 'Pictos', 
 inline-font-files(
 'icomoon.woff', 
 woff, 
 'icomoon.ttf', 
 truetype,
 'icomoon.svg', 
 svg
 )
);

Remove unused style definitions

It’s rather unusual for a Sencha Touch app to utilize every visual component in the framework. You can therefore reduce the size of your CSS file by excluding the styles for the components that your app isn’t using. Simply replace the @import ‘sencha-touch/default/all’; with entries for the components that your app is using as demonstrated by the following snippet:

// throw out default pictos font
$include-pictos-font: false;
$include-default-icons: false;

// load default styles
@import 'sencha-touch/default';

// exclude components that app is not using
@import 'sencha-touch/default';
@import 'sencha-touch/default/src/Class';
@import 'sencha-touch/default/src/Button';
@import 'sencha-touch/default/src/Panel';
@import 'sencha-touch/default/src/Sheet';
@import 'sencha-touch/default/src/MessageBox';
@import 'sencha-touch/default/src/Toolbar';
// @import 'sencha-touch/default/src/carousel/Carousel';
@import 'sencha-touch/default/src/form/Panel';
@import 'sencha-touch/default/src/form/FieldSet';
@import 'sencha-touch/default/src/field/Field';
@import 'sencha-touch/default/src/field/Checkbox';
// @import 'sencha-touch/default/src/field/Radio';
// @import 'sencha-touch/default/src/field/Search';
@import 'sencha-touch/default/src/field/Select';
@import 'sencha-touch/default/src/field/Slider';
@import 'sencha-touch/default/src/field/Spinner';
// @import 'sencha-touch/default/src/field/TextArea';
// @import 'sencha-touch/default/src/dataview/IndexBar';
@import 'sencha-touch/default/src/dataview/List';
@import 'sencha-touch/default/src/picker/Picker';
@import 'sencha-touch/default/src/plugin/ListPaging';
@import 'sencha-touch/default/src/plugin/PullRefresh';
@import 'sencha-touch/default/src/slider/Slider';
@import 'sencha-touch/default/src/slider/Toggle';
@import 'sencha-touch/default/src/tab/Panel';

// embed custom pictos font
@include font-face(
 'Pictos', 
 inline-font-files(
 'icomoon.woff', 
 woff, 
 'icomoon.ttf', 
 truetype,
 'icomoon.svg', 
 svg
 )
);

Optimize your Images

You can reduce the number of http requests that are made to your app by base-64 encoding your images directly into the CSS file. Of course, you have to tread lightly here since large CSS files have an exceptionally negative impact on Android performance.

Typically you would embed an image using the Compass helper inline-image() as illustrated by following syntax:

.introbackground {
  background-image:inline-image('../images/1024x748.jpg');
  background-size: cover;
}

If you are planning to use this approach, make sure that your images are as small as possible. One of the counter-intuitive things that we’ve discovered is that JPG images can often produce much smaller file sizes than their PNG counterparts, depending on the nature of your visual.

Compare the following. Can you tell the difference?

Cockpit.png: (482K)
cockpit-8bit

Cockpit.jpg: (114K)
cockpit

Theming for Blackberry 10

The basic BB 10 theme should appear as follows:

@import 'sencha-touch/bb10';
@import 'sencha-touch/bb10/all';

bb

You should apply all of the aforementioned optimizations to this theme before proceeding.

One of the many interesting things about the Blackberry 10 UI is that their apps typically dock titlebars at the bottom of the screen while every other device vendor docks titlebars at the top. Using Sencha’s new theming engine you can easily account for this discrepancy by using the new Ext.theme.name property to configure a default docked position on titlebars as illustrated by the following snippet:

launch: function() {
 Ext.fly('appLoadingIndicator').destroy();
 if (Ext.theme.name == "Blackberry") {
   Ext.override(Ext.TitleBar, {
     config: { docked: 'bottom' }
   });
 } else {
   Ext.override(Ext.TitleBar, {
     config: { docked: 'top' }
   });
 }
 Ext.Viewport.add({xtype: 'main'});
}

Alternatively, you could use the new platformConfig property to internally branch your views:

Ext.define('MyApp.view.intro.Credits', {
 extend: 'Ext.Container',
 xtype: 'credits',
 config: {
   cls: 'starbackground credits',
   contentEl: 'credits',
   items: [
    {
     xtype: 'titlebar',
     docked: 'top',
     title: 'Credits',
     items: [{
       xtype: 'button',
       align: 'left',
       ui: 'back',
       text: 'Back',
       itemId: 'btnBack'
     }, {
       text: 'About Fig Leaf Software',
       itemId: 'btnContact',
       align: 'right'
     }]
   }
  ]
 },
 platformConfig: [
   {
    platform: 'blackberry',
    items: [{
     xtype: 'titlebar',
     docked: 'bottom',
     title: 'Credits',
     items: [{
	xtype: 'button',
	align: 'left',
	ui: 'back',
	text: 'Back',
	itemId: 'btnBack'
     }, {
	text: 'About Fig Leaf Software',
	itemId: 'btnContact',
	align: 'right'
     }]
   }
  ] // platformConfig
})

Theming for Microsoft IE 10

The good news is that Microsoft has finally embraced web standards! The bad news for you is that Microsoft has finally embraced web standards!

preferences

Your default Windows Mobile stylesheet appears as follows:

@import 'sencha-touch/windows';
@import 'sencha-touch/windows/all';

You should apply all of the optimizations discussed at the top of this blog entry before continuing.

When Sencha Touch first debuted, it was very webkit-centric, and as such it made heavy use of -webkit- css prefixes as illustrated by the CSS contained in the default production index.html file:

html, body {
  height: 100%;
  background-color: #1985D0
}

#appLoadingIndicator {
 position: absolute;
 top: 50%;
 margin-top: -15px;
 text-align: center;
 width: 100%;
 height: 30px;
 -webkit-animation-name: appLoadingIndicator;
 -webkit-animation-duration: 0.5s;
 -webkit-animation-iteration-count: infinite;
 -webkit-animation-direction: linear;
}

#appLoadingIndicator > * {
 background-color: #FFFFFF;
 display: inline-block;
 height: 30px;
 -webkit-border-radius: 15px;
 margin: 0 5px;
 width: 30px;
 opacity: 0.8;
}

@-webkit-keyframes appLoadingIndicator{
 0% { opacity: 0.8 }
 50% { opacity: 0 }
 100% { opacity: 0.8 }
}

In order to property support the mobile ie browser, you’ll need to also add non-prefixed directives to your index.html and any custom CSS that you might have developed as illustrated below:

#appLoadingIndicator {
 position: absolute;
 top: 50%;
 margin-top: -15px;
 text-align: center;
 width: 100%;
 height: 30px;
 -webkit-animation-name: appLoadingIndicator;
 -webkit-animation-duration: 0.5s;
 -webkit-animation-iteration-count: infinite;
 -webkit-animation-direction: linear;

 animation-name: appLoadingIndicator;
 animation-duration: 0.5s;
 animation-iteration-count: infinite;
 animation-direction: linear;
}

@keyframes appLoadingIndicator{
 0% { opacity: 0.8 }
 50% { opacity: 0 }
 100% { opacity: 0.8 }
}

Note that Compass and Sass support some mixins that help mitigate these x-browser styling issues.

You’ll also likely need to add the following CSS in order to get your forms to become visible (pretty sure this is a Touch 2.2 bug):

.x-form {
 background-color: black !important;
}

Including a Partial

All of your device-specific stylesheets will likely contain a large section of overlapping, custom styles. I suggest that you place these into a “partial” file that is dynamically included from each of your .SASS files.

In our Sencha’s Raiders game, for example, we’ve got a file in the /resources/sass folder named _common.scss. At the bottom of the bb10.scss, ie10.scss, and sencha-touch.scss files, we simply added the following statement:

@import 'common';

Testing your Work

You can test your work by appending a variable named platform to your app’s URL as illustrated below:

  • index.html?platform=blackberry
  • index.html?platform=ie10

What are you waiting for?

Get started theming your apps for multiple devices and contact Fig Leaf Software for code review, best-practices, full life-cycle development, or ongoing support and maintenance of your Sencha Touch apps!

6 thoughts on “Sencha Touch 2: Theming Apps for Multiple Devices

  1. Pingback: Changes to Sencha Touch 2.2 Theming Part 1: Using Iconography | Druck-I.T.

  2. FrankG

    Good advice. I got everything to work except for the check mark, it disappeared from all default checkboxes after I did away with the default pictos font. I now get a number 3 instead of the checked/unchecked image. I cannot find where ST2 sets this up, all I found was the icon name, check2. Any ideas?

    Reply
    1. sdrucker Post author

      Yep-

      At the bottom of your .scss file, you’ll need to put in the following directive:

      .x-checkmark-base, .x-field-checkbox .x-field-mask::after, .x-field-radio .x-field-mask::after, .x-select-overlay .x-item-selected.x-list-item::after {

      font-family: ‘icomoon’ !important;

      }

      (assuming that icomoon is the name of your font that maps the number ‘3’ to a checkmark icon)

      Reply
  3. Ronald

    Thanks for your blog post.

    I have one question. If I wanted to use different stylesheets for different devices AND add my own custom font (containing my custom icons) to each, would I create a .scss file in ‘/resources/sass’ for each theme (e.g. bb.scss, ie10.scss, sencha-touch.scss) and compile that to a respective .css file for inclusing into my app.json?

    Reply
  4. howellzify

    Reblogged this on Sencha Touch Tips and commented:
    Multi device themes is a great piece that I have had to implement yet but would like to. Especially to support tablets and mobile at the same time. This article really helps to get you started.

    Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s