Creating custom themes
Overview
While the base technologies used within Tui-based themes have changed, a number of theme features from older versions of Totara are still available. Totara 13's new Ventura theme provides a variable and override approach for other custom themes that inherit from Ventura. Ventura itself inherits the new Legacy theme, which provides a pathway to migration away from older technology, while retaining support for Totara Learn and applying a level of visual consistency with Totara Perform and Totara Engage.
Implementation
Custom theme inheriting from Ventura and Legacy
To create a new theme targeting the new features in Totara 13, you will need to create files in both the server and client directories. The naming convention for your custom theme must follow these rules:
The name should start with one or more lowercase alpha characters (a-z).
If there are any underscores:
All characters before the first underscore need to be lowercase alpha characters (a-z).
The character directly following the first underscore needs to be lowercase alpha (a-z), the following characters can be any combination of underscores and lowercase alphanumeric values.
The name should end with lowercase alphanumeric values (a-z, 0-9).
The files you'll need are:
server/theme/mytheme/config.php
<?php
$THEME->doctype = 'html5';
$THEME->name = 'mytheme';
$THEME->parents = ['ventura', 'legacy', 'base'];
$THEME->enable_dock = true;
$THEME->enable_hide = true;
$THEME->minify_css = false;
server/theme/mytheme/lang/en/mytheme.php
<?php
$string['pluginname'] = 'My Theme';
$string['choosereadme'] = 'My Theme is a custom theme for Totara.';
// Block positions.
$string['region-bottom'] = 'Bottom';
$string['region-main'] = 'Main';
$string['region-side-post'] = 'Right';
$string['region-side-pre'] = 'Left';
$string['region-top'] = 'Top';
server/theme/mytheme/version.php (copy from the Ventura version.php and update the version and component fields)
If you want to enable the Theme Settings page for your custom theme, you'll also need to copy the following files from Ventura and update them to match your theme's name:
index.php
lib.php
settings.php
That takes care of the files in the server directory, but if we want to apply customisations to the Tui styling or components, we also need to create a folder in the client/component directory with the right files.
This can be done in one step by running npm run tui-init theme_mytheme vendor
(where vendor
is a unique string identifying your organisation. Lowercase is suggested for ease of typing - Totara internal components use 'totara' for example).
At this point you're now ready to start customising.
In order to customise component CSS, template, or functionality, you can add a component file in a special location where it will be picked up by the override system. Refer to the Totara 13 Theme stack diagram for the high-level flow of inheritance and overrides.
Custom theme inheriting from Legacy only
Follow the same steps as above, but remove 'ventura' from the array in config.php.
Customising variables and global CSS
After completing the instructions above, you can add your theme customisations in the following places:
client/component/my_theme/global_styles/_variables.scss (variable overrides)
client/component/my_theme/global_styles/static.scss (global CSS rules)
You can also break up your variables or global CSS rules into multiple files and use the @import
syntax to include them. @import 'my_theme/xyz'
maps to client/component/my_theme/global_styles/xyz
. You can see this in action at client/component/tui/src/global_styles/_variables.scss
.
Overriding component styles or functionality
Any Vue component can be extended in a theme by creating a file in the right folder.
The pattern for override files is: client/component/theme_(themename)/src/(subfolder)/overrides/(original client/component/* subfolder)/(rest of path)
For example, if the component you want to extend is: tui/components/buttons/Button
, you would create client/src/theme_mytheme/components/overrides/tui/buttons/Button.vue
This component will replace the original one, unless it only contains a <style lang="scss">
block, in which case the original component will continue to be loaded.
Variable usage
In general, variables used by components are defined in Tui core or plugins and can then be overridden by themes.
CSS variables may freely depend on the value of other variables, and updating the value of a base variable will change the computed value of variables that depend on it as well.
Both CSS variables and SCSS variables are available, but it is strongly recommended to use CSS variables as they work a lot better for theming - chains of CSS variables do not pose a problem, whereas they do in SCSS due to its imperative nature.
Note that code in a Tui component may not depend on variables defined in another Tui component, unless that component is declared as a dependency in tui.json.
Derived variables
Some variables within the Ventura theme are preceded by a comment when they are defined in src/global_styles/_variables.scss
. These comments denote that the variables will be processed when a change is made to the file. We process these variables to output a JSON object which describes the variables and their relationship to one another. The JSON output for a Tui-based theme can be found in client/component/theme_customthemename/build/css_variables.json
. For example, the following variable definition has two preceding comments:
The first, /* theme:var */
, tells the processing script to process the variable. The second, /* theme:derive adjust-hex-value-brightness(var(--color-state), -10) */
, is additional data to be used during the process step.
In this example, the hover colour value for the variable --color-state-hover
will be calculated by a script in tui/theme, specifically the method adjust-hex-value-brightness()
will be called, using the variable --color-state
as a starting point and adjusting the brightness value by -10
.
Processing variables in this way provides a replacement for the now unsupported SCSS functions (Darken()
, Lighten()
, mix()
, button-variant()
etc.), as they do not operate on CSS Variables.
It also provides a way to reduce the number of colour options expressed in the Theme Settings page, and have them automatically re-processed with Javascript when a base colour is changed. This removes the need for the SCSS compiler to be invoked as there is no need to re-call a SCSS function such as Darken()
.
The JSON object that is output is then consumed by the Theme Appearance page and used to populate field values.
Tui-based custom themes that add these comments above variables defined in _variables.scss will also result in a JSON object being output for that theme. The custom theme could then express any new or altered variables in a Theme Appearance page Vue SFC file, therefore customising the available UI options for the custom theme.
CSS load order
CSS is included in the following order:
Legacy CSS.
SCSS variables in the same order as below (typically not present as we prefer CSS variables for their greater flexibility).
CSS variables and CSS from Tui core.
CSS variables and CSS from each Tui component.
CSS variables and CSS from each Tui theme.
Theme customisations via UI - CSS variable overrides.
Theme customisations - user-defined custom CSS.
Tips and known limitations
CSS class names - as with custom components, if you are defining an entirely new component it is recommended that you use a prefix other than
tui-
in the class names to avoid collisions.If you are entirely replacing a component (new template, style, and script), you will probably find it easier to use a new CSS class name to avoid collisions with the old styling.
Overriding TUI Core or other TUI Plugin code blocks from inside another plugin other than a theme is not currently supported, due to the risk of third-party plugins detrimentally overriding code.
When using a custom theme no customisations of the parent theme settings are applied to the custom theme. Child themes inherit only from the default parent theme settings.
Recommended reading
Totara publishes an Example Theme to demonstrate the structure of a custom theme, including both features from the Tui framework, and features from earlier versions of Totara (useful when upgrading your custom theme to use Tui).