Data Grid layout, Styling and Theming

Other Resources

Examples

Theming Ext JS

The default themes available with Ext JS can be used out of the box to create clean, professional looking applications. You may wish to provide your own styling, however, to match your personal design aesthetic or that of an existing enterprise design.

Note: You can build custom themes with our graphical tool Sencha Themer.

Historically, styling an application meant creating style sheets with rules to tweak or decorate individual HTML elements used in the rendering of a component. Several issues arise with this approach. The first being that you’re now burdened with styling across all supported browsers. Secondly, as the framework evolves the component’s underlying elements may change, leaving you with the unpleasant task of chasing the changes in your style rules. Styling components using the Ext JS Theming API solves these problems for you.

Themes may be shared across any number of Ext JS applications in the workspace (a workspace is a simple file-system structure defined by Sencha Cmd). Themes allow you to style once and apply that styling over and over with confidence in the consistency of your applications’ look and feel.

This guide lists the requirements necessary for theming and covers the basics for creating a custom theme. Since many of the details differ based on whether you are using the classic or modern toolkit, this guide focuses on the common principals. The specifics are found in these guides: Theming The Ext JS Modern Toolkit and Theming The Ext JS Classic Toolkit.

Requirements

Sencha Cmd 6.5+

Sencha Cmd is a command-line tool used to package and deploy Ext JS applications. To build a theme in Ext JS 6.5+, you must have Sencha Cmd 6.5 or higher installed on your computer. For more information about installing and getting started with Sencha Cmd see Introduction to Sencha Cmd.

Java JRE

The Java JRE is required to run Sencha Cmd. Whenever possible, we recommend running the latest version of the JRE (version 1.8 as this is written). Version 1.7 or newer, however, is required to run sencha app watch. This command is run within the application directory and updates the compiled theme automatically as you modify the theme source. Sencha Cmd has installers with the JRE included or as a stand-alone installer without the JRE. If you do not already have a suitable JRE, we recommend using the installer with JRE if one is available for your platform.

Note: Without Java JRE 1.7+ and sencha app watch you will need to manually rebuild your application’s styling after each change using sencha app build --development.

Ext JS

Custom themes are based on default themes included with the Ext JS SDK. Download Ext JS and extract the Ext JS development kit (SDK) to a location of your choosing. We recommend a folder in your home directory called sencha-sdks.

Building a Custom Theme

Once you have installed the above requirements, you can proceed to create your custom theme. A theme is simply a specific type of Sencha Cmd package. All packages can contain JavaScript, CSS styles and resources that will be incorporated into apps when Sencha Cmd builds them. As we will see, theme packages take advantage of all of these types of assets.

Create The Workspace

Workspaces are Sencha Cmd’s way of connecting applications (as well as themes or other code packages) with a shared copy of the Ext JS framework. If you only have one application to build, Sencha Cmd can combine the workspace and application in a single directory.

For this tutorial we’ll create a workspace so that the custom theme is accessible to multiple applications. Run the following command from the directory where you want to create your workspace (e.g., “my-workspace”):

  1. $ sencha workspace init
  2. $ sencha framework add ~/sencha-sdks/ext-7.1.0

On Windows, replace ~/sencha-sdks/ with %USERPROFILE%\sencha-sdks\. If you have chosen a different path, then use the appropriate path here.

The sencha workspace init command will create the scaffolding for a Sencha workspace in the current directory. The sencha framework add command copies the necessary files from the Ext JS SDK into the workspace so that the theme and applications can find these dependencies.

This workspace is where your custom theme package will live. You will also create applications here that will use your custom theme. After running the above commands, you will find these files in your workspace directory:

  • ext65/ - The Ext JS SDK (populated by sencha framework add).
  • workspace.json - The configuration descriptor for the workspace.
  • .gitignore - A starter file to keep your Git repository clean.

Generate The Theme Package

Sencha Cmd streamlines the process of creating a custom theme by generating a theme package containing all the necessary files. Follow these steps to generate your theme:

  1. Run: sencha generate workspace/path/to/workspace
  2. Go to workspace Dir.
  3. Run: sencha -sdk ../ext generate app NewExtApp new-app
  4. Run: sencha workspace init
  5. cd new-app
  6. Run: sencha generate theme my-theme

This tells Sencha Cmd to generate a theme package named my-theme in the packages/local directory of the workspace. Let’s take a look at the default contents of the custom theme folder:

  • “package.json” - This is the package definition file. It tells Sencha Cmd certain things about the package like its name and dependencies (i.e., the packages it requires).
  • “sass/“ - This directory contains all of your theme’s Fashion source files. The source files are divided into 4 main sections:
    • sass/var/ - Variable definitions organized by class name.
      • sass/var/all.scss - Global variable settings.
    • sass/src/ - Rules and UI mixin calls using variables defined in sass/var.
    • sass/etc/ - Additional utility functions or mixins.
    • sass/example - Used Sencha Cmd to perform image slicing for IE8/9 (do not delete). (Classic Only)
  • “resources/“ - Images and other static resources.
  • “overrides/“ - JavaScript overrides to Ext JS component classes.

The files and folders in sass/src and overrides should be organized to match the component class name that you are styling or overriding. For example, theme code related to Ext.panel.Panel should be placed in a file named sass/src/panel/Panel.scss. It is important to place mixin calls and style rules in the appropriate file based on the associated class so that Sencha Cmd can include only the code needed by the application in a build.

The same organization can be applied to sass/var/. Alternatively, you may choose to just use the sass/var/all.scss file if you only have a small number of variables to set. Because setting variables does not impact the size of the generated CSS, the choice between keeping all theme variables in a single file or splitting across multiple files organized by class name is a matter of personal preference or project guidelines.

Configuring Theme Inheritance

All Sencha theme packages are part of a larger hierarchy of themes. Each theme package extends a parent or base theme. The next step in creating your custom theme is to select which theme to extend. Ext JS ships with several themes for each toolkit:

