App Annotations
Target: TYPE
The primary annotation. Registers a class as a UI endpoint and mounts it at the given route path. Every Mateu application needs at least one @UI-annotated class.
public @interface UI { String value(); // required: route path e.g. "/orders" String indexHtmlPath() default "/static/_index.html"; String frontendComponentPath() default "/assets/mateu.js";}| Attribute | Type | Default | Description |
|---|---|---|---|
value | String | — | URL path at which this UI is mounted |
indexHtmlPath | String | "/static/_index.html" | Path to the HTML shell served for this UI |
frontendComponentPath | String | "/assets/mateu.js" | Path to the compiled frontend component |
@UI("/home")@Title("My first Mateu app")public class Home {
@NotEmpty String name;
@Button public Message greet() { return new Message("Hello " + name); }}Marks a class as the application shell and selects its navigation layout variant.
public @interface App { AppVariant value() default AppVariant.AUTO; AppLayout layout() default AppLayout.SINGLE_SLOT; boolean themeToggle() default false; String accentColor() default ""; String accentStrip() default ""; int accentStripSeed() default 7;}| Attribute | Type | Default | Description |
|---|---|---|---|
value | AppVariant | AUTO | Navigation layout variant for the shell |
layout | AppLayout | SINGLE_SLOT | Content area layout (SINGLE_SLOT or SPLIT) |
themeToggle | boolean | false | Shows a moon/sun icon button in the header to switch dark/light mode (ignored by the Redwood renderer, which is light only) |
accentColor | String | "" | The app’s brand accent (a CSS colour). Not the primary colour: see below |
accentStrip | String | "" | The accent strip’s image (a URL, repeated along it); blank = a strip Mateu draws from the accent; "none" = a plain band in the accent colour (Vaadin renderer) |
accentStripSeed | int | 7 | The seed of the generated strip: another seed, another drawing in the same palette |
backLink | BackLink | BREADCRUMBS | PARENT draws a single «← Parent» link (labelled with the title of the nearest screen above the app’s route) and drops the breadcrumb trail inside the app — the console style for a record master |
An @App with no menu of its own that answers a routes.yaml route with children offers
those children as its options — the tabs of a record master. See the
recipe.
AppVariant values:
| Value | Description |
|---|---|
HAMBURGER_MENU | Collapsible hamburger-style navigation drawer. (HAMBURGUER_MENU, the old misspelling, still works but is deprecated.) |
MENU_ON_LEFT | Persistent navigation panel on the left side |
MENU_ON_TOP | Navigation bar along the top |
TABS | Tab-based navigation |
TILES | Flat top-level nav; items with children open a tiles hub in the content area |
RAIL | Compact icon rail on the left side |
AUTO | Framework picks the variant from the menu shape (see below) |
MEDIATOR | Used for nested sub-applications acting as a mediator |
With AUTO (the default), the variant is inferred from the menu: a menu that federates a RemoteMenu → MENU_ON_TOP; if any top-level item has grandchildren (menu depth > 2) → TILES; more than 7 top-level items → HAMBURGER_MENU; otherwise MENU_ON_TOP. An app with no submenus at all renders as TABS, however many items it has. See Navigation & Menus for the full behaviour of each variant.
AppLayout values:
| Value | Description |
|---|---|
SINGLE_SLOT | Standard single-content-area layout (default) |
SPLIT | Content area split into two panes |
@UI("/app")@App(AppVariant.HAMBURGER_MENU)public class ShellApp { ... }Dark / light mode toggle
Section titled “Dark / light mode toggle”Set themeToggle = true to show a moon/sun icon button in the application header. Clicking it switches between dark and light mode and persists the choice in localStorage. On first load the stored preference is applied; if no preference is stored the OS prefers-color-scheme media query is used as the default.
The Redwood renderer is light only: it ignores themeToggle (no switch in its header) and always paints the light theme, whatever the OS prefers or a stored choice says. The same app class served by the Vaadin renderer keeps its switch.
@UI("/app")@App(value = AppVariant.AUTO, themeToggle = true)public class MyApp { ... }Brand accent
Section titled “Brand accent”accentColor gives the shell the app’s brand colour, for example @App(accentColor = "#D2232A"). It is kept separate from the theme’s primary colour on purpose. Primary means “you can click this”, and the accent never does. The Vaadin shell uses it in these places:
- the accent strip, drawn where Redwood draws its colour strip rather than fixed under the header: under a page’s header, on top of a listing’s results and at the foot of the welcome hero. By default Mateu draws it from the accent, a Redwood-style band in a palette derived from the colour (
accentStripSeedpicks the drawing).accentStripcan name the app’s own image instead (for example@App(accentColor = "#464c68", accentStrip = "/images/strip.svg")), repeated along the strip, or say"none"for a plain band in the accent colour. See Accent colour and strip. The Redwood renderer keeps its own Spectra strips; - the welcome hero’s background, a deep tone of the accent;
- the console name in the menu band, in the light theme only. In dark mode the name stays in the header text colour, because a brand red loses contrast on the dark base.
With no accent there is no strip. The values reach the page as custom properties on mateu-app (--mateu-accent, --mateu-page-band-h, --mateu-page-band-image; the hero also reads --mateu-hero-background), which an app’s own CSS can also set.
@PageTitle
Section titled “@PageTitle”Target: TYPE
Sets the browser tab title (<title> element in the HTML).
public @interface PageTitle { String value();}@UI("/orders")@PageTitle("Orders – My App")public class OrdersPage { ... }@Title
Section titled “@Title”Target: TYPE
Sets the visible heading rendered inside the page, above the content area.
public @interface Title { String value();}@UI("/home")@Title("My first Mateu app")public class Home { ... }
@Titlecontrols the heading inside the page UI.@PageTitlecontrols the browser tab. Both can be combined.
@Subtitle
Section titled “@Subtitle”Target: TYPE
Sets a subtitle displayed below the page title.
public @interface Subtitle { String value();}@UI("/products")@Title("Product Catalog")@Subtitle("Browse and manage all products")public class ProductCatalog { ... }Target: TYPE
Sets the URL of the image displayed as the application logo in the header or navigation drawer.
public @interface Logo { String value(); // URL or path to the logo image}@UI("/_workflow")@Logo("/images/logo.svg")public class WorkflowHome { ... }@FavIcon
Section titled “@FavIcon”Target: TYPE
Sets the browser favicon for the UI.
public @interface FavIcon { String value(); // URL or path to the favicon}@UI("/_workflow")@FavIcon("/images/favicon.ico")public class WorkflowHome { ... }@DrawerClosed
Section titled “@DrawerClosed”Target: TYPE
No attributes. Starts the navigation drawer in the closed (collapsed) state when the page is first loaded.
@UI("/app")@App(AppVariant.HAMBURGER_MENU)@DrawerClosedpublic class ShellApp implements App { ... }@KeycloakSecured (deprecated)
Section titled “@KeycloakSecured (deprecated)”Target: TYPE
Integrates Keycloak authentication. The frontend will redirect unauthenticated users to the Keycloak login page before the UI is displayed.
public @interface KeycloakSecured { String url(); String realm(); String clientId(); String jsUrl() default "";}| Attribute | Type | Default | Description |
|---|---|---|---|
url | String | — | Keycloak server base URL |
realm | String | — | Keycloak realm name |
clientId | String | — | Keycloak client ID |
jsUrl | String | "" | Optional override for the Keycloak JS adapter URL |
@UI("/_workflow")@KeycloakSecured( url = "https://auth.example.com", realm = "myrealm", clientId = "my-app")public class WorkflowHome { ... }@UISpec
Section titled “@UISpec”Target: TYPE
Points to a YAML file that describes the UI declaratively instead of (or alongside) Java annotations.
public @interface UISpec { String value(); // path to a YAML UI spec file}@UI("/dashboard")@UISpec("/specs/dashboard.yaml")public class Dashboard { ... }Target: TYPE
Enables an AI assistant sidebar for the UI by wiring it to a Server-Sent Events (SSE) endpoint that streams AI responses.
public @interface AI { String sse(); // SSE endpoint for AI assistant streaming}| Attribute | Type | Description |
|---|---|---|
sse | String | Path to the SSE endpoint that provides AI responses |
@UI("/support")@AI(sse = "/api/ai/stream")public class SupportPage { ... }Combined example
Section titled “Combined example”@UI("/home")@Title("My first Mateu app")@Style(StyleConstants.CONTAINER)public class Home {
@NotEmpty String name;
@Button public Message greet() { return new Message("Hello " + name); }}A full application shell combining several annotations:
@UI("/_workflow")@FavIcon("/images/favicon.svg")@PageTitle("Workflow Engine")@Logo("/images/logo.svg")@Title("Workflow Engine")public class WorkflowHome {
@Menu WorkflowMenu workflow;}