Upload
sencha
View
108
Download
0
Embed Size (px)
Citation preview
Theming the Modern Toolkit
Phil GuerrantSenior Software Engineer, Sencha
Getting Started
Choosing Between App and Theme
• Both can contain styling
• Theme contains shared styling
• Application contains its own styling
Generating an Application
sencha -sdk path/to/sdk generate app -modern ThemeDemo theme-demo
Application Structure
Set theme variables in sass/var/all.scss
$base-color: green;
$panel-body-font-size: 15px;
Theme Variableshttp://docs.sencha.com
Application Structure
• Rule generating code goes in sass/src/• Mixin calls
• CSS rules
• scss files are auto-included based on class-path of classes in your application• Ext class - ThemeDemo.view.Main
• Includes sass/src/view/Main.scss
Rapid Development Using Fashion
sencha app watch --fashion
[INF] Server started at port : 1841[INF] Application available at http://localhost:1841
Generating a Theme PackageAllows styling to be shared between applications
# your application directory
cd theme-demo sencha generate package --type theme --extend theme-triton --name my-theme
Generating a Theme Package
Theme Package Structure
• scss files are auto-included based on class-path of framework classes
• e.g. Ext.field.Text
Theme HierarchyClassic Toolkit Modern Toolkit
base
neutral
classic
gray
neptune
crisp
crisp-touch
neptune-touch
triton
base
neptune
triton
ios
material
Variables
VariablesAll theme variables must be dynamic
• dynamic() must be used for any newly declared variables.
• When overriding framework variables dynamic() is not required
• When in doubt it is safe to always use dynamic()
Dynamic VariablesLast assignment wins, and is valid throughout entire scope
$base-color: dynamic(blue);
/* CSS Output */ .foo { background: red; }
$base-color: dynamic(red);
.foo { background: $base-color; }
Component VariablesBegin with xtype, end with CSS property name
$button-font-size
$tab-background-color
$listitem-border-width
Element VariablesElement name after xtype but before CSS property name
$button-badge-color
$textfield-input-padding
$listitem-disclosure-margin
State VariablesState name after the thing it describes (xtype or element)
$button-pressed-color
$listitem-selected-border-color
$listitem-disclosure-pressed-background-color
State Names
• pressed
• hovered
• selected
• focused
• disabled
Style Inheritance
Style Inheritance
Components should inherit styles defined by their superclasses
Style Inheritance
classCls – the inheritable baseCls
Ext.define('Child', { extend: 'Parent', classCls: 'x-child' });
Style InheritanceclassCls allows styles to cascade through the Component hierarchyExt.define('Parent', { extend: 'Ext.Component', classCls: 'x-parent' });
<div class="x-component x-parent x-child"></div>
Ext.create('Child');
Ext.define('Child', { extend: 'Parent', classCls: 'x-child', classClsRoot: true });
Style Inheritance – Opting OutclassClsRoot defines inheritance boundaryExt.define('Parent', { extend: 'Ext.Component', classCls: 'x-parent' });
<div class="x-child"></div>
Ext.create('Child');
Component UIs
Component UIsOne component, multiple presentations
Component UIsOne component, multiple presentationsExt.create({ xtype: 'button', ui: 'action' });
<div class="x-button x-button-action"></div>
Ext.define('Child', { extend: 'Parent', classCls: 'x-child' });
UI InheritanceUI name is appended to each classCls in the hierarchyExt.define('Parent', { extend: 'Ext.Component', classCls: 'x-parent' });
<div class="x-component x-parent x-child x-component-foo x-parent-foo x-child-foo"></div>
Ext.create('Child', { ui: 'foo' });
UIs are ComposableUse one or many, your choiceExt.create({ xtype: 'button', ui: 'action round' });
<div class="x-button x-button-action x-button-round"></div>
UI MixinsThe structure of a UI mixin// theme-neptune/sass/var/Button.scss@mixin button-ui( $ui: null, $color: null, $border-color: null ) { $ui-suffix: ui-suffix($ui); // -#{$ui} OR empty string .x-button#{$ui-suffix} { // .x-button[-ui] color: $color; border-color: $border-color; } }
UI MixinsThe magic of null
$color1: null; $color2: red;
/* CSS Output */ .button { border-color: red; }
.button { color: $color1; border-color: $color2; }
UI MixinsThe magic of null
@include button-ui( $ui: foo );
/* CSS Output */
Empty!
UI MixinsThe magic of null
@include button-ui( $ui: foo, $color: green );
/* CSS Output */ .x-button { color: green; }
UIs MixinsThe default UI// theme-neptune/sass/var/Button.scss
$button-color: #606060; $button-border-color: #e4e4e4; . . .
// theme-neptune/sass/src/Button.scss @include button-ui( $color: $button-color, $border-color: $button-border-color . . . );
No $ui parameter($ui == null)
UIs MixinsThe default UI/* CSS Output */ .x-button { . . . } .x-button.x-pressed { . . . } .x-button .x-icon-el { . . . }
UIs MixinsAdditional UIs/* CSS Output */ .x-button-alert { . . . } .x-button-alert.x-pressed { . . . } .x-button-alert .x-icon-el { . . . }
Theming Lists and Grids
Can Grids have UIs?
Column Header Check Column Header
GroupHeader
Row
CheckCell
ExpanderCell Paging
Toolbar
TextCell
Row NumbererCell
NumberCell
WidgetCell
DateCell
Grids are ListsUse itemConfig to configure the UI of list items
Ext.create({ xtype: 'list', itemConfig: { ui: 'contact' } });
@include listitem-ui( $ui: contact, $color: #fff, $background-color: #444, $border-color: #777 );
Before After
Row
Grids are ListsUse itemConfig to configure the UI of grid rows
Ext.create({ xtype: 'grid', itemConfig: { ui: 'contact' } });
@include gridrow-ui( $ui: contact, $background-color: #888 );
GroupHeader
Grids Group Row UIUse the “header” config of the grid row
Ext.create({ xtype: 'grid', itemConfig: { header: { ui: 'contact' } } });
@include rowheader-ui( $ui: contact, $background-color: #ccc );
Column Header
Grid Column Header UISet the UI on the column config
Ext.create({ xtype: 'grid', columns: [{ text: 'Name', ui: 'contact' }] });
@include gridcolumn-ui( $ui: contact, $color: white, $background-color: gray );
TextCell
Grid Cell UIUse the “cell” config of the column
Ext.create({ xtype: 'grid', columns: [{ text: 'Name', cell: { ui: 'contact' } }] });
@include gridcell-ui( $ui: contact, $font-weight: bold );
PagingToolbar
Grid Paging Toolbar UIUse the “toolbar” config of the Paging Toolbar plugin
Ext.create({ xtype: 'grid', plugins: [{ type: 'pagingtoolbar', toolbar: { ui: 'contact' } }] });
@include pagingtoolbar-ui( $ui: contact, $padding: 30px, $background-color: #ddd );
Compound Components are the Sum of Their PartsThis pattern is repeated throughout the framework
Other Examples:
• Slider/Thumb
• TextField/Trigger
Exceptions:
Panel/Panel Header/Tools (on roadmap for improvement)
Big Mode
Big ModeLarger sizing and spacing for touch screens
• Classic toolkit used separate themes (neptune vs. neptune-touch)
• Modern toolkit – single theme
Big ModeLarger sizing and spacing for touch screens
<html class="x-big">
Big ModeChoosing between big and normal modes
// theme-neptune/overrides/init.js Ext.theme.getDocCls = function() { return Ext.platformTags.desktop ? '' : 'x-big'; };
Big ModeOpting Out
$enable-big: false;
Big ModeImplementing big mode rules // UI mixin code .x-button { padding: $padding; @if $enable-big { .x-big & { padding: $padding-big; } } }
/* CSS Output */ .x-button { padding: 5px; } .x-big .x-button { padding: 10px; }
Breaking the Rules
Before Breaking the RulesWhat do I do when the theming API does not meet my needs?
1. Let us know on the forums – https://sencha.com/forum
2. Add your own styling using the same techniques that the framework uses.
classClsAlways matches the xtype and goes on the main elementExt.define('Ext.grid.column.Column', { xtype: 'gridcolumn', classCls: 'x-gridcolumn'});
<div class="x-gridcolumn x-gridcolumn-contact"></div>
// instance config { ui: 'contact' }
State and Configuration CSS ClassesAlways on the main elementExt.define('Ext.grid.column.Column', { xtype: 'gridcolumn', classCls: 'x-gridcolumn', config: { align: 'left' } });
<div class="x-gridcolumn x-sorted x-align-left"></div>
State and Configuration CSS ClassesAlways chain to the classCls
.x-align-left .x-title-el
.x-gridcolumn.x-sorted .x-sorted
.x-gridcolumn-contact.x-align-left
Reference ElementsShould be named x-[name]-el
Ext.define('Ext.grid.column.Column', { template: [{ reference: 'titleElement', className: 'x-title-el' }] });
Reference ElementsPrefer descendant selectors over child selectors
.x-gridcolumn .x-title-el { . . .}
.x-gridcolumn > .x-title-el { . . .}
Reference ElementsPrefer descendant selectors over child selectors
• Except when container nesting is possible
• Include xtype/ui info in class name
.x-panel-inner-foo { . . . }
When in DoubtCopy the selectors used by the framework
Material and iOS
iOS Material Material Dark
iOS Theme
• Based on Triton
• Follows the latest iOS look and feel
• Will evolve with iOS
• Customizable – all framework theme variables and mixins are available
Material Theme
• Based on Neptune
• Follows the material design guidelines https://material.google.com
• Material Color Palette
• Supports CSS Variables
Material Color Palette
Deep Purple #673AB7
Red #F44336
Material Color Palettehttps://material.google.com/style/color.html
Light Blue #03A9F4
Green #4CAF50
Yellow #FFEB3B
Deep Orange #FF5722
Blue Grey #607D8B
Pink #E91E63 Purple #9C27B0
Indigo #3F51B5 Blue #2196F3
Cyan #00BCD4 Teal #009688
Light Green #8BC34A Lime #CDDC39
Amber #FFC107 Orange #FF9800
Brown #795548 Grey #9E9E9E
Black #000000 White #FFFFFF
$base-color-name: indigo;
$accent-color-name: yellow;
$dark-mode: true;
Configuring the Material Theme
• Configure base color, accent color, and dark mode
• All derived colors, spacing and sizing are handled automatically
• Can be further customized using theme variables and mixins but this is outside the scope of material design.
Selecting Themes by PlatformStep 1: Setup multiple build profiles in app.json{ "builds": { "material": { "theme": "theme-material" }, "ios": { "theme": "theme-ios" }, "triton": { "theme": "theme-triton" } } }
Selecting Themes by PlatformStep 2: Setup output directories in app.json (same as universal app){ "output": { "base": "${workspace.build.dir}/${build.environment}/${app.name}", "page": "index.html", "manifest": "${build.id}.json", "js": "${build.id}/app.js", "appCache": { "enable": false }, "resources": { "path": "${build.id}/resources", "shared": "resources" } } }
Selecting Themes by PlatformStep 3: Use Ext.beforeLoad to select a profile<script type="text/javascript"> var Ext = Ext || {}; // Ext namespace won't be defined yet... Ext.beforeLoad = function (tags) { var profile; if (tags.ios) { profile = 'ios'; } else if (tags.android) { profile = 'material'; } else { profile = 'triton'; } Ext.manifest = profile; }; </script>
Demo
Please Take the Survey in the Mobile App
• Navigate to this session in the mobile app
• Click on “Evaluate Session”
• Respondents will be entered into a drawing to win one of five $50 Amazon gift cards