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 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.
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
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
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.
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.
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
We can use the
ComponentUtil#addListener method to add listeners. For this, we need a custom event class.
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.
That’s it, the application now supports multiple theme variants!
The complete code can be found on GitHub.