Ext JS Theming and Styling — Custom Themes, CSS, and Visual Customization
In this tutorial, you will learn about Ext JS Theming and Styling. We cover key concepts, practical examples, and best practices to help you master this topic.
Ext JS theming allows complete visual customization through CSS variables, component-level style overrides, custom themes built with Sencha Cmd, and responsive configuration for different screen sizes.
What You'll Learn
- CSS variable system in Ext JS themes
- Creating custom themes with Sencha Cmd
- Component-level styling and overrides
- Responsive Design with responsiveConfig
- Performance considerations for custom themes
Why It Matters
Enterprise applications need consistent branding that matches company style guides. Ext JS themes control colors, fonts, spacing, and component appearance globally. Custom themes ensure a professional, branded experience without overriding individual component styles.
Real-World Use
A financial application using the company's brand colors (dark blue header, green accents, custom font stack), with a light dashboard theme and a dark trading theme that users can toggle, all built on a custom Ext JS theme package.
Theming Architecture
flowchart TD
A[Custom Theme] --> B[Base Theme]
B --> C[CSS Variables]
A --> D[Component Styles]
A --> E[Ext SASS/SCSS]
A --> F[Images/Icons]
D --> G[Panel]
D --> H[Grid]
D --> I[Button]
D --> J[Form Field]
C --> K[Colors]
C --> L[Fonts]
C --> M[Spacing]
style A fill:#e6f3ff,stroke:#4a90d9,stroke-width:2px
CSS Variable Overrides
// Override CSS variables globally (Ext JS 7+)
Ext.define('MyApp.overrides.Theme', {
override: 'Ext.Component',
// Applied to all components
setStyle: function() {
// Set CSS custom properties on the document root
Ext.getDoc().setStyle('--base-color', '#1a237e');
Ext.getDoc().setStyle('--base-highlight-color', '#283593');
Ext.getDoc().setStyle('--base-light-color', '#e8eaf6');
Ext.getDoc().setStyle('--base-alt-color', '#f5f5f5');
Ext.getDoc().setStyle('--color', '#212121');
Ext.getDoc().setStyle('--font-size', '14px');
Ext.getDoc().setStyle('--font-family', "'Segoe UI', Roboto, sans-serif");
Ext.getDoc().setStyle('--border-radius', '4px');
this.callParent();
}
});
Expected output: Setting CSS variables on the document root changes the appearance of all Ext JS components that reference these variables. Base color, font, and border radius are updated globally.
Custom Theme Package
# Create a custom theme with Sencha Cmd
sencha generate theme MyApp-theme /path/to/ext
# The generated structure:
# packages/
# local/
# MyApp-theme/
# package.json
# sass/
# var/
# component/
# src/
# component/
# resources/
// sass/var/all.scss - Global variable overrides
$base-color: #1a237e;
$base-highlight-color: #283593;
$base-light-color: #e8eaf6;
$base-alt-color: #f5f5f5;
$color: #212121;
$font-family: 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;
$font-size: 14px;
$border-radius: 4px;
// Panel variables
$panel-header-background-color: $base-color;
$panel-header-color: #ffffff;
$panel-header-font-size: 16px;
$panel-border-color: #c5cae9;
// Grid variables
$grid-header-background-color: $base-light-color;
$grid-header-color: $base-color;
$grid-row-cell-font-size: 13px;
$grid-row-cell-alt-background-color: #fafafa;
$grid-row-cell-selected-background-color: #c5cae9;
// Button variables
$button-default-background-color: $base-color;
$button-default-color: #ffffff;
$button-default-border-color: darken($base-color, 5%);
$button-toolbar-background-color: #ffffff;
$button-toolbar-color: $base-color;
// Form field variables
$form-field-border-color: #c5cae9;
$form-field-focus-border-color: $base-color;
$form-field-label-color: $color;
Expected output: The custom theme changes all panels to dark blue headers, light blue grid headers, styled buttons, and form fields with blue focus borders. The theme is compiled with Sencha Cmd and applied to the application.
Component-Level Styling
// 1. Inline style config
Ext.create('Ext.panel.Panel', {
title: 'Styled Panel',
style: {
backgroundColor: '#e8f5e9',
borderColor: '#66bb6a',
borderWidth: '2px'
},
bodyStyle: {
padding: '20px',
fontSize: '16px'
}
});
// 2. CSS class
Ext.create('Ext.button.Button', {
text: 'Custom Button',
cls: 'my-custom-button',
// Add multiple classes
cls: ['my-btn', 'my-btn-primary']
});
// 3. Component UI
Ext.create('Ext.panel.Panel', {
title: 'Custom UI Panel',
ui: 'custom-panel'
});
// Define the UI style
// sass/src/panel/Panel.scss
// @include extjs-panel-ui(
// $ui: 'custom-panel',
// $header-background-color: #ff9800,
// $header-color: #ffffff,
// $border-color: #e65100
// );
Expected output: Panels, buttons, and other components accept style, cls, and ui configurations for targeted visual customization.
Responsive Design
Ext.create('Ext.panel.Panel', {
title: 'Responsive Panel',
responsiveConfig: {
// Desktop (> 800px)
'width > 800': {
width: 700,
height: 400,
layout: 'hbox'
},
// Tablet (481-800px)
'width > 480 && width <= 800': {
width: '100%',
height: 350,
layout: 'vbox'
},
// Mobile (<= 480px)
'width <= 480': {
width: '100%',
height: 300,
layout: 'vbox',
collapsible: true,
collapsed: true
}
}
});
// Responsive grid columns
Ext.create('Ext.grid.Panel', {
responsiveConfig: {
'width > 800': {
columns: [
{ text: 'ID', width: 50 },
{ text: 'Name', flex: 2 },
{ text: 'Email', flex: 3 },
{ text: 'Phone', width: 130 },
{ text: 'Status', width: 100 },
{ text: 'Actions', width: 150 }
]
},
'width <= 800': {
columns: [
{ text: 'Name', flex: 1 },
{ text: 'Email', flex: 2 },
{ text: 'Status', width: 80 }
]
}
}
});
Expected output: Panels and grids adjust their layout, size, columns, and visibility based on viewport width. ResponsiveConfig evaluates expressions and applies matching configs.
Dark Theme Toggle
Ext.define('MyApp.controller.Theme', {
extend: 'Ext.app.Controller',
init: function() {
this.control({
'button[action=toggleTheme]': {
click: 'onToggleTheme'
}
});
},
onToggleTheme: function(btn) {
var isDark = Ext.getDoc().hasCls('dark-theme');
if (isDark) {
this.applyLightTheme();
} else {
this.applyDarkTheme();
}
},
applyDarkTheme: function() {
Ext.getDoc().addCls('dark-theme');
Ext.getDoc().removeCls('light-theme');
// Update CSS variables for dark mode
Ext.getDoc().setStyle('--background-color', '#121212');
Ext.getDoc().setStyle('--text-color', '#e0e0e0');
Ext.getDoc().setStyle('--surface-color', '#1e1e1e');
Ext.getDoc().setStyle('--primary-color', '#90caf9');
Ext.getDoc().setStyle('--border-color', '#333333');
// Reapply application theme
Ext.util.CSS.updateRule('.x-panel', 'background-color', '#1e1e1e');
Ext.util.CSS.updateRule('.x-panel-header', 'background-color', '#333333');
Ext.util.CSS.updateRule('.x-grid-row', 'background-color', '#1e1e1e');
Ext.util.CSS.updateRule('.x-grid-row-alt', 'background-color', '#2a2a2a');
// Save preference
localStorage.setItem('theme', 'dark');
},
applyLightTheme: function() {
Ext.getDoc().addCls('light-theme');
Ext.getDoc().removeCls('dark-theme');
Ext.getDoc().setStyle('--background-color', '#ffffff');
Ext.getDoc().setStyle('--text-color', '#212121');
Ext.getDoc().setStyle('--surface-color', '#f5f5f5');
Ext.getDoc().setStyle('--primary-color', '#1a237e');
Ext.getDoc().setStyle('--border-color', '#e0e0e0');
localStorage.setItem('theme', 'light');
}
});
Expected output: Clicking a toggle button switches between light and dark themes. CSS variables update immediately, and the preference is saved to localStorage for persistence.
Icon Customization
// Use Font Awesome icons instead of default PNGs
Ext.define('MyApp.overrides.Icon', {
override: 'Ext.panel.Tool',
// Use Font Awesome icons for panel tools
getToolConfig: function() {
var config = this.callParent();
if (config.type === 'close') {
config.iconCls = 'x-fa fa-times';
} else if (config.type === 'maximize') {
config.iconCls = 'x-fa fa-window-maximize';
} else if (config.type === 'minimize') {
config.iconCls = 'x-fa fa-window-minimize';
} else if (config.type === 'gear') {
config.iconCls = 'x-fa fa-cog';
}
return config;
}
});
// Global icon configuration
Ext.setIconCls({
// Default icons for common components
close: 'x-fa fa-times',
menu: 'x-fa fa-bars',
refresh: 'x-fa fa-refresh',
search: 'x-fa fa-search',
add: 'x-fa fa-plus',
delete: 'x-fa fa-trash',
edit: 'x-fa fa-pencil',
save: 'x-fa fa-floppy-o',
cancel: 'x-fa fa-ban',
// Custom application icons
dashboard: 'x-fa fa-tachometer',
users: 'x-fa fa-users',
products: 'x-fa fa-cubes',
reports: 'x-fa fa-bar-chart'
});
Expected output: All component icons are replaced with Font Awesome equivalents, giving a modern, consistent icon set across the entire application.
Performance and Theme Optimization
// 1. Disable unused component styles
Ext.require([
'Ext.grid.Panel',
'Ext.form.Panel',
'Ext.tree.Panel',
'Ext.chart.CartesianChart'
// Don't require components you don't use
]);
// 2. Lazy load theme resources
Ext.Loader.loadScript('/resources/MyApp-theme-all.js', function() {
// Theme loaded, now create the app
Ext.application({ /* ... */ });
});
// 3. Use compact themes for non-critical components
// Sencha Cmd: sencha app build --theme MyApp-compact-theme
// 4. Disable animations on low-end devices
if (Ext.os.is.Android && Ext.os.version.lt('5')) {
Ext.enableAnimations = false;
}
// 5. Sprite icons and images
// Use CSS sprites for custom icons to reduce HTTP requests
// Configure in sass/var/all.scss
$include-missing-icons: false; // Don't load individual PNG files
Expected output: Reduced CSS/JS payload by only including styles for used components. Animations disabled on older devices improves perceived performance.
Common Mistakes
Overriding component styles globally with CSS - Direct CSS overrides on .x- classes break when Ext JS updates. Use the theme variable system or component UI instead.
Not rebuilding after theme changes - SCSS variable changes require a Sencha Cmd build. Running the app without rebuilding shows old styles.
Mixing too many fonts - Ext JS loads font files for icons. Adding custom web fonts increases page weight. Limit to one icon font and one UI font family.
Forgetting responsive breakpoints - Themes tested only on desktop break on mobile. Set responsiveConfig on all major components and test at 320px, 768px, and 1280px.
Using inline styles instead of theme variables - Inline styles override theme variables but create maintenance nightmares. A single variable change in the theme updates hundreds of components.
Practice Questions
- How do CSS variables control component appearance in Ext JS?
- What is the purpose of the ui config on a component?
- How would you create a theme that changes all button colors?
- What is responsiveConfig and how does it work?
- How do you implement a dark mode toggle?
Challenge: Build a fully themed application with: a custom theme package using brand colors, a dark mode toggle with localStorage persistence, responsive grid columns that hide columns on mobile, Font Awesome icon integration, custom component UI for primary and secondary buttons, and a settings panel where users can customize accent color and font size.
FAQ
Mini Project
Build a theming demo application with: a light and dark theme toggle with smooth transition, a theme settings panel (accent color picker, font size slider, border radius selector), responsive layout that switches from desktop sidebar to mobile bottom navigation, custom component UIs for panels, buttons, and form fields, Font Awesome icons for all toolbar actions, and theme preference saved to localStorage.
What's Next
Theming makes your app look professional. Learn how Ext JS Extensions and Plugins extend functionality with custom components, plugins, mixins, and reusable extensions.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro