Loading...
Loading...
Guide Claude on creating visually distinctive, polished Vaadin 25 interfaces that go beyond default theme styling. This skill should be used when the user asks to "make it look good", "improve the design", "style the view", "make it visually appealing", "add polish", "design a UI", "create a beautiful interface", or when building a new view where visual quality matters. Also trigger when the user wants to add animations, visual effects, or build polished component compositions in a Vaadin application.
npx skill4agent add vaadin/claude-plugin frontend-designsearch_vaadin_docsget_component_stylingget_component_java_apivaadin_version"25"ui_language"java"@StyleSheetLumoUtility.*/* In styles.css */
@font-face {
font-family: 'Your Font';
src: url('./fonts/your-font.woff2') format('woff2');
font-display: swap;
}
html {
/* Aura */
--aura-font-family: 'Your Font', sans-serif;
/* Lumo */
--lumo-font-family: 'Your Font', sans-serif;
}html {
--aura-base-font-size: 15; /* unitless number, represents px */
}html {
--lumo-font-size-xxxl: 2.5rem;
--lumo-font-size-xxl: 1.75rem;
--lumo-font-size-xl: 1.375rem;
--lumo-font-size-l: 1.125rem;
--lumo-font-size-m: 1rem;
--lumo-font-size-s: 0.875rem;
--lumo-font-size-xs: 0.8125rem;
--lumo-font-size-xxs: 0.75rem;
}// Lumo theme only — requires Lumo.UTILITY_STYLESHEET
H2 title = new H2("Dashboard");
title.addClassNames(
LumoUtility.FontSize.XXLARGE,
LumoUtility.FontWeight.BOLD,
LumoUtility.TextColor.HEADER
);
Span subtitle = new Span("Weekly performance overview");
subtitle.addClassNames(
LumoUtility.FontSize.MEDIUM,
LumoUtility.TextColor.SECONDARY
);LumoUtilityhtml {
--aura-accent-color-light: hsl(220, 80%, 50%);
--aura-accent-color-dark: hsl(220, 85%, 65%);
--aura-background-color-light: hsl(220, 20%, 98%);
--aura-background-color-dark: hsl(220, 20%, 12%);
--aura-blue: hsl(220, 80%, 50%);
--aura-green: hsl(150, 60%, 40%);
--aura-red: hsl(0, 75%, 55%);
}html {
/* Primary color — used for buttons, links, focus rings */
--lumo-primary-color: hsl(220, 80%, 50%);
--lumo-primary-color-50pct: hsla(220, 80%, 50%, 0.5);
--lumo-primary-color-10pct: hsla(220, 80%, 50%, 0.1);
--lumo-primary-text-color: hsl(220, 80%, 45%);
--lumo-primary-contrast-color: #fff;
/* Surface colors — backgrounds, cards, dialogs */
--lumo-base-color: hsl(220, 20%, 98%);
/* Error, success, warning */
--lumo-error-color: hsl(0, 75%, 55%);
--lumo-success-color: hsl(150, 60%, 40%);
--lumo-warning-color: hsl(40, 95%, 50%);
}@ColorScheme[theme~="dark"]-light-dark// Lumo theme only
badge.addClassNames(
LumoUtility.Background.PRIMARY,
LumoUtility.TextColor.PRIMARY_CONTRAST
);
warningCard.addClassNames(
LumoUtility.Background.WARNING_10PCT,
LumoUtility.TextColor.WARNING
);html {
--aura-base-size: 18; /* unitless, range 12–24 */
--aura-base-radius: 8; /* unitless, range 0–10 */
}html {
--lumo-space-xs: 0.25rem;
--lumo-space-s: 0.5rem;
--lumo-space-m: 1rem;
--lumo-space-l: 1.5rem;
--lumo-space-xl: 2.5rem;
}// Lumo theme only
card.addClassNames(
LumoUtility.Padding.LARGE,
LumoUtility.Gap.MEDIUM
);
section.addClassNames(
LumoUtility.Padding.Horizontal.LARGE,
LumoUtility.Padding.Vertical.XLARGE
);// Lumo theme — compact variants
grid.addThemeVariants(GridVariant.LUMO_COMPACT, GridVariant.LUMO_NO_BORDER);
textField.addThemeVariants(TextFieldVariant.LUMO_SMALL);
button.addThemeVariants(ButtonVariant.LUMO_SMALL);
// Aura theme — compact variants
grid.addThemeVariants(GridVariant.AURA_COMPACT, GridVariant.AURA_NO_BORDER);
// Note: Aura does not have SMALL variants for TextField/Button.elevated-card {
background: var(--aura-surface-color);
--aura-surface-level: 2;
}
.sunken-area {
background: var(--aura-surface-color);
--aura-surface-level: -1;
}html {
--lumo-box-shadow-xs: 0 1px 2px 0 rgba(0,0,0,0.05);
--lumo-box-shadow-s: 0 2px 4px -1px rgba(0,0,0,0.1);
--lumo-box-shadow-m: 0 4px 8px -2px rgba(0,0,0,0.1);
--lumo-box-shadow-l: 0 8px 16px -4px rgba(0,0,0,0.15);
--lumo-box-shadow-xl: 0 16px 32px -8px rgba(0,0,0,0.2);
}// Lumo theme only
card.addClassNames(
LumoUtility.BoxShadow.SMALL,
LumoUtility.BorderRadius.MEDIUM
);/* Lumo */
html {
--lumo-border-radius-s: 4px;
--lumo-border-radius-m: 8px;
--lumo-border-radius-l: 16px;
}
/* Aura — adjust the base radius instead */
html {
--aura-base-radius: 8; /* computes all radius values */
}// Lumo theme only
section.addClassNames(
LumoUtility.Border.BOTTOM,
LumoUtility.BorderColor.CONTRAST_10
);/* Fade-in for view content */
.fade-in {
animation: fadeIn 0.3s ease-out;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(8px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Staggered reveal for list items */
.stagger-item {
animation: fadeIn 0.3s ease-out backwards;
}
.stagger-item:nth-child(1) { animation-delay: 0.05s; }
.stagger-item:nth-child(2) { animation-delay: 0.1s; }
.stagger-item:nth-child(3) { animation-delay: 0.15s; }
.stagger-item:nth-child(4) { animation-delay: 0.2s; }
.stagger-item:nth-child(5) { animation-delay: 0.25s; }content.addClassName("fade-in");
// Staggered card reveal
for (Component card : cards) {
card.addClassName("stagger-item");
}.interactive-card {
transition: box-shadow 0.2s ease, transform 0.2s ease;
}
.interactive-card:hover {
box-shadow: 0 4px 8px -2px rgba(0,0,0,0.1); /* or use theme token */
transform: translateY(-2px);
}// Lumo theme only — uses LumoUtility classes
public static Div createCard(String title, String value, String description) {
Div card = new Div();
card.addClassNames(
LumoUtility.Background.BASE,
LumoUtility.BorderRadius.MEDIUM,
LumoUtility.BoxShadow.SMALL,
LumoUtility.Padding.LARGE,
LumoUtility.Display.FLEX,
LumoUtility.FlexDirection.COLUMN,
LumoUtility.Gap.SMALL
);
Span titleSpan = new Span(title);
titleSpan.addClassNames(
LumoUtility.FontSize.SMALL,
LumoUtility.TextColor.SECONDARY,
LumoUtility.FontWeight.MEDIUM
);
Span valueSpan = new Span(value);
valueSpan.addClassNames(
LumoUtility.FontSize.XXLARGE,
LumoUtility.FontWeight.BOLD,
LumoUtility.TextColor.HEADER
);
Span descSpan = new Span(description);
descSpan.addClassNames(
LumoUtility.FontSize.SMALL,
LumoUtility.TextColor.TERTIARY
);
card.add(titleSpan, valueSpan, descSpan);
return card;
}LumoUtility.metric-card {
background: var(--aura-surface-color);
--aura-surface-level: 2;
border-radius: var(--vaadin-border-radius-m);
padding: var(--vaadin-padding);
display: flex;
flex-direction: column;
gap: 0.5rem;
}public static Span createBadge(String text, BadgeVariant variant) {
Span badge = new Span(text);
badge.getElement().getThemeList().add("badge " + variant.theme);
return badge;
}
public enum BadgeVariant {
DEFAULT(""),
SUCCESS("success"),
ERROR("error"),
WARNING("warning"),
CONTRAST("contrast"),
PRIMARY("primary"),
PILL("pill"),
SMALL("small");
final String theme;
BadgeVariant(String theme) { this.theme = theme; }
}"badge success small pill"// Lumo theme only — uses LumoUtility classes
VerticalLayout dashboard = new VerticalLayout();
dashboard.setPadding(true);
dashboard.setSpacing(false);
dashboard.addClassNames(
LumoUtility.Gap.LARGE,
LumoUtility.Background.CONTRAST_5
);
// Metric cards row
HorizontalLayout metrics = new HorizontalLayout();
metrics.setWidthFull();
metrics.addClassNames(LumoUtility.Gap.MEDIUM);
metrics.add(
createCard("Revenue", "$48,200", "+12% from last month"),
createCard("Users", "1,420", "+5% from last month"),
createCard("Orders", "384", "+8% from last month")
);
metrics.getChildren().forEach(c -> ((HasSize) c).setWidthFull());
dashboard.add(createSectionHeader("Overview"), metrics);::part()::part()/* Style Grid header cells */
vaadin-grid::part(header-cell) {
background-color: var(--lumo-contrast-5pct); /* Lumo */
/* or for Aura: --aura-surface-level: 0; */
font-weight: 600;
}
/* Style Dialog overlay */
vaadin-dialog-overlay::part(overlay) {
border-radius: 16px;
}
/* Style TextField input */
vaadin-text-field::part(input-field) {
border-radius: 8px;
}@StyleSheet("styles/views/dashboard-view.css")
@Route("dashboard")
public class DashboardView extends VerticalLayout {
// ...
}ButtonVariantGridVariantTextFieldVariant::part()references/design-patterns.mdreferences/theming-patterns.md