logo
dojo/framework

Working with themes

Widget theme keys

Dojo's theming framework uses the concept of a 'widget theme key' to connect style overrides to the corresponding widget that the styles are intended for. Style overrides are usually specified in a theme, but can also be passed directly via theme middleware's classes override property, if required.

The theme key for a given widget is determined as:

{package-name}/{widget-css-module-name}

where package-name is the value of the name property within the project's package.json, and widget-css-module-name is the filename of the primary CSS module used for the widget (without the .m.css extension).

Theme key example

For a given project:

package.json

{
    "name": "my-app"
}

When following widget CSS module naming conventions, a given widget such as src/widgets/MyWidget.ts will use a corresponding CSS module name similar to src/styles/MyWidget.m.css. The theme key for MyWidget is therefore:

my-app/MyWidget

Here, the name of the widget is the same as the the name of its CSS module file, but developers should be careful not to mistake the widget's theme key as representing the widget's TypeScript class name.

For a second widget that does not follow CSS module naming conventions, such as src/widgets/BespokeWidget.ts that uses a corresponding CSS module such as src/styles/BespokeStyleSheet.m.css, its widget theme key would instead be:

my-app/BespokeStyleSheet

Writing a theme

Themes are TypeScript modules that export a default object which maps widget theme keys to typed CSS module imports. CSS modules in a theme are the same as regular modules used directly in widgets. Once a theme is applied in an application, each widget identified via its theme key in the theme's definition object will have its styles overridden with those specified in the CSS module associated with that widget's theme key.

The following is a simple illustration of a complete theme for a single MyWidget widget (using a default CSS module of MyWidget.m.css), contained in a project named my-app:

src/themes/myTheme/styles/MyWidget.m.css

.root {
    color: blue;
}

src/themes/myTheme/theme.ts

import * as myThemedWidgetCss from './styles/MyWidget.m.css';

export default {
    'my-app/MyWidget': myThemedWidgetCss
};

Here, MyWidget is following naming conventions with its primary style class being named root, allowing myTheme to easily override it via the root class in its src/themes/myTheme/styles/MyWidget.m.css CSS module.

The theme associates the new root styling class to MyWidget via its theme key of my-app/MyWidget. When myTheme is applied, MyWidget will have its color set to blue and will no longer receive any other styles defined in the root class in its original CSS module.

Scaffolding themes for third-party widgets

It is likely that application themes will need to include styling of any third-party widgets that may be used, such as those provided by Dojo's native widget library.

The @dojo/cli-create-theme package provides tooling support to quickly generate theme scaffolding for third party widgets, via its dojo create theme CLI command. It can be installed locally within an application via:

npm install --save-dev @dojo/cli-create-theme

and can be used as follows from a project's root directory:

dojo create theme -n {myThemeName}

Running this command will begin to create the specified myThemeName theme by asking two questions:

  • What Package to do you want to theme?
    • The answer to this should be all the packages that contain the third-party widgets intended for theming, for example @dojo/widgets. The command will continue to ask for more packages until a user is done.
  • Which of the {third-party-package} theme files would you like to scaffold?
    • A list will be shown of all themeable widgets in the third-party packages that were specified when answering the first question. Users can then pick the subset of all compatible widgets that should be included in the resulting theme - usually only the widgets that are actually used in the current application will be selected, to help keep the theme's size to a minimum.

Several files will be created in the current project upon successful execution of the command:

  • src/themes/{myThemeName}/theme.ts
  • src/themes/{myThemeName}/{third-party-package}/path/to/{selectedWidget}.m.css

The theme's CSS modules created for all {selectedWidget}s come ready with themeable CSS selectors which can then be filled in with the appropriate stylings for {myThemeName}.

Compatible packages

Any third-party package that has a theme directory containing widget CSS module files (*.m.css) and their corresponding compiled definition files (*.m.css.js - see Distributing themes for details on what these are) is compatible.

For example:

node_modules
└── {third-party-package}
    └── theme
        │   {widget}.m.css
        │   {widget}.m.css.js

Distributing themes

Dojo's cli-build-theme package provides a CLI command to help build themes that are intended for distribution across multiple applications. It will create all files necessary to use the theme in a variety of different ways.

Note that when using dojo create theme to scaffold a new theme, there is no need to use dojo build theme, as all relevant files will already be in place. This applies to themes in projects that are built either via @dojo/cli-build-app or @dojo/cli-build-widget.

To use the tooling, install @dojo/cli-build-theme locally in a theme project:

npm install --save-dev @dojo/cli-build-theme

Then to build a theme, run the command and specify a theme name as well as an optional release version:

dojo build theme --name={myThemeName} --release={releaseVersion}

If no release is specified, then the current version from package.json will be used instead.

Running the command will create a new dist/src/{myThemeName} directory in the project containing:

Using Dojo-provided themes

The @dojo/themes package provides a collection of ready-to-use themes that cover all widgets in Dojo's native widget library. The themes can be used as-is, or composed as the basis for a full application theme.

  1. To use the themes, install @dojo/themes into your project, for example through npm i @dojo/themes. Then, for regular Dojo applications:

  2. Import the theme CSS into your project's main.css:

    @import '~@dojo/themes/dojo/index.css';
    
  3. Import the theme TypeScript module and use it as per any other theme:

    import theme from '@dojo/themes/dojo';
    
    render() {
        return w(Button, { theme }, [ 'Hello World' ]);
    }
    

If attempting to use the themes in custom elements, after installing @dojo/themes:

  1. Add the custom element-specific theme CSS to index.html:

    <link rel="stylesheet" href="node_modules/@dojo/themes/dojo/dojo-{version}.css" />
    
  2. Add the custom element-specific theme JS to index.html:

    <script src="node_modules/@dojo/themes/dojo/dojo-{version}.js"></script>
    

Composing off Dojo themes

Once @dojo/themes is installed in a project, it can be used as the basis for an extended application theme by including relevant components with CSS modules' composes functionality in the new theme.

@dojo/themes also includes its own :root variables.css file which can be imported if the extended application theme would like to reference Dojo-specified properties elsewhere in the new theme.

The following is an example of a new theme for the @dojo/widgets/button widget that extends off @dojo/themes, and changes the button's background to green while retaining all other button theme style properties:

src/themes/myTheme/theme.ts

import * as myButton from './myButton.m.css';

export default {
    '@dojo/widgets/button': myButton
};

src/themes/myTheme/myButton.m.css

@import '@dojo/themes/dojo/variables.css';

.root {
    composes: root from '@dojo/themes/dojo/button.m.css';
    background-color: var(--dojo-green);
}