Modern Toolkit

  • theme-material - Material Design theme.
  • theme-ios - iOS Theme.
  • theme-triton - Modern flat, borderless theme.
  • theme-neptune - Modern borderless theme.

Layouts & Styling - 图1

Classic Toolkit

  • theme-triton - Modern theme using font-icons. Extends “theme-neptune”.
  • theme-crisp - Minimalistic Theme. Extends “theme-neptune”.
  • theme-crisp-touch - Crisp-Based Touch Theme. Extends “theme-crisp”.
  • theme-neptune - Modern borderless theme.
  • theme-neptune-touch - Neptune-Based Touch Theme. Extends “theme-neptune”.
  • theme-classic - The classic blue Ext JS theme.
  • theme-gray - Gray theme. Extends “theme-classic”.

Layouts & Styling - 图2

Which theme should your custom theme extend? For the modern toolkit, we recommend using theme-material or theme-triton, while for the classic toolkit, we recommend using theme-triton, theme-crisp or theme-neptune as the starting point for custom themes (or perhaps theme-neptune-touch or theme-crisp-touch when theming for tablets). These themes contain all the code necessary for creating an attractive theme out of the box.

The theme-base and (in classic) theme-neutral themes should be thought of as abstract and should not be extended directly.

In this tutorial we will create a custom theme that extends theme-triton. The first step is to configure your custom theme with the name of the theme it is extending. This is done by changing the extend property in packages/local/my-theme/package.json from its default value to the desired value:

  1. "extend": "theme-triton"

Your custom theme is now configured to use the Triton theme as a base. Your custom theme starts off as identical to the default Triton theme. In the following steps you’ll make your own changes to begin to differentiate your custom theme.

Building The Theme

Building the theme creates a build directory in your theme package directory. Inside my-theme/build/resources you will find a file named my-theme-all.css. This file contains all the style rules for all Ext JS components for your theme. The built theme’s files contain all the styles for every Ext JS component and is useful for applications that do not use Sencha Cmd.

Note: It is not necessary to build a theme when that theme is used in a Sencha Cmd application. When Sencha Cmd builds an application it incorporates the .js and .scss files needed directly from the theme’s sources.

Generate A Test Application

To rapidly develop and test our custom theme we need to host it in an Ext JS application. The application should be setup to use the modern or classic toolkit as appropriate for your needs. To create the application, run these commands from the workspace directory:

  1. $ mkdir demo-app
  2. $ cd demo-app
  3. $ sencha app init --ext65 --modern App
  4. -- or --
  5. $ sencha app init --ext65 --classic App

Configure The Application’s Theme

To configure the test application to use your custom theme, find the line that sets "theme" in demo-app/app.json and change it to be:

  1. "theme": "my-theme",

Watch The Application

Sencha Cmd has now generated an application named App in this sub-directory. Going forward, we’ll be making changes to the theme and sample application. We’ll want Sencha Cmd to detect these changes and automatically compile them into the output CSS used by the application. To accomplish this, run the following command:

  1. $ sencha app watch --fashion

Reminder: The sencha app watch command requires Java JRE 1.7 or higher. If you are unable to run sencha app watch you’ll need to run sencha app build --development after each change to the theme. For brevity, we’ll assume you are running sencha app watch.

After running sencha app watch you can load your application in the browser using the URL:

  1. http://localhost:1841/demo-app/

The --fashion switch will instruct the browser to refresh the styling within the application as you make changes to the application’s theme - often in under a second!

Note: Live updates using --fashion are supported on modern browsers only. Most changes to styling in the theme will update near real time with no browser refresh required. However, theme changes that modify a component’s dimensions may require a manual browser refresh.

The Theme API

Ext JS Classic and Modern toolkits have the same fundamental approach to their API: they define variables to configure the default appearance of components and they provide mixins that can generate custom appearances. These custom appearances are assigned names of your choice and these names are applied to component instances using the ui config property. Due to this relationship to the ui config, these mixins are often called “UI mixins” or “theme mixins”.

The names of these variables differ by toolkit, but there are some common variables like $base-color that apply to both. For the complete list of variables see Global_CSS.

In the Classic toolkit, the ui config is a single string (such as "custom"). This string keys to the output of the mixin call where this name was specified. In the Modern toolkit, the ui config is more like a CSS class. It can be a space-separated set of names. Each name maps to the name passed to a mixin call, but the outcome of these calls is much more composable. For more details on the ui config, please consult the appropriate toolkit theme guide and API documentation.

Configuring Theme Variables

Let’s start by modifying the $base-color which is a value from which many Ext JS components’ colors are derived. Due to the use of $base-color through the default themes, making a global change to $base-color will have an effect on most all components in the Ext JS library.

Create the file packages/local/my-theme/sass/src/Component.scss and add the following code:

  1. $base-color: #317040;

The value of $base-color must be a valid HTML color code; see the HTML Color Codes web page.

At this time you should see the green color we specified earlier as $base-color applied to the components on the screen.

Note: Theme variables are all defined as dynamic and so the dynamic() declaration is not needed to set them (as show above). If you want to define your own variables, however, we recommend making them dynamic:

  1. $my-custom-color: dynamic(red);

Theme JS Overrides

Sometimes a theme needs to change an aspect of a component that is only configurable via JavaScript. This is easily accomplished by adding a JavaScript override to your theme package. To demonstrate how this is done, let’s change the titleAlign config of Panels in the custom theme. Create a new file named my-theme/overrides/panel/Panel.js and add the following code:

  1. Ext.define('MyTheme.panel.Panel', {
  2. override: 'Ext.panel.Panel',
  3. titleAlign: 'center'
  4. });

When you view the application in the browser you’ll notice that all Panel headers have centered titles. Although any Ext JS component config can be overridden in this manner, best practice is to only use overrides to change configs that affect the visual appearance (and not the functionality) of a component.

Modifying Image Assets

All required image assets are inherited from the parent theme by default, but in some cases you may need to override an image. This can be easily done by placing the desired image in my-theme/resources/images/ and giving it the same name as the image it is intended to override from the base theme.

In many modern themes like theme-triton there are few if any images. These themes use vector font icons. Therefore the images you can supplant in this way is specific to your choice of base theme.

Adding Custom Utilities

If your theme requires functions or mixins that are not related to component styling (e.g. utilities), these should be placed in the theme’s my-theme/sass/etc directory.

You can organize files in this directory however you like, but the only file that Sencha Cmd includes in the build is my-theme/sass/etc/all.scss. Any other files must be imported (@import) by the all.scss file. For an example that follows this pattern see ext/classic/theme-base/sass/etc/.

Styling Your Application

Styling that is not shared between applications belongs in the application itself, not in the theme. Sencha Cmd provides an easy way to add application-level styling by allowing you to organize your styles right alongside your JavaScript code.

Styling Your Application’s Views

To write CSS rules associated with an application view, you create an .scss file in the same folder and with the same base name as the view. For example, to style the view App.view.main.Main, you may can add Main.scss to that folder:

  1. demo-app/
  2. app/
  3. view/
  4. main/
  5. Main.js
  6. Main.scss
  7. MainController.js
  8. MainModel.js

While the ability to add arbitrary CSS styles offers maximum flexibility, it is best to avoid directly styling elements owned by Ext JS components. Instead these should be styled using the Ext JS Theming API whenever possible. Using the theming API safeguards your styling against breaking markup changes in future versions of Ext JS.

Changing Theme Variables In Your Application

The application acts as the final level in the theme hierarchy. As such, applications can change theme variables.

Let’s continue using the demo-app application created above and override the theme’s $base-color in the application. Create the Application.scss file:

  1. demo-app/
  2. app/
  3. Application.js
  4. Application.scss

The Application.scss file is an easy option for global settings since it matches with the global nature of the Application class. And add the following to this file:

  1. $base-color: #724289;

View the application in a browser and you will see that the base color has changed to purple.

Theming In An Application

To do significant theme work in an application, you should create a sass folder structure in the same manner as a theme and inform Sencha Cmd that your styles apply to all namespaces (not just the application). The code that would normally be in the theme’s sass folder can then be placed in the application’s sass folder.

The Sass Namespace

When Sencha Cmd builds your styles, it looks at all the JavaScript classes that are used in the application. It then includes all .scss files that coincide with these classes. This process uses the sass.namespace to align a class name (such as Ext.button.Button) with files on disk for a package, theme or application.

For example, in a theme you might have the file sass/var/button/Button.scss as well as the file sass/src/button/Button.scss. These files will be considered matches because the default sass.namespace for a theme is Ext.

The default sass.namespace for an application, however, is its own namespace (App). This means Sencha Cmd will not recognize demo-app/sass/var/button/Button.scss as a match for the Ext.button.Button class. Instead that would match App.button.Button. To include files that match all namespaces, you need to set sass.namespace to the empty string in app.json:

  1. "sass": {
  2. "namespace": ""
  3. }

Once this change is made, Sencha Cmd will match demo-app/sass/var/Ext/button/Button.scss and demo-app/sass/src/Ext/button/Button.scss to Ext.button.Button.

This also means that if you want to use the sass folder to organize styles for your views, you will need to use App sub-folders. For example sass/src/App/view/main/Main.scss is the path that will match App.view.main.Main. While this was the pattern in previous releases, and is still supported, placing view styles in .scss files that reside next to their .js file (as descried previously) is a more maintainable structure going forward.

Organization Of Generated Styles

Sencha Cmd combines the styling from your theme, your application and from any required packages (see the Sencha Cmd Packages guide) when it compiles your application. It is important to understand the way Sencha Cmd combines these files so that you know what you can use from your theme or required packages and when these things will be available.

The structure of the all.scss file is this:

  1. +---------------------------------------+
  2. | inclusion flags |
  3. +-----------+-----------+---------------+
  4. | | | base |
  5. | | theme +---------------+
  6. | | | derived |
  7. | +-----------+---------------+
  8. | | |
  9. | etc | packages (dep order) |
  10. | | |
  11. | +---------------------------+
  12. | | |
  13. | | application |
  14. | | |
  15. +-----------+---------------------------+
  16. | | | base |
  17. | | theme +---------------+
  18. | | | derived |
  19. | +-----------+---------------+
  20. | | |
  21. | var | packages (dep order) |
  22. | | |
  23. | +---------------------------+
  24. | | |
  25. | | application |
  26. | | |
  27. +-----------+-----------+---------------+
  28. | | | base |
  29. | | theme +---------------+
  30. | | | derived |
  31. | +-----------+---------------+
  32. | | |
  33. | src | packages (dep order) |
  34. | | |
  35. | +---------------------------+
  36. | | |
  37. | | application (*) |
  38. | | |
  39. +-----------+---------------------------+
  40. * - Includes application styles that are placed
  41. next to their corresponding .js files.

Inside the bands for sass/var and sass/src, the individual .scss files for a given theme, package and the application are always ordered to match the JavaScript class hierarchy. For example, if the base theme had .scss files for Ext.Container and Ext.Component in its sass/var folder, the file for Ext.Component would be included before the file for Ext.Container since it extends Ext.Component.

The goals and rationale for this structure are as follows:

  • In sass/etc are utilities and the like:
    • Utilities from base themes are available to derived themes.
    • Package utilities can use facilities provided by the current theme.
    • Application utilities are able to use their theme and any required packages.
  • In sass/var, the concerns are variable control and derived calculation.
    • Themes come first so that they set values for their variables.
    • Derived theme can change variables since they follow their base themes.
    • Package variables are introduced in their package dependency order.
    • Applications come last so that their vars will override themes and packages.
  • For sass/src this order yields the proper cascade of rules so that:
    • Derived theme rules win over rules from their base theme(s).
    • Application rules cascade in last so that they always have the final authority.

