Themes

Themes are used to manage the visual presentation of the application.

A theme consists of CSS files and other resources like images and fonts.

Jmix provides jmix-lumo theme built on top of Vaadin Lumo theme.

Applying a Theme

The application theme is applied using the @Theme annotation with the name of the theme folder as a parameter. The @Theme annotation must be placed on the class that implements the AppShellConfigurator interface, which is usually the main application class.

@Theme(value = "my-theme")
public class MyProjectApplication implements AppShellConfigurator {
    ...
}

A theme variant can be specified as an additional parameter, such as dark or light.

@Theme(value = "my-theme", variant = "dark")
public class MyProjectApplication implements AppShellConfigurator {
    ...
}
Themes cannot be switched at runtime. Although a project may have multiple themes, only one can be applied for the UI. However, there are ways to switch between variants of the same theme and load dynamically additional styles on top of a theme.

Application Theme

For use in an application, the theme folder is placed inside the frontend/themes folder, with a name of your choosing. Jmix application projects created using Studio have a predefined theme folder with the same name as the project itself.

theme folder structure
Figure 1. Theme folder file structure
  • my-project - the theme folder, also used as theme name

  • styles.css - the master stylesheet

  • my-project.css - an entry point for custom styles and imports

  • theme.json - a theme configuration file, which by default defines jmix-lumo as the default theme.

styles.css is the entry point to the theme’s styles. All CSS, including Lumo style property values and custom component styles, can be added there.

It can be useful to split CSS into multiple files to avoid cluttering the main stylesheet. Additional CSS files are loaded using @import directives. By default, the project’s master stylesheet contains a single import of <theme-name>.css file that defines your styles and any other imports.

styles.css
@import url('./my-project.css');

Reusable Theme

An application theme can be used in multiple applications by packaging it as a JAR dependency.

The theme project’s structure looks as follows:

theme addon structure
Figure 2. Theme project structure
  • cobalt - the theme folder, also used as theme name

  • styles.css - the master stylesheet. Can contain custom styles and imports, for example:

    html {
        --lumo-border-radius: calc(var(--lumo-size-m) / 2);
    
        --lumo-primary-color: rgb(0, 85, 166);
        --lumo-primary-color-50pct: rgba(0, 85, 166, 0.5);
        --lumo-primary-color-10pct: rgba(0, 85, 166, 0.1);
        --lumo-primary-text-color: rgb(0, 85, 166);
    }
  • theme.json - a theme configuration file. It’s recommended to define jmix-lumo as the parent theme, so your custom theme includes jmix-lumo styles required for proper work of Jmix UI components and views. For example:

    {
      "parent": "jmix-lumo",
      "lumoImports":["typography","color","spacing","badge","utility"]
    }

The build.gradle file of the theme add-on project may look as follows:

plugins {
    id 'java'
    id 'com.gradle.plugin-publish' version '1.2.1'
}

group = 'com.company'
version = '0.0.1-SNAPSHOT'

repositories {
    mavenCentral()
    maven {
        url 'https://global.repo.jmix.io/repository/public'
    }
}

dependencies {
    implementation 'io.jmix.flowui:jmix-flowui-themes:2.4.0' (1)
}
1 Dependency to jmix-flowui-themes that contains jmix-lumo theme.

After the theme JAR is added as a dependency to the project, the packaged theme can be used as a standalone theme:

@Theme(value = "cobalt")
public class MyProjectApplication implements AppShellConfigurator {
    ...
}

or as a parent for the project’s themes, for example:

theme.json
{
  "parent": "cobalt",
  "lumoImports":["typography","color","spacing","badge","utility"]
}

In this case, the project’s theme will load on top of the base theme, extending it.

custom theme
Figure 3. Example of applied custom theme

Pluggable Styles

When developing a custom add-on, you can create a file with custom styles in the src/main/resources/META-INF/resources/ directory. To add these styles to the resulting application, define the jmix.ui.export-styles property in the module.properties file. The value of this property is a path relative to src/main/resources/META-INF/resources/, for example, jmix.ui.export-styles = addon-styles/my-addon-styles.css:

my-addon-styles.css
.test {
    color: red;
}

Custom styles are added as <style type="text/css"> to the <head> element:

<style type="text/css">
.test {
    color: red;
}
</style>
Use this approach only for a small number of styles that cannot be applied to a particular UI component. For example, for CSS utility classes of views inside an add-on.

Changing Theme Variants at Runtime

To switch between theme variants, e.g. light and dark, the framework provides a static helper class that works in conjunction with the JavaScript file.

The ThemeUtils class provides methods for switching theme variants, which effectively means setting the theme variant in Web Local Storage and invoking JavaScript code to apply it.

For example, you can add a dropdownButton to the main view and switch between theme variants:

main-view.xml
<dropdownButton id="themeSwitcher"
                text="Theme" icon="ADJUST"
                classNames="ms-auto me-m"
                dropdownIndicatorVisible="false">
    <items>
        <actionItem id="systemThemeItem">
            <action id="systemThemeAction"
                    text="System" icon="ADJUST"/>
        </actionItem>
        <actionItem id="lightThemeItem">
            <action id="lightThemeAction"
                    text="Light" icon="SUN_O"/>
        </actionItem>
        <actionItem id="darkThemeItem">
            <action id="darkThemeAction"
                    text="Dark" icon="MOON_O"/>
        </actionItem>
    </items>
</dropdownButton>
MainView.java
@Subscribe("themeSwitcher.systemThemeItem.systemThemeAction")
public void onThemeSwitcherSystemThemeItemSystemThemeAction(final ActionPerformedEvent event) {
    ThemeUtils.applySystemTheme();
}

@Subscribe("themeSwitcher.lightThemeItem.lightThemeAction")
public void onThemeSwitcherLightThemeItemLightThemeAction(final ActionPerformedEvent event) {
    ThemeUtils.applyLightTheme();
}

@Subscribe("themeSwitcher.darkThemeItem.darkThemeAction")
public void onThemeSwitcherDarkThemeItemDarkThemeAction(final ActionPerformedEvent event) {
    ThemeUtils.applyDarkTheme();
}
theme switcher
Figure 4. ThemeSwitcher component

The JavaScript file provides code that applies a theme variant to an HTML document depending on the value in the Web Local Storage and subscribes to the prefers-color-scheme to update the theme variant if the user indicates their preference through an operating system setting (e.g., light or dark mode) or the user agent’s settings.

You need to import it into the main application class:

@JsModule("./src/theme/color-scheme-switching-support.js")
@SpringBootApplication
public class OnboardingApplication implements AppShellConfigurator {
theme switcher