Creating a Custom Theme
A custom theme is the easiest way to provide a custom look and feel for your entire application. It can be packaged as a dependency for reuse in multiple applications, as described in Packaging a Theme for Reuse. The CSS in a custom theme is always applied on top of the default Lumo theme.
Theme Folder Structure
For use in a single application, a custom theme is implemented as a folder inside frontend/themes
, with the following minimal structure:
frontend
└── themes (1)
└── my-theme (2)
├── components/ (3)
└── styles.css (4)
-
The
themes
folder can contain multiple custom themes (but only one can be applied to the application at a time). -
Each theme is in its own sub-folder. The name of this folder is provided as a parameter to the
@Theme
annotation to apply the theme to the application. -
The
components
sub-folder is for component style sheets that target the (local CSS) internals of Vaadin components. -
styles.css
is the theme’s master style sheet that’s automatically loaded when the theme is applied.
See Style Scopes for details on global vs local CSS.
Applying a Custom Theme
You can apply a different theme using the @Theme
annotation on a class that implements AppShellConfigurator
.
An application may only have one such class and you need to define any similar configuration annotations in the same class, such as @PWA
or PageTitle
.
For example, in a Spring Boot application, you could have an application configuration class such as the following:
@Theme("my-theme")
public class Application extends SpringBootServletInitializer
implements AppShellConfigurator {
...
}
You can find more information on the usage of the annotation in the @Theme
annotation guide.
Changes to the theme folder’s contents are automatically picked up during development. If the application has live reload enabled, the UI should reload automatically. Otherwise a page reload may be needed to see the changes.
Sometimes, compilation throws an error ("no such file or directory") when files or folders are removed from the theme folder while the application is running.
This might happen if the file being removed is a style sheet referenced from my-theme/styles.css
, or from other style sheet in the theme folder.
In that case, to avoid re-compilation errors, remove the style sheet import first, and then remove the file.
Note
|
Dismissing the error message
When you encounter a “no such file or directory” error reported by Vite, shown in an overlay in your application, click on the overlay (or refresh the browser page), and it should disappear.
You can then continue working on your application and theming.
If it doesn’t help, you need to restart the application.
|
Master Style Sheet
When a custom theme is applied to the application, the master style sheet styles.css
is loaded automatically as global CSS.
Other style sheets, except for Vaadin component styles in the components folder, need to be included through the master style sheet using CSS @import
statements.
The master style sheet typically contains:
-
Imports of other global style sheets within the theme folder.
-
Overrides of default Lumo properties and declarations of custom CSS properties.
-
Styles applied to UI elements through class names and other CSS selectors.
@import 'other-styles.css'; 1
html, :host { 2
--lumo-border-radius-m: 0.5em;
--my-brand-color: purple;
}
.application-header { 3
background: white;
border-bottom: 1px solid gray;
}
CSS custom properties (for example, for overriding Lumo defaults) are recommended to use the selector html, :host
, as in the example above, to ensure that they are applied when the theme is applied to an embedded Flow application or component, or when used with the Design System Publisher tool.
Caution
|
Restriction on style sheet imports
At the moment all @import statements need to be in the theme root folder.
See Vaadin Flow issue 9794.
|
Vaadin Component Styles
As the internal styling of Vaadin components is isolated from the global CSS inside the component’s shadow DOM, the easiest way to customize their styles is through the CSS properties available in the built-in themes (see Foundation section for details), and by injecting custom CSS directly into the shadow DOM of the components by placing them in the components sub-folder.
To inject CSS into the shadow DOM of a Vaadin component, create a style sheet whose name matches the web component HTML element name of the component in the components sub-folder.
As an example, to apply styling to the vaadin-button
component, create a style sheet called vaadin-button.css
.
frontend
└── themes
└── my-theme
├── components
│ ├── vaadin-button.css
│ └── vaadin-text-field.css
└── styles.css
See Styling Components for details on writing CSS for Vaadin components.
This style injection is based on a feature used by Vaadin components called Themable Mixin. You can create your own web components that use the same feature to make them support this theming mechanism.
Components that don’t use shadow DOM, such as custom Flow-based components, can be styled using regular global CSS placed in styles.css
or any other style sheet imported through it.
Other Theme Assets
In addition to style sheets, themes often need other assets like fonts, images, and icons. These can be included in the theme folder, either in the root or in sub-folders as desired.
In the following example, a couple of images are included in an img
sub-folder (1), and a font file is included in the theme root (2).
frontend
└── themes
└── my-theme
├── components/
├── img (1)
│ ├── logo.png
│ └── background.jpg
├── my-font.woff (2)
└── styles.css
These assets can be used in the theme’s style sheets through URIs relative to the style sheet’s location:
@font-face {
font-family: "My Font";
src: url('./my-font.woff') format("woff");
}
.application-logo {
background-image: url('./img/logo.png');
}
Document Root Style Sheet
When Flow UIs are embedded into other web pages, some styles need to be in the root of the parent page instead of the Shadow DOM wrapper in the embedded UI.
You can load the styles into the parent page by placing them in a stylesheet called document.css
.
This is necessary for the following two:
-
@font-face
declarations as they are not supported in Shadow DOM; -
Global CSS that has to work in Vaadin component overlays such as Dialogs;
To avoid copy-pasting styles into two places, move them to a separate style sheet and use @import
to include them in both styles.css
and document.css
.
Style Loading Order
When using a custom theme, CSS is loaded in a Vaadin application in the following order:
-
Lumo
styles -
Application-specific bundled CSS added with a
@CssImport
-
Styles added on Java side via
Page::addStylesheet()
-
Application-specific unbundled CSS added with
@StyleSheet
-
Parent styles
-
Custom theme styles
Theme Resolving Order
The following logic is used to determine which theme is used:
-
If the
@Theme
annotation is found on the application shell class, the theme set in the annotation is used -
If the
@NoTheme
annotation is found on the application shell class, no theme style sheets are loaded -
If the
com.vaadin.flow.theme.lumo.Lumo
class is available in the classpath, the Lumo theme is used
Resolving stops when a match is found. No theme is used if none of the conditions are met.
Limitations
The following limitations apply to custom themes:
-
The theme can’t be switched run-time.
-
Using the built-in Material theme isn’t currently supported. Custom themes are always loaded on top of the Lumo theme.
-
At the moment all @import statements need to be in style sheets in the theme root folder.
F13DEB48-4F04-4897-874E-F827F42B1CE0