Inclusion Flags

The inclusion flag variables are a set of read-only variables defined to be true or false for each JavaScript class that may be included. The value of this variable is true if that class is being included in the application build. The variables are created dynamically by Sencha Cmd with a prefix of $include- followed by the full class name with its parts separated by - instead of . and all in lowercase. For example, if the build uses Ext.Img, you can test for true within a custom mixin:

  1. @if $include-ext-img {
  2. // styling contingent upon the presence of Ext.Img in the app
  3. }

Note: Through most of the guide we advocate for the use of sencha app watch to build your application incrementally as changes are made to both the application and the custom theme. However, the $include- variables are all set to true in development (which is the environment sencha app watch operates within). In order to test your theme’s use of $include- variable checks you’ll need to build your application for testing or production:

  1. $ sencha app build --testing
  2. $ sencha app build --production

Sharing A Theme Between Applications

It’s easy to share the theme you’ve just built with a second application. Simply navigate to the workspace directory and run the following command (note: if you’re following along using the steps above and have sencha app watch running you’ll first need to end it using Ctrl+C):

  1. $ mkdir another-app
  2. $ cd another-app
  3. $ sencha app init --ext65 --modern (or --classic) AnotherApp

This tells Sencha Cmd to generate an app in the another-app directory named AnotherApp and to use the same Ext JS SDK as the first app you created.

The next step is to tell the app to use the custom theme. Edit another-app/app.json and change the theme to:

  1. "theme": "my-theme",

To ensure changes to the app and theme are picked up as you make them again change your working directory to the application directory and run:

  1. $ sencha app watch --fashion

When you view the another-app/index.html page in your browser you will now see a starter app that uses the same custom theme as App.

Next Steps

That covers the general concepts and common practices for all Ext JS themes. For specifics on each toolkit, see these guides for Theming The Ext JS Modern Toolkit and Theming The Ext JS Classic Toolkit.

Ext JS Modern Toolkit Theming Guidelines

In this guide, we’re going to walk through the changes to theming introduced with the modern toolkit in Ext JS 6.2. There are a lot of exciting new changes that provide for easier theming, dynamic updating, and much more. Along with these updates come some new concepts that you should be aware of.

Let’s talk about the conventions and best practices for theming in the modern toolkit.

Dynamic Variables

All variables are defined using the dynamic() indicator. This ensures that the last variable declaration wins and remains valid throughout the entire scope. Additionally, variables may then derive from one another without concern for ordering.

For example:

  1. $calendar-days-time-color: dynamic($color);

File Naming

All non-rule-generating code are placed in the theme’s sass/var/ directory in a file matching the Component’s class name. This is generally going to include all component variable and UI mixin declarations.

Since all var files are included in the build regardless of whether or not the corresponding class is actually required, this allows variables to freely derive from any other variable. Including mixins in the sass/var/ directory ensures that a derived theme can override those mixins before they are called by any code in the sass/src/ directory.

All rule-generating code is located in the theme’s sass/src/ directory in a file matching the Component’s class name. This includes calls to UI mixins and rules not contained inside the body of a mixin.

At build time, src files are only included if the corresponding Component is required. This ensures that only the rules needed are included in the CSS output.

Base Styles vs. Configurable Styles

Layout-specific styles and styles related to the core functionality of a Component are placed in the theme-base/sass/src/ folder in a file matching the Component’s class name. The properties set in these rules are not configurable using variables, as changing them would likely break the functionality or layout of the Component.

Examples of CSS properties that are generally not configurable are:

  • display
  • visibility
  • position
  • overflow
  • z-index

Configurable styles not related to the core functionality or layout of a component are controlled using variables. These variables are defined in an scss file matching the component’s class name in the theme-neptune/sass/var directory. The rules that use these variables are contained in a UI mixin in the same file.

Examples of commonly configurable styles are:

  • background-color
  • color
  • padding
  • border-radius
  • font-size

The Neptune theme is the base for all other themes and contains the theming capabilities supported by the framework even though it may not utilize them all itself.

Themes derived from theme-neptune do not typically define new UI mixins or create their own CSS rules. Instead, they simply set the values of variables defined in theme-neptune.

Variable Naming Conventions

Component variables begin with an xtype, and end with the CSS property name being styled, for example:

  1. $button-font-family: dynamic(helvetica);
  2. $button-color: dynamic(#fff);
  3. $button-background-color: dynamic(red);

If the component has various states such as hovered, focused, and pressed, the name of the state comes immediately after the xtype, but before the CSS property name:

  1. $button-hovered-background-color: dynamic(blue);

If the component has variables that control the styling of sub-elements, the name of the sub-element being styled are included after the xtype and state (if present).

For example, when styling the button’s “badge” element:

  1. $button-badge-color: dynamic(#fff);
  2. $button-hovered-badge-color: dynamic(green);

If the “state” refers to a sub-element’s state, it comes after that element’s name. For example, if a tab has a close icon that has a separate hover state from the tab:

  1. $tab-close-icon-hovered-background-color: dynamic(red);

Components have separate variables for border-width, border-color, and border-style, and all three properties accept either a single value or a list of values so that 4 sides can be specified separately if needed:

  1. $button-border-color: dynamic(red yellow green blue);
  2. $button-border-width: dynamic(1px 3px);
  3. $button-border-style: dynamic(solid);

Variables use the following names to indicate component state:

  • “pressed” when the component is being pressed by the user or is in a pressed state
  • “pressing” if the component has a “pressing” state that is separate from “pressed”
  • “hovered” when the mouse pointer is over the element
  • “focused” when the element has focus
  • “disabled” when the component is disabled.

Since “focused” can sometimes be combined with other states components may provide variables that indicate a combination of states, for example:

  1. $button-focused-pressed-border-color

Normal and Big Modes

Each theme has two modes of sizing: normal and big. Big mode increases spacing and sizing to be more touch-screen friendly. Themes select whether or not to use big mode by inspecting Ext.platformTags in the theme’soverrides/init.js and adding a CSS class name of x-big to the <html> element on the page. For example, the Triton theme only enables big mode when loaded on a phone, otherwise, it uses normal mode.

  1. Ext.theme.getDocCls = function() {
  2. return Ext.platformTags.phone ? 'x-big' : '';
  3. };

Variables that set properties affecting visual size of a component, like font-size, line-height, and padding have big counterparts. Big variables always have the -big suffix appended to the end:

  1. $button-padding: dynamic(1rem);
  2. $button-padding-big: dynamic(1.2rem);

CSS rules target big mode using the .x-big selector:

  1. .#{$prefix}button {
  2. padding: $padding;
  3. .#{$prefix}big & {
  4. padding: $padding-big;
  5. }
  6. }

