Theme development

How theme works

The theming system in Experience Builder provides you the power to systematically customize the look-and-feel of your apps. Experience Builder provides a default theme with configurable attributes called theme variables and style modules for the UI components from Jimu UI. Under the hood, the style modules read the theme variables and use them to generate CSS styles at runtime and apply them to components accordingly.

All themes are customized and extended from the default theme.

A specific theme usually consists of the following:

  • theme variable overrides: colors, typography, spacings, components, etc. provided in a JSON file.
  • style overrides: either a Sass file(.scss) or a TypeScript file(.ts)
  • supporting assets: fonts, images, etc.

Theme variables

Theme variables are the configurable attributes that the components use to style themselves with.

Core variables

  • Colors:
    • primary: used to present primary UI elements, show active states, or interactive sections
    • secondary: used to present secondary UI elements
    • info: used to present general information
    • success: used to present a success state, such as completion of a process or passing a successful check
    • warning: used to present an in-progress state or warning information that the users should be noticed
    • danger: used to present a failed state or a severe situation that the users should be aware of
    • light: mostly used to present the background and border of layout elements, such as containers and separators
    • dark: mostly used to present text elements
    • black
    • white
    • All the above theme colors, except black and white, also have nine different shades (from 100 to 900)
  • Typography: font family, font sizes, font weights, and line heights
  • Spacing:
    • sizes: used to define margins and paddings
    • gutters: used to define small spaces, for example, space between an icon and text within a button
  • Border: the default border color and width
  • Border radius: none, sm, default, lg, circle, and pill
  • Box shadows: none, sm, default, lg

Component variables

  • Body: color and font definition to be applied to the body section
  • Header: Background and color definition to be applied to the header section
  • Footer: Background and color definition to be applied to the footer section
  • Link: color definition to be applied to the <a> tags
  • All the other UI components: Button, Dropdown, Nav, Modal, etc.

See the full theme variable JSON from the demo theme.

These variables are defined in a JSON format and can be overridden by a custom variables.json file.

How to create a theme

There are two approaches to create a new theme:

  1. Override the default theme variables
  2. Provide extra style modules or stylesheets

Normally, providing variable overrides should be sufficient and it is the easiest. Only consider the second approach if your theme design cannot be achieved by redefining variables and customize CSS styles are needed, for example, to provide a unique look-and-feel for a certain component.

Override theme variables

This is the easiest and most common approach to define your own theme in Experience Builder.

First, let's create a new folder under client/your-extensions/themes/ directory with the following structure:

  • variables.json: where the custom theme variables will go to
  • thumbnail.png: the thumbnail of the theme
  • manifest.json: view jimu-core/lib/types/manifest.d.ts for the list of properties

Open variables.json and add your custom overrides such as:

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
{
  "colors": {
    "primary": "red"
  },
  "typography": {
    "fontFamilyBase": "Impact, Arial",
    "fontSizeBase": "1rem"
  }
}

Re-run npm start in the client directory in your command prompt or terminal window to have Experience Builder pick up the new theme.

Override CSS styles

If you would like to provide additional CSS styles to your theme, or use CSS to override existing styles that simply overriding theme variables cannot achieve, you can do so by adding a style file to the theme.

The style file can have two different extensions (representing two different ways of writing styles):

  • .ts (recommended): uses the CSS-in-JS way to provide scoped CSS styles
  • .scss: the more "traditional" way of directly writing static CSS styles, with extra abilities offered by the Sass library.

Note: only one style file can be added to a theme folder.

style.ts

This refers to the use of the CSS-in-JS (Emotion library) method to provide scoped CSS styles to re-style components in your theme. Global styles are also considered as a special style module in the theming system.

The reasons that using CSS-in-JS is recommended are as follows:

  • Full access to the theme variables in a JSON structure from the code
  • The styles are scoped and targeted to certain components, making the style management more controlled

Example: re-style the Button component

First, create a style.ts file in the theme folder, and add the following required import to the file:

Use dark colors for code blocksCopy
1
import { css } from 'jimu-core';

Then we can start writing custom style modules for the target components by using the 'css' prop from Emotion.

Create a function with css prop as the returned value:

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
import { css } from 'jimu-core';

// custom styles applied to the Button component from Jimu UI:
const customButtonStyles = (props) => {
  return css`
    /* put your CSS here */
  `;
}

Optionally, you can call props.theme to get the theme variables from the props parameter, as well as other props available from the target component.

Put any of your CSS styles for the component inside of css` `:

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { css } from 'jimu-core';

// custom styles applied to the Button component from Jimu UI:
const customButtonStyles = (props) => {
  const theme = props.theme;
  const buttonType = props.type;
  return css`
    /* make the primary button's text to be bold and has a shadow */
    ${buttonType === 'primary' && `
      font-weight: ${theme.typography.weights.bold};
      box-shadow: ${theme.boxShadows.default};
    `}
  `;
}

Last step, export the function as a module renamed to the name of the component:

Use dark colors for code blocksCopy
1
2
// at the end of the style.ts file:
export { customButtonStyles as Button};

Example: global styles

Create another function with css prop as the returned value:

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
import { css } from 'jimu-core';

// global styles applied to the entire app:
const customGlobalStyles = (props) => {
  return css`
    /* make the root font size smaller */
    html {
      font-size: 80%;
    }
  `;
}

Global style module can be exported with the name 'Global':

Use dark colors for code blocksCopy
1
2
// at the end of the style.ts file:
export { customGlobalStyles as Global};

At runtime, Jimu's theme manager will load these extra styles along with the default one and apply them together to the components.

style.scss

This refers to the "traditional" way of writing styles in static CSS.

In the theme folder, you can optionally add a style.scss file to define your custom CSS with. In the runtime, this file will be imported last and will override all the other styles, such as the styles generated from variables.json.

Quick sample:

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
body {
  background: #333;
  color: white;
  font-family: Impact, Arial;
}
.btn {
  background: red;
}

Other helpful tips

Use custom fonts

Experience Builder allows custom fonts to be added to a theme.

The custom fonts can be files hosted on a server, such as Google Fonts, or local ones inside of the theme folder, such as path/to/your-theme/assets/fonts/.

Import fonts

If the font set comes with a font CSS file, then the font can be imported as such:

Using style.ts:

Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
import { css } from 'jimu-core';

// add font import to the global style function:
const customGlobalStyles = (props) => {
  return css`
    /* import Open Sans font */
    @import url('https://fonts.googleapis.com/css?family=Open+Sans');
  `;
}

export { customGlobalStyles as Global};

Using style.scss:

Use dark colors for code blocksCopy
1
2
/* import Open Sans */
@import url('https://fonts.googleapis.com/css?family=Open+Sans');

Apply custom fonts

Configure the fontFamilyBase attribute in variables.json:

Use dark colors for code blocksCopy
1
2
3
4
5
{
  "typography": {
    "fontFamilyBase": "Open Sans"
  }
}

Your browser is no longer supported. Please upgrade your browser for the best experience. See our browser deprecation post for more details.