Themes

As of the Kilo release, styling for the OpenStack Dashboard can be altered through the use of a theme. A theme is a directory containing a _variables.scss file to override the color codes used throughout the SCSS and a _styles.scss file with additional styles to load after dashboard styles have loaded.

As of the Mitaka release, Horizon can be configured to run with multiple themes available at run time. It uses a browser cookie to allow users to toggle between the configured themes. By default, Horizon is configured with the two standard themes available: ‘default’ and ‘material’.

To configure or alter the available themes, set AVAILABLE_THEMES in local_settings.py to a list of tuples, such that ('name', 'label', 'path')

name

The key by which the theme value is stored within the cookie

label

The label shown in the theme toggle under the User Menu

path

The directory location for the theme. The path must be relative to the openstack_dashboard directory or an absolute path to an accessible location on the file system

To use a custom theme, set AVAILABLE_THEMES in local_settings.py to a list of themes. If you wish to run in a mode similar to legacy Horizon, set AVAILABLE_THEMES with a single tuple, and the theme toggle will not be available at all through the application to allow user configuration themes.

For example, a configuration with multiple themes:

AVAILABLE_THEMES = [
    ('default', 'Default', 'themes/default'),
    ('material', 'Material', 'themes/material'),
]

A configuration with a single theme:

AVAILABLE_THEMES = [
    ('default', 'Default', 'themes/default'),
]

Both the Dashboard custom variables and Bootstrap variables can be overridden. For a full list of the Dashboard SCSS variables that can be changed, see the variables file at openstack_dashboard/static/dashboard/scss/_variables.scss.

In order to build a custom theme, both _variables.scss and _styles.scss are required and _variables.scss must provide all the default Bootstrap variables.

Inherit from an Existing Theme

Custom themes must implement all of the Bootstrap variables required by Horizon in _variables.scss and _styles.scss. To make this easier, you can inherit the variables needed in the default theme and only override those that you need to customize. To inherit from the default theme, put this in your theme’s _variables.scss:

@import "/themes/default/variables";

Once you have made your changes you must re-generate the static files with:

python manage.py collectstatic

By default, all of the themes configured by AVAILABLE_THEMES setting are collected by horizon during the collectstatic process. By default, the themes are collected into the dynamic static/themes directory, but this location can be customized via the local_settings.py variable: THEME_COLLECTION_DIR

Once collected, any theme configured via AVAILABLE_THEMES is available to inherit from by importing its variables and styles from its collection directory. The following is an example of inheriting from the material theme:

@import "/themes/material/variables";
@import "/themes/material/styles";

All themes will need to be configured in AVAILABLE_THEMES to allow inheritance. If you wish to inherit from a theme, but not show that theme as a selectable option in the theme picker widget, then simply configure the SELECTABLE_THEMES to exclude the parent theme. SELECTABLE_THEMES must be of the same format as AVAILABLE_THEMES. It defaults to AVAILABLE_THEMES if it is not set explicitly.

Bootswatch

Horizon packages the Bootswatch SCSS files for use with its material theme. Because of this, it is simple to use an existing Bootswatch theme as a base. This is due to the fact that Bootswatch is loaded as a 3rd party static asset, and therefore is automatically collected into the static directory in /horizon/lib/. The following is an example of how to inherit from Bootswatch’s darkly theme:

@import "/horizon/lib/bootswatch/darkly/variables";
@import "/horizon/lib/bootswatch/darkly/bootswatch";

Organizing Your Theme Directory

A custom theme directory can be organized differently, depending on the level of customization that is desired, as it can include static files as well as Django templates. It can include special subdirectories that will be used differently: static, templates and img.

The static Folder

If the theme folder contains a sub-folder called static, then that sub folder will be used as the static root of the theme. I.e., Horizon will look in that sub-folder for the _variables.scss and _styles.scss files. The contents of this folder will also be served up at /static/custom.

The templates Folder

If the theme folder contains a sub-folder templates, then the path to that sub-folder will be prepended to the TEMPLATE_DIRS tuple to allow for theme specific template customizations.

Using the templates Folder

Any Django template that is used in Horizon can be overridden through a theme. This allows highly customized user experiences to exist within the scope of different themes. Any template that is overridden must adhere to the same directory structure that the extending template expects.

For example, if you wish to customize the sidebar, Horizon expects the template to live at horizon/_sidebar.html. You would need to duplicate that directory structure under your templates directory, such that your override would live at { theme_path }/templates/horizon/_sidebar.html.

The img Folder

If the static root of the theme folder contains an img directory, then all images that make use of the {% themable_asset %} templatetag can be overridden.

These assets include logo.svg, splash-logo.svg and favicon.ico, however overriding the SVG/GIF assets used by Heat within the dashboard/img folder is not currently supported.