Component UIs

Most components have a UI mixin for generating multiple visual renditions of that component. These mixins are named [xtype]-ui. For example button-ui or panel-ui.

UI mixins have a parameter for each of the component’s global variables. Additionally, the parameter names are the same as the global variable names. The only exception is that the mixin parameters do not contain the xtype in their names.

For example, a global variable named $button-border-radius, would correspond to a parameter of the button-ui mixin named $border-radius.

The parameters to the UI mixin default to null and do not produce any output if unspecified by the caller. This means that when the mixin is invoked, it produces a set of styling that represents a delta from the default UI. This minimizes the number of CSS rules required to create new UIs since the mixin automatically eliminates any null values from the output.

The styling for the default UI is applied using CSS class names that do not contain a UI name. For example, x-button, not x-button-default. This is the key to minimizing the number of rules required to create additional UIs. For instance, all buttons will have the x-button class in addition to one or more optional x-button-[ui] classes. It allows the default UI to serve as a base set of styles for all other UIs.

To generate additional UIs, invoke the mixin passing only the parameters that are different from the default UI. For example, the following mixin call generates a UI named “action” that builds upon the default UI by changing the background-color to red, but inherits all other properties from the default UI via the cascade. The output from this UI invocation is minimal. It only contains the rule or rules needed to set the background-color, nothing else.

  1. @include button-ui(
  2. $ui: 'action',
  3. $background-color: red
  4. );

Derived UIs

Subclasses of Components that have UI mixins typically have their own UI mixin and a complete set of global variables for configuring that mixin.

Subclass variables that do not have different default values from the superclass variables will be null. This ensures that they will inherit the proper values via the parent CSS class rather than redefining a redundant value.

Components use classCls set to x-[xtype] to accomplish this inheritance (See section below on CSS class names).

An example of this pattern is the grid pagingtoolbar component that extends toolbar:

  1. // theme-neptune/sass/var/grid/plugin/PagingToolbar.scss
  2. $pagingtoolbar-background-color: dynamic(null);
  3. @mixin pagingtoolbar-ui(
  4. $ui: null,
  5. $xtype: pagingtoolbar,
  6. $background-color: null,
  7. $prev-icon: null
  8. ) {
  9. $ui-suffix: ui-suffix($ui);
  10. // Call base toolbar mixin.
  11. // Only produces output for non-null parameters
  12. @include toolbar-ui(
  13. $ui: $ui,
  14. $xtype: $xtype,
  15. $background-color: $background-color
  16. );
  17. // paging toolbar specific styles
  18. .#{$prefix}#{$xtype}#{$ui-suffix} {
  19. .#{$prefix}icon-prev {
  20. @include icon($prev-icon);
  21. }
  22. }
  23. }
  24. // theme-neptune/sass/src/grid/plugin/PagingToolbar.scss
  25. @include pagingtoolbar-ui(
  26. $background-color: $pagingtoolbar-background-color;
  27. );

Configuring Theme UIs in Derived Themes

Additional UIs provided by a theme are typically not configurable via global variables or at least not overly configurable. Instead, these UIs are wrapped in a mixin of their own, which can be overridden by derived themes to change the parameters:

  1. @mixin button-action-ui() {
  2. @include button-ui(
  3. $ui: 'action',
  4. $background-color: red
  5. );
  6. }

Themes provide a single variable for each additional UI that defaults to “true” but can be overridden to “false” to disable generation of the UI. This variable will have the same name as the corresponding mixin:

  1. @if $button-action-ui {
  2. @include button-action-ui;
  3. }

Composable UIs

UIs are generally composable. For example if two separate button renditions are required, a red “action” button, and a rounded red “action” button, simply create an “action” UI and “round” UI:

  1. // sass/var
  2. $button-action-ui: dynamic($enable-default-uis);
  3. $button-confirm-ui: dynamic($enable-default-uis);
  4. @mixin button-action-ui() {
  5. @include button-ui(
  6. $ui: 'action',
  7. $background-color: red
  8. );
  9. }
  10. @mixin button-round-ui() {
  11. @include button-ui(
  12. $ui: 'round',
  13. $border-radius: 10000px
  14. );
  15. }
  16. // sass/src
  17. @if $button-action-ui {
  18. @include button-action-ui
  19. }
  20. @if $button-round-ui {
  21. @include button-round-ui
  22. }

To compose UIs, simply use any number of UI names, space separated, in your component config:

  1. ui: 'action round'

If multiple UIs set the same properties, the winner is the last one in the cascade, i.e. the one whose mixin was invoked last. Composable UIs typically strive to limit their area of concern to separate aspects of styling (colors, sizing, border-radius, etc), so that there is little ambiguity when combining them.

Using composable UIs ensures that the generated CSS code remains very DRY, by avoiding unnecessary duplication of CSS rules. In the example above, we avoid duplication of the background-color rules for every UI that may optionally need roundness. Any UI can be combined with the “round” UI to add roundness.

