Erik Lumme.dev

Dynamic theming in Vaadin Flow

Posted 15 Jun 2021 by Erik Lumme

Whether for multi-tenancy or user preference, the need to dynamically change the theming of an application is a common business application need. In this post, we show how it’s done using CSS custom properties and HTML attributes.

Vaadin 14.6 brought an improved theming mechanism which makes it easy to package and reuse themes. While switching between packaged themes at runtime is not supported, a single theme can support multiple looks and feels with just a few lines of CSS. This approach also works well without the new theming mechanism.

The application used in this project was generated through start.vaadin.com. It consists of a menu, a header, and a master-detail view for editing personal information.

The application that we are styling
The application that we are styling

The distinguishing factor

To limit the scope of our style rules to a specific theme variant, we can set a class or attribute on the HTML body element. A good candidate is the theme attribute, which is also used for setting the Lumo theme variants.

Once the attribute is set, global style rules are easily limited to one or more theme variants.

By using the ~= operator, the selector will match even when multiple theme variants are defined. For example, [theme~="dark"] will match <body theme="plain dark">. If this is not needed, just using = is slightly more performant.

In Java, we can dynamically set the attribute at any time through the UI class. Depending on your use case, you might fetch the theme variant from the session, the database, or even the URL.

UI.getCurrent().getElement().setAttribute("theme", "my-theme-variant");

For convenience, I have created an enum to describe the available variants. The caption can be used to display the theme variant name to a user.

A utility class comes in handy to change the theme at runtime. By also storing it in the session, it is persisted across tabs and page reloads.

To set the initial value of the theme attribute when the page is first loaded, we can utilize an IndexHtmlResponseListener.

Creating a theme variant selector

A theme variant selector can be created in many ways. The code below shows one approach, using the Tabs component. Using the Vaadin ComponentUtil utility class, we associate a tab with a ThemeVariant.

Efficient styling with CSS custom properties

Vaadin’s Lumo theme uses CSS custom properties to define properties that most styles are derived from. This allows us to make major changes to the look and feel of our application by only modifying some of these properties.

It is a good idea to define your own CSS custom properties for colors and other values that you will reuse.

The application being styled uses Lumo’s dark theme variant on the navbar element, so an additional attribute selector is needed to override those styles.

As so many styles are derived from these properties, these few changes have a big effect on the look of the application.

The application with the Carrot Inc theme variant selected
The application with the Carrot Inc theme variant selected

We can also add normal style rules under the theme attribute scope. The CSS custom properties can easily be set just for a specific component, so as not to affect other elements on the page.

Theme-attribute-based styles inside a shadow root is a trickier topic. While the host-context pseudo-class allows for styling a shadow root based on the attribute of a parent element, the class has not been, and will not be, implemented in Safari.

So, the remaining approach is to define the style as a CSS custom property. The default value can be defined under the :root pseudo-class, which refers to the html element, and the value can be overridden for a theme variant as desired.

Here I define the default value for the date picker overlay background, and I override it for my clean theme variant.

I then use my property in my component-specific style sheet.

The application with the clean theme variant selected
The application with the clean theme variant selected

If the lack of support for Internet Explorer 11 is not a concern, elements inside a shadow root that have the part attribute set can also be styled from the global style sheet using the ::part pseudo-element. This does not work in nested shadow roots.

Listening to theme variant changes

Some theme-specific styles may need to be applied programmatically. One example is changing out an image, which can’t be done in CSS except by using the background-image property.

We can use the ComponentUtil#addListener method to add listeners. For this, we need a custom event class.

The Registration returned from addThemeChangeListener can be used to remove the listener. Use ComponentUtil#fireEvent to notify the listeners when the theme variant has changed.

We can use this in the MainView.java class to set a different logo for the Carrot Inc theme variant. We set the initial value based on the initial theme variant, and update the value whenever it changes.

A different logo for the Carrot Inc theme variant
A different logo for the Carrot Inc theme variant

That’s it, the application now supports multiple theme variants!

The complete code can be found on GitHub.