Best practice for switchable Vaadin 12 themes

643 views Asked by At

I am currently in the process of migrating a Vaadin 8 application over to Vaadin 12. The look and feel should be used by the user and changed on Login or via a button press.

In Our Vaadin 8 application we had 2 themes (a dark and a light one), each with their own SASS/CSS and some shared properties. The user was able to switch it using the setTheme() Method. When a click to the switching button happened, the Look and Feel just changed. In Vaadin 12 the Theming follows a different approach and I am struggling to find a good way to implement this feature in Vaadin 12.

Let's say we don't want to create a whole new Theme and just want to use customized LUMO. I can set the Theme/Variant through the @Theme Annotation. The Downside: The Theme will be fixed at Runtime.

Also i could just write some code to apply variants to my application and components. (like in the dynamic styling chapter: https://vaadin.com/docs/flow/element-api/tutorial-dynamic-styling.html ) The Downside: It won't be very practicable to iterate through each element and apply the variant.

My question now:

What is the best way to achieve a switch between to themes at runtime? (customized light- and dark variants of Lumo or any other theme).

Would I just create 2 HTML Files (for compatibility) containing CSS and then somehow override the currently used File through a dynamic import?

I hope my question is clear and someone can point me to the right direction.

2

There are 2 answers

1
cfrick On BEST ANSWER

If you are only interested in toggling between light and dark, then you can just add/remove dark at a very high place in the DOM. E.g. the element of the UI is usually the body or at least very high up.

E.g.:

new Checkbox("Use Dark Theme").tap{
    addValueChangeListener{ cb ->
        getUI().ifPresent(){ ui ->
            def themeList = ui.getElement().getThemeList()
            if (cb.value) {
                themeList.add(Lumo.DARK)
            } else {
                themeList.remove(Lumo.DARK)
            }
        }
    }
}

edit

As asked in the comments of another answer:

To change the colors in a theme, you can override the used colors. This is the example how to change the text color for light and dark Lumo theme:

html {
    --lumo-body-text-color: red;
}
[theme~="dark"] {
    --lumo-body-text-color: yellow;
}

3
Leif Åstrand On

It's relatively easy to switch between two different variants of the same theme, e.g. the dark and light variants of Lumo. To do this, you only need to toggle the corresponding theme attribute on the <html> element. There's no direct access to that element from the server, but you can do it with a small snippet of JavaScript: ui.getPage().executeJavaScript("document.documentElement.setAttribute($0, $1)", "theme", "dark");

Depending on circumstances, you can or must apply the changes to the <body> element instead. In that case, you can either switch out .documentElement for .body in the JS snippet or directly use ui.getElement().setAttribute("theme", "dark") in Java.

Switching between two different base themes, e.g. Lumo vs Material is a much more complicated affair. For each component, there can only be one base theme loaded in the browser at the same time, and reloading the page is the only way of getting rid of the one that is already loaded. For each component that is used for Flow, the framework takes care of loading the right theme import in addition to the base import that doesn't have any styling. To make things even more complicated, the theme designated using @Theme is automatically included in the application's production bundle. To be able to use multiple base themes, you'd also have to somehow produce multiple different bundles and also somehow configure Flow to use the right bundle depending on circumstances.