CSS Class Names

It is important to maintain consistency in naming CSS classes since they play a major role in adding semantics and structure to the DOM elements that are generated by Components in the framework. This consistency of naming serves two purposes:

  1. It improves the readability and maintainability of SASS code and reduces the chance for error.
  2. For users who do not use the API, it provides a clear and understandable DOM structure for styling.

Main Element and UI CSS Classes

Components have a class name of x-[xtype] on its main element. For example, a Text Field component will have a CSS class name of x-textfield.

There are two possible ways for Components to set this main CSS class. They can either set classCls or baseCls on the body of their class definition, though classCls is preferred. Setting either of these will add the CSS class to the main element and use it as the prefix for UI-specific class names that are also added to the main element. The classCls and baseCls configs only differ in their inheritability. classCls is inherited by subclasses and is additional to the classCls of those subclasses, whereas baseCls is not.

Additionally, when using classCls a UI-specific CSS class name will be added for each and every classCls in the class hierarchy. For example, an Ext.field.Password component with a “ui” set to foo would have the following UI classes:

  • x-passwordfield-foo
  • x-textfield-foo
  • x-field-foo

This pattern ensures that styling is correctly inherited through the class hierarchy and allows Components to only provide styling for the functionality that they add. For edge cases where inheriting styling is not desired Components may set classClsRoot:true to prevent inheritance of classCls from ancestors.

Reference Element CSS Classes

Reference elements follow the pattern x-[referencePrefix]-el. For example the bodyElement reference element of a form field will have the CSS class x-body-el. For consistency, element references will have the Element suffix on the JavaScript side. The -el suffix on the CSS class name helps to differentiate the reference element from a potential Component with an xtype of the same name.

CSS Classes for Component Configuration and State

CSS class names that reflect Component configuration follow the pattern x-[configName]-[configValue], and are placed on the Component’s main element. For example a form field with a labelAlign: 'left' config has a CSS class name of x-label-align-left added to the main element.

CSS class names for boolean configs generally follow one of two patterns:

  1. Truthiness causes a new class to be added. For example, a checkbox with a checked config would have an x-checked CSS class when the value is true, but would not have the class when false.

  2. Falsiness causes a new class to be added. This is sometimes useful when the default value is true, and the component needs needs additional styling only in the falsey state. For example, the List component has an x-no-row-lines CSS class when rowLines is configured as false.

Likewise, class names that reflect Component state follow the pattern x-[state], and are placed on the Component’s main element .

For example, a button that is in pressed state would have the class x-pressed on its main element.

Setting Component state and configuration CSS classes on the main element, rather than on a reference element, allows the state or configuration to be scoped to the Component. This is true even if the classes only affect the styling of a child element or elements. This also results in a more stable DOM structure as these class names do not change location even if the internal dom structure is modified.

CSS Selectors

Since reference, config, and state CSS classes do not contain xtype info in their name, they must be used in combination with the Component’s classCls or baseCls to avoid colliding with other Components that may have the same config or state class name. For example to style the pressed state of a button’s main element, one would use the following selector:

  1. .x-button.x-pressed

UI mixins use UI-specific CSS class names in combination with reference, config, and state CSS classes. For example if the ui of a button is ‘foo’, one would style the pressed state as follows:

  1. .x-button-foo.x-pressed

Child vs. Descendant Selectors

When styling a component’s inner elements, descendant selectors such as .x-foo .x-bar should be preferred over direct child selectors like .x-foo > .x-bar. This allows for much more flexibility in the markup and allows it to tolerate more change, such as the insertion of a wrapping element in between x-foo and x-bar without potentially breaking the styling.

The only exception to this rule is when there is the potential for nesting. For example, a panel might use a selector such as .x-panel > .x-body-el in order to only style its own body element, and not the body elements of other panels nested within it. In some cases, when there are a varying number of dom elements in between the container element and it’s child, UI-specific class names are added to the child element.

An example of this is Ext.Container. It adds a UI-specific class for each classCls to its innerElement because there can be a varying number of DOM ancestors in between the innerElement and the element depending on whether or not the container has docked items.

Theming The Ext JS Classic Toolkit

This guide is the continuation of the Ext JS Theming guide and is focused on the Theme API for the Ext JS Classic Toolkit, so please read that guide before proceeding. See the Theming The Ext JS Modern Toolkit guide for information about the modern toolkit.

Requirements

This guide assumes you have met all the Requirements described in the Ext JS Theming guide. To recap:

Setup

This guide assumes you have a workspace, the custom my-theme and the demo-app generated as described in the Ext JS Theming guide.

You should be able to watch the demo application using the following command (or run the sencha app build --development command to compile your styles):

  1. $ sencha app watch --fashion

After running sencha app watch you can pull up your application using the URL:

  1. http://localhost:1841/demo-app/

The --fashion switch will instruct the browser to refresh the styling within the application as you make changes to the application’s theme - often in under a second! Reminder: This Live Update feature is supported on modern browsers only (others will require a manual Reload to see changes).

Theming Components

As discussed in general in the previous guide, the Theme API for components consists of variables and mixins. The default appearance of a component is determined by its variables, while custom appearances can be defined and named by calling the “UI mixin”.

Configuring Theme Variables

Each themeable Ext JS component has a list of variables that can be used to configure its appearance. Let’s change the font-family of Panel Headers in my-theme. Create a file named my-theme/sass/var/panel/Panel.scss and add the following code:

  1. $panel-header-font-family: Times New Roman;

View your application now and you should see that the panel headers use “Times New Roman” font. You can find the complete list of variables for each component in the “Theme Variables” section of the component’s API documentation. For example, see Ext.panel.Panel and scroll to the section titled “Theme Variables”

Using Theme Mixins

All components in the Ext JS Classic Toolkit have a ui config property, which defaults to "default". This config property can be configured on individual component instances to give them a different appearance from other instances of the same type. This config is used within the Neptune theme to create different types of Panels and Buttons. For example, panels with the default ui have dark blue headers and panels with the ‘light’ ui have light blue headers. Buttons use ui‘s to give toolbar buttons a different appearance from regular buttons.

The theme-neutral theme includes Theme Mixins (or UI Mixins) for many of the different Ext JS components. You can call these mixins to generate a new ui for components. Available mixins for each component are listed in the API documentation. For example, see Ext.panel.Panel and scroll down to the “Theme Mixins” section to see what parameters the Panel UI mixin accepts. Let’s use this mixin to create a custom Panel ui.

Create a file named my-theme/sass/src/panel/Panel.scss and add the following to it:

  1. @include extjs-panel-ui(
  2. $ui: 'highlight-framed',
  3. $ui-header-background-color: red,
  4. $ui-border-color: red,
  5. $ui-header-border-color: red,
  6. $ui-body-border-color: red,
  7. $ui-border-width: 5px,
  8. $ui-border-radius: 5px,
  9. $ui-header-color: white
  10. );

This mixin call creates a new Panel ui named "highlight" which has a red header background, red bordering, 5px border, 5px border-radius, and white text. To use this ui, configure a Panel with 'highlight' as its ui property (along with frame: true). Open demo-app/app/view/main/List.js and replace its contents with the following:

  1. Ext.define('App.view.main.List', {
  2. extend: 'Ext.grid.Panel',
  3. xtype: 'mainlist',
  4. ui: 'highlight',
  5. frame: true,
  6. requires: [
  7. 'App.store.Personnel'
  8. ],
  9. title: 'Personnel',
  10. store: {
  11. type: 'personnel'
  12. },
  13. columns: [
  14. { text: 'Name', dataIndex: 'name' },
  15. { text: 'Email', dataIndex: 'email', flex: 1 },
  16. { text: 'Phone', dataIndex: 'phone', flex: 1 }
  17. ],
  18. listeners: {
  19. select: 'onItemSelected'
  20. }
  21. });

View your application in a web browser and you should see the red “highlight” Grid.

While UI mixins are a handy way to configure multiple appearances for a component, they should not be overused. Each call to a UI mixin generates additional CSS rules. Excessive calls to UI mixins can produce an overly large CSS file.

Slicing Images for CSS3 effects in IE

In some themes, many components have rounded corners and linear gradient backgrounds. These effects are simple to accomplish in modern browsers using CSS3. However, Ext JS supports IE8 and IE9 and neither of these browsers support these effects (or do so in a way that makes combining the effects problematic).

Sencha Cmd closes this gap by rendering each component requiring these effects in a headless browser and slicing images from the corners and gradients for use as background images in the component markup in IE8/9. When adding custom ui‘s you’ll need to include them in the slicing manifest used by Sencha Cmd so that the component decorated with the custom ui will be sliced for use in IE8/9.

To do this, we need to tell Sencha Cmd which components and ui‘s need slicing. In order to create slices for the rounded corners of the “highlight” panel ui that you created earlier in the guide, edit the file named my-theme/sass/example/custom.js and add the following:

  1. Ext.theme.addManifest({
  2. xtype: 'panel',
  3. ui: 'highlight'
  4. });

Note: Multiple manifest entries may be added in the same addManifest call like:

  1. Ext.theme.addManifest({
  2. xtype: 'panel',
  3. ui: 'highlight'
  4. }, {
  5. xtype: 'button',
  6. ui: 'green'
  7. });

If you create an original component that requires slicing you’ll need to add any applicable ui configs to the slicing manifest as demonstrated above. You will also need to add config entries for the custom component using the Ext.theme.addShortcuts() call in custom.js.

The shortcut configs along with the ui‘s passed to the manifest will be used in rendering the custom component for slicing.

For a more detailed description of how to use Ext.theme.addShortcuts and Ext.theme.addManifest, refer to the inline documentation descriptions for each method found in my-theme/sass/example/render.js. You can refer to examples of addShortcuts for the framework components within the ext/classic/theme-base/sass/example/shortcuts.js file.

Modifying Image Assets

As an example of modifying an image asset let’s change the info icon of the MessageBox component. Save the following image as my-theme/resources/images/shared/icon-info.png. This image asset will take precedence over the one used in the parent Crisp theme at my-workspace/ext/classic/theme-crisp/resources/images/shared/icon-info.png.

Now modify your test application to show a MessageBox that uses the custom icon. Add the following tbar config to the “highlight” Grid in your application’s demo-app/app/view/main/List.js file:

  1. ...
  2. tbar: [{
  3. text: 'Show Message',
  4. handler: function() {
  5. Ext.Msg.show({
  6. title: 'Info',
  7. msg: 'Message Box with custom icon',
  8. buttons: Ext.MessageBox.OK,
  9. icon: Ext.MessageBox.INFO
  10. });
  11. }
  12. }]
  13. ...

Now, view the app in the browser. When you click the “Show Message” button you should see that the MessageBox contains a friendly face.

Styling Your Application

Styling that is not shared between applications belongs in the application itself, not in the theme. Sencha Cmd provides an easy way to add application-level styling by allowing you to organize your styles right alongside your JavaScript code.

Styling Your Application’s Views

To write CSS rules associated with an application view, you create an .scss file in the same folder and with the same base name as the view. For example, to style the view App.view.main.Main, located in demo-app/app/view/main/Main.js, you would put that code in demo-app/app/view/main/Main.scss.

Let’s style the content of the Users tab in the App application:

  1. .content-panel-body h2 {
  2. color: orange;
  3. }

Add the content-panel-body CSS class to the config of the Users panel in your application’s Main.js file:

  1. ...
  2. title: 'Users',
  3. iconCls: 'fa-user',
  4. html: '<h2>Content appropriate for the current navigation.</h2>',
  5. bodyCls: 'content-panel-body'
  6. ...

View your application and you’ll see that the h2 element in the Users view is now orange. While the ability to add arbitrary CSS styles offers maximum flexibility, any styling applied directly to elements owned by Ext JS components should be styled using the Ext JS theming API whenever possible. Using the theming API safeguards your styling against breaking markup changes in future versions of Ext JS.

Additional Notes

‘default’ Component Images

Various components have images relating the the component’s "default" ui (Buttons, Menus, etc.). When you create a custom ui for one of these components you’ll notice when the theme is compiled it warns that images for your theme were not found.

  1. WARNING: @theme-background-image: Theme image not found:

While refreshing the theme or app, Sencha Cmd will be looking for images using the ui name in place of "default" in the image name. For example, if you create a mixin ui with a name of "admin" for small Buttons, Sencha Cmd will warn that "admin-small-arrow.png" was not found.

The solution to this warning is to copy over any image assets with “default” in the file name from the theme you’re extending into the custom theme’s resources/images directory. You’ll then rename those files and replace “default” with the name of your custom ui. In the case of the "admin" button ui in your custom theme extending Neptune you would copy the "default" images from the ext/classic/theme-neptune/resources/images/button folder and paste them into packages/local/my-theme/resources/images/button/. You’ll then rename all "default" instances to "admin". For instance:

  1. $ mv default-small-arrow.png admin-small-arrow.png

Ext.button.Button

Button ui images will need to be copied from the parent theme to the custom theme when creating a custom ui. See the “‘default’ Component Images” section above for more detail.

Button scale can be configured as small, medium, or large with small being the default. When creating custom UIs for buttons you’ll need to provide a button mixin for each scale used in your application.

Note: The extjs-button-ui mixin should be avoided in favor styling buttons using the scale-specific mixins.

  1. @include extjs-button-small-ui(
  2. $ui: 'green',
  3. $background-color: green
  4. );
  5. @include extjs-button-medium-ui(
  6. $ui: 'green',
  7. $background-color: green
  8. );
  9. @include extjs-button-large-ui(
  10. $ui: 'green',
  11. $background-color: green
  12. );

The same applies when using the -toollbar button mixins. Each has a scale and should be included separately in the Button.scss file in order to support all button scales. Additionally, when working with the -toolbar button mixins you will need to add -toolbar to the ui config of the button in your application. Below is an example mixin for a small toolbar button mixin:

  1. @include extjs-button-toolbar-small-ui(
  2. $ui: 'green',
  3. $background-color: green
  4. );

which would decorate a button configured in a toolbar like:

  1. xtype: 'toolbar',
  2. items: [{
  3. text: 'Toolbar Button',
  4. ui: 'green-toolbar'
  5. }]

Ext.panel.Panel

Panels may be configured with frame: true and are frame: false by default. So, by default if you have a ui config of ui: 'highlight' then the resulting Panel.scss would look like:

  1. @include extjs-panel-ui(
  2. $ui: 'highlight',
  3. $ui-header-background-color: red,
  4. $ui-border-color: red,
  5. $ui-header-border-color: red,
  6. $ui-body-border-color: red,
  7. $ui-border-width: 5px,
  8. $ui-border-radius: 5px
  9. );

However, this will only apply styling to non-framed panels. In order to style panels configured with frame: true and ui: 'highlight' you will need to add -framed to the $ui name in the Panel.scss file. Commonly both the framed and unframed ui versions will be represented in Panel.scss

  1. @include extjs-panel-ui(
  2. $ui: 'highlight',
  3. $ui-header-background-color: red,
  4. $ui-border-color: red,
  5. $ui-header-border-color: red,
  6. $ui-body-border-color: red,
  7. $ui-border-width: 5px,
  8. $ui-border-radius: 5px
  9. );
  10. @include extjs-panel-ui(
  11. $ui: 'highlight-framed',
  12. $ui-header-background-color: red,
  13. $ui-border-color: red,
  14. $ui-header-border-color: red,
  15. $ui-body-border-color: red,
  16. $ui-border-width: 5px,
  17. $ui-border-radius: 5px
  18. );

Ext.menu.Menu

Menu ui images will need to be copied from the parent theme to the custom theme when creating a custom ui. See the “‘default’ Component Images” section above for more detail.

Ext.toolbar.Breadcrumb

Breadcrumb ui images will need to be copied from the parent theme to the custom theme when creating a custom ui. See the “‘default’ Component Images” section above for more detail.

Ext.tab.Tab

Tab ui images will need to be copied from the parent theme to the custom theme when creating a custom ui. See the “‘default’ Component Images” section above for more detail.

When creating a tab ui be sure to include all applicable state vars you want to style including the -active tab states such as $ui-color-active, $ui-background-color-active, etc.

Ext.tab.Bar

TabBar ui images will need to be copied from the parent theme to the custom theme when creating a custom ui. See the “‘default’ Component Images” section above for more detail.

Note: When creating a TabBar ui with the extjs-tab-bar-ui mixin, you will need to create a corresponding tab-ui of the same name.

This will ensure that the tabs render properly in your theme. Not creating a matching tab theme may result in unpredictable tab rendering.

Ext.toolbar.Toolbar

Toolbar ui images will need to be copied from the parent theme to the custom theme when creating a custom ui. See the “‘default’ Component Images” section above for more detail.

Upgrading from Ext JS 5.x

While most of the updates to themeing occurred behind the scenes between Ext JS 5 and 6, there are a few changes to note when upgrading your theme.

Any variables defined in sass/etc/all.scss should be moved to sass/var/all.scss (or a .scss file @import-ed by sass/var/all.scss).

(recommended) Remove !default from the end of variable declarations

(recommended) Relocate your custom theme folder from the root packages/ folder in your application / workspace to packages/local/.