Using Themes in Apps

Smartface Cloud IDE provides theming feature for UI editor components.

This allows you to set default properties of each component with classes just like CSS. These classes can be overridden by other classes as well, which will be explained later. Reusing components with themes across projects is also possible with this way.

Multiple themes can also be defined under the same project and different styles (properties) can be set for each component.

1. When You Will Need Themes

If you want to use any component on other projects, you don't need to set its properties one by one each time. Just define a class under your theme, set your style properties once, then use it multiple times!

2. How To Use

When you create your workspace, a theme named defaultTheme will be present under themes folder. There must be at least one theme in your project.

Theme defaultTheme is a special theme and must be kept.

Each folder under themes represents a theme which you can give any name you want. Under your theme folder, there is a file called index.json which stores the configuration of the related theme. This file may include Defaults section.

Defaults must include all existing components with their default class names. Those class names must be unique for each component and defined in separate files called style files.

Each style file must denote one class.

Sample index is as follows:

{
"Defaults": {
"ActivityIndicator": ".activityIndicator",
"Button": ".button",
"FlexLayout": ".flexLayout",
"HeaderBar": ".headerBar",
"ImageView": ".imageView",
"Label": ".label",
"ListView": ".listView",
"MapView": ".mapView",
"Page": ".page",
"SearchView": ".searchView",
"Slider": ".slider",
"StatusBar": ".statusBar",
"Switch": ".switch",
"TextBox": ".textBox",
"TextArea": ".textArea",
"VideoView": ".videoView",
"WebView": ".webView",
"ScrollView": ".scrollView",
"ListViewItem": ".listViewItem"
},
"Paths": {
"defaults": "/styles/defaults",
"pages": "/styles/pages"
},
"parent": "parentThemeName"
}

2.1. Style File Template

Under the styles folder there are style files defined for each component. These files must be in JSON format. If you end up with an invalid file, an error popup will be generated to guide you.

Known Issue

Properties must be always above classnames. If not classes can not be inherited below properties.

Sample style file for a Button component is as follows:

{
".button": {
"width": 250,
"height": 70,
"touchEnabled": true,
"text": "Button",
"visible": true,
"backgroundColor": "#00A1F1",
"alpha": 1,
"borderColor": "rgba(0,0,0,1)",
"borderWidth": 0,
"textColor": "#FFFFFF",
"textAlignment": "MIDCENTER",
"font": {
"size": 16,
"bold": false,
"italic": false,
"family": "Arial"
},
"flexProps": {
"positionType": "RELATIVE",
"alignSelf": "AUTO"
},
"&-small": {
"width": 100
},
"&-medium": {
"width": 250
},
"&-large": {
"width": 350
},
"&-warning": {
"backgroundColor": "#9F6000"
},
"&-error": {
"backgroundColor": "#D8000C"
},
"&-success": {
"backgroundColor": "#4F8A10"
}
}
}

Each style file must have these characteristics

Being a valid JSON Wrapped with a default class name (such as .button)

Following properties are conventionally given under "flexProps"

positionType, flexDirection, flexGrow, flexWrap, alignContent, alignItems, alignSelf, direction, justifyContent, overflow, flexShrink

Don't forget that following properties must be given under "font"

size, bold, italic, family

BackgroundColor With Gradient

It is possible to color backgroundColor gradient with style files. Creating a new style file or modifying exist style file provides gradient supplement for backGroundColor from code section.

{
".flexLayout": {
"height": 200,
"width": 200,
"touchEnabled": true,
"visible": true,
"backgroundColor": {
"startColor": "#340470",
"endColor": "#c6aee5",
"direction": "DIAGONAL_LEFT"
},
"alpha": 1,
"borderColor": "rgba(0,0,0,1)",
"borderWidth": 0,
"font": {},
"flexProps": {
"alignSelf": "AUTO",
"direction": "INHERIT",
"alignContent": "STRETCH",
"alignItems": "STRETCH",
"justifyContent": "FLEX_START",
"flexWrap": "NOWRAP",
"flexDirection": "COLUMN",
"positionType": "RELATIVE"
},
"&-small": {
"width": 100
},
"&-medium": {
"width": 250
},
"&-large": {
"width": 350
}
}
}

2.2. Class Names

Each component comes with a class name. When a component first created, class name is retrieved from index.json. For example, when a Button is created, it comes with class name of .button.

A component can get any class you want. You can choose from existing ones or create a new one by just clicking the plus button.

Autocomplete is also supported.

When you select a class tag, all of it's properties are listed. You can add, edit or remove properties.

Class tags are draggable. Changing order will update properties due to new class ordering. Last tag will have highest precedence.

In our example, button component has .button and .button-error classes. They both have backgroundColor property. But .button-error has higher precedence so in result, our button will have backgroundColor of red.

If no class name is selected, then combined properties are shown. That is, ultimate result of properties to be shown on design area.

Combined properties are a combination of both:

  • Properties of classes

  • User properties

2.3. User Properties

User properties are properties that set by user from Properties Panel. These properties have highest precedence over class properties. Just like inline style in css, they are component specific.

In order to show that, User Properties are denoted as one tag, and placed at the end of class names.

Just like removing a class, User Properties can be removed too. One by one or all of them at the same time.

Exporting User Properties

It is possible to export user properties of a component as a new class so that you can use it with any component you want.

For instance, when you export them to a class, named .myClass following steps will occur accordingly:

  • Create .myClass under current theme

  • Set current component's user properties as .myClass properties

  • Delete current component's user properties

  • Add .myClass to current component's class name list

2.4. Rules

Some properties can be set when a specified condition is met. For example, user can specify backgroundColor of page to be blue only if the device is a tablet.

Currently rules can be specified under Device object. Available properties are below:

JavaScript
JavaScript
Device: {
screen: {
width: 0,
height: 0,
dpi: 0,
ppi: 0
},
os: "iOS" || "Android",
osVersion: "version",
type: "tablet" || "phone",
orientation: "landscape" || "portrait",
language: "EN"
}

Adding a rule

  • Select a class name

  • Press add sign next to Rules section

  • Select Device from dropdown as rule type.

  • For second area, type your condition. (In our example, Devce.type === "tablet")

  • Select the command.

  • To add a new property, press add sign.

  • And you can type any available property to your rule.

  • Styles will be rendered on phones and tablets differently.

2.5. Variables

It is possible to use variables for your themes which maximizes the reusability of the application styles.

Assume you have a theme called myAppTheme which is located under /themes/myAppTheme. Just right click and create a file named variables.json

variables.json

If the developer wants to use variables for a specific theme, then variables.json must be located as /themeName/variables.json. Naming of the file is important.

Sample variables file

It is nothing more than a key value mapping. Values are treated as variables.

variables.json
variables.json
{
"black": "rgba(0,0,0,1.0)",
"white": "#FFFFFF",
"backgroundMain": "#00A1F1",
"genericImage": "images://smartface.png"
}

Usage

Instead of setting hardcoded styles from your styles file, use variables. See the sample button.json file:

button.json
button.json
{
".sf-button": {
"backgroundColor": "${backgroundMain}",
"textColor": "${white}",
"width": 100,
"height": 50,
}
}

Inheritance with variables

It is possible to have themes without styles but variables only. With the power of the inheritance, you can just define different themes with variables.

With this approach, your app will have a base theme and may have lots of simple themes to support different themes. And this way, adding a new theme becomes surprisingly easy!

Variable type

Type of variables must be string. Other than string types are not supported currently such as boolean, number.

Using Themes Programmatically

Creating theme context must be done in app.js just like below:

JavaScript
JavaScript
const Application = require("sf-core/application");
const themeConfig = require("./settings.json").config.theme;
const { createThemeContextBound } = require("@smartface/contx/lib/styling/ThemeContext");
const themeSources = themeConfig.themes
.map(name => ({
name,
rawStyles: require(`./themes/${name}`),
isDefault: themeConfig.currentTheme === name
}));
Application.theme = createThemeContextBound(themeSources);

Switching Between Themes

JavaScript
JavaScript
myButton.onPress = () => {
// "this" is current page
// "Style1" is new theme to be switched to
changeTheme.call(this, "Style1");
};
function changeTheme(themeName) {
this.themeContext({
type: "changeTheme",
theme: themeName
});
this.dispatch({
type: "invalidate"
});
}

settings.json

This file is auto generated. You shouldn't modify its contents. When you change your current theme via the "Themes" panel, this file is updated automatically.

Theme Inheritance

All workspaces include defaultTheme and a base theme. Name of base theme varies workspace to workspace. If you have workspace named demo you will have base theme named demoTheme.

By default, base theme inherits defaultTheme and all other themes created by user inherit base theme. But this behavior could be overridden.

Parent of a theme could be specified on theme's index file such:

JavaScript
JavaScript
{
"parent": "bar"
}

In this case related theme will inherit bar.

Style File Management

Class names created by user will be saved on different files performing following steps:

  • Get root of class name (For example root of .button-small is button)

  • If root denotes an sf-core component (For button it does) properties of related class name will be saved under related theme's styles/defaults folder. (as button.json in this example)

    • styles/defaults path is configurable.

  • If root denotes a page name (Our class may be #myPage-red) properties of related class name will be saved under related theme's styles/pages folder. (as MyPage.json in this example)

    • styles/pages path is configurable.

  • If root does not match either a page name or an sf-core component then class name must be generic. (For example .foo) In that case properties of related class name will be saved under related theme's styles/common.json.

    • styles/common.json path is not configurable.

Path configuration can be performed on related theme's index.json fie such:

JavaScript
JavaScript
{
"Paths": {
"defaults": "/styles/defaults",
"pages": "/styles/pages"
}
}

If no Paths field is provided on index.json, /styles folder will be considered for both paths.

3. Limitations

Currently iOS and Android specific properties cannot be set by the style files.