vaadin-frontend-design

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Creating Distinctive Vaadin Interfaces

创建独特的Vaadin界面

Use the Vaadin MCP tools (
search_vaadin_docs
,
get_component_styling
,
get_component_java_api
) to look up the latest documentation whenever uncertain about a specific API detail. Always set
vaadin_version
to
"25"
and
ui_language
to
"java"
.
This skill guides creation of distinctive, polished Vaadin interfaces that go beyond default theme styling. The goal is production-grade code with genuine attention to aesthetic detail — not generic defaults.
当不确定特定API细节时,请使用Vaadin MCP工具(
search_vaadin_docs
get_component_styling
get_component_java_api
)查阅最新文档。请始终将
vaadin_version
设置为
"25"
ui_language
设置为
"java"
本技能指导创建独特、精致的Vaadin界面,突破默认主题样式的限制。目标是产出符合生产标准的代码,真正注重美学细节——而非通用默认样式。

Design Thinking

设计思路

Before writing code, understand the context and commit to a clear aesthetic direction:
  • Purpose: What problem does this interface solve? Who uses it? An admin dashboard has different design needs than a customer-facing portal.
  • Tone: Pick a direction: clean and professional, warm and approachable, bold and data-dense, light and airy, dark and focused, editorial, playful, or refined. The direction should serve the users and the content.
  • Constraints: Vaadin component library, Vaadin theme system (Aura or Lumo), server-side rendering model, accessibility requirements.
  • Differentiation: What makes this interface feel crafted rather than default? What detail will users notice?
CRITICAL: Choose a clear direction and execute it consistently. Every color, spacing, and typography choice should reinforce the same aesthetic. A cohesive simple design always beats an inconsistent elaborate one.
Then implement working code (Java + CSS) that is:
  • Production-grade and functional
  • Visually cohesive with a clear aesthetic point of view
  • Built on Vaadin's component library and theme system
  • Refined in spacing, color, typography, and visual hierarchy
编写代码前,先理解上下文并确定清晰的美学方向:
  • 用途:该界面解决什么问题?面向哪些用户?管理后台与客户门户的设计需求截然不同。
  • 风格基调:选择一个方向:简洁专业、温暖亲和、醒目密集、轻盈通透、深色聚焦、编辑风格、活泼趣味或精致典雅。方向需服务于用户和内容。
  • 约束条件:Vaadin组件库、Vaadin主题系统(Aura或Lumo)、服务端渲染模型、无障碍要求。
  • 差异化:是什么让这个界面显得精心设计而非默认样式?用户会注意到哪些细节?
关键原则:选择清晰的方向并一致执行。每一个颜色、间距和排版选择都应强化同一美学风格。协调统一的简约设计永远优于杂乱无章的复杂设计。
随后实现可运行的代码(Java + CSS),需满足:
  • 符合生产标准且功能完整
  • 视觉协调,具有明确的美学定位
  • 基于Vaadin组件库和主题系统构建
  • 在间距、颜色、排版和视觉层次上经过优化

The Vaadin Theme System

Vaadin主题系统

All visual customization starts with your chosen theme — Aura or Lumo. Both provide:
  1. CSS custom properties — the foundation for colors, typography, spacing, sizing, borders, shadows. Override these to change the entire application's look.
  2. Component theme variants — pre-built visual variations (e.g., primary, tertiary, compact). Use these before writing custom CSS.
  3. Custom CSS — for anything the theme doesn't cover. Use
    @StyleSheet
    with view-scoped CSS files.
Lumo additionally provides:
  1. Utility classes — Tailwind-like classes (
    LumoUtility.*
    ) for layout, spacing, colors. Fast to apply from Java. Not available with Aura.
Always work top-down: change a theme property before writing component-specific CSS, and use a theme variant before writing custom styles.
For full details on theme selection, loading, and all design tokens, see the theming skill.
所有视觉定制都始于你选择的主题——AuraLumo。两者均提供:
  1. CSS自定义属性——颜色、排版、间距、尺寸、边框、阴影的基础。覆盖这些属性即可改变整个应用的外观。
  2. 组件主题变体——预构建的视觉变体(如primary、tertiary、compact)。在编写自定义CSS前优先使用这些变体。
  3. 自定义CSS——用于主题未覆盖的场景。使用
    @StyleSheet
    搭配视图作用域的CSS文件。
Lumo额外提供:
  1. 工具类——类似Tailwind的类(
    LumoUtility.*
    ),用于布局、间距、颜色。可从Java快速应用。Aura不支持此功能。
始终遵循自上而下的原则:先修改主题属性,再编写组件特定CSS;先使用主题变体,再编写自定义样式。
有关主题选择、加载及所有设计令牌的详细信息,请查看主题定制技能。

Typography

排版

Typography sets the tone of the entire interface.
Customizing the font family (works with both themes):
css
/* 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;
}
Tuning the type scale:
Aura — adjust one value and the entire scale is computed:
css
html {
    --aura-base-font-size: 15;  /* unitless number, represents px */
}
Lumo — override individual tokens:
css
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;
}
Creating typographic hierarchy from Java (Lumo utility classes):
java
// 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
);
For Aura, use CSS classes or inline styles instead of
LumoUtility
.
Key principle: Establish a clear hierarchy — one dominant heading style, one body style, one secondary/caption style. Use font size, weight, and color together to differentiate levels. Avoid using more than 3-4 distinct text styles in a single view.
排版奠定整个界面的基调。
自定义字体族(适用于两种主题):
css
/* 在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;
}
调整排版比例:
Aura——调整一个值即可计算整个比例:
css
html {
    --aura-base-font-size: 15;  /* 无单位数字,代表px */
}
Lumo——覆盖单个令牌:
css
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;
}
从Java创建排版层次(Lumo工具类):
java
// 仅Lumo主题——需要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
);
对于Aura,请使用CSS类或内联样式替代
LumoUtility
关键原则:建立清晰的层次——一种主导标题样式、一种正文样式、一种次要/说明样式。结合字体大小、字重和颜色区分层级。单个视图中避免使用超过3-4种不同的文本样式。

Color and Theming

颜色与主题定制

Color is the fastest way to give a Vaadin app a distinctive identity.
Aura — customizing the accent and palette:
css
html {
    --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%);
}
Lumo — overriding the color palette:
css
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%);
}
Dark mode:
Both themes support the
@ColorScheme
annotation. For custom dark mode colors, see the theming skill for theme-specific selectors (
[theme~="dark"]
for Lumo,
-light
/
-dark
suffixed properties for Aura).
Accent and semantic colors from Java (Lumo utility classes):
java
// Lumo theme only
badge.addClassNames(
    LumoUtility.Background.PRIMARY,
    LumoUtility.TextColor.PRIMARY_CONTRAST
);

warningCard.addClassNames(
    LumoUtility.Background.WARNING_10PCT,
    LumoUtility.TextColor.WARNING
);
Key principle: Commit to a cohesive palette. Pick one strong primary/accent color and use the theme's variant system (Lumo's opacity variants, Aura's computed variants) for secondary uses. A dominant primary with restrained accent colors outperforms an evenly distributed rainbow.
颜色是快速赋予Vaadin应用独特标识的方式。
Aura——自定义强调色和调色板:
css
html {
    --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%);
}
Lumo——覆盖调色板:
css
html {
    /* 主色——用于按钮、链接、聚焦环 */
    --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;

    /* 表面色——背景、卡片、对话框 */
    --lumo-base-color: hsl(220, 20%, 98%);

    /* 错误、成功、警告 */
    --lumo-error-color: hsl(0, 75%, 55%);
    --lumo-success-color: hsl(150, 60%, 40%);
    --lumo-warning-color: hsl(40, 95%, 50%);
}
深色模式:
两种主题均支持
@ColorScheme
注解。如需自定义深色模式颜色,请查看主题定制技能中的主题特定选择器(Lumo使用
[theme~="dark"]
,Aura使用后缀为
-light
/
-dark
的属性)。
从Java应用强调色和语义色(Lumo工具类):
java
// 仅Lumo主题
badge.addClassNames(
    LumoUtility.Background.PRIMARY,
    LumoUtility.TextColor.PRIMARY_CONTRAST
);

warningCard.addClassNames(
    LumoUtility.Background.WARNING_10PCT,
    LumoUtility.TextColor.WARNING
);
关键原则:坚持协调统一的调色板。选择一种鲜明的主色/强调色,并使用主题的变体系统(Lumo的透明度变体、Aura的计算变体)用于次要场景。主色突出且强调色克制的效果,远胜于颜色均匀分布的“彩虹”样式。

Spacing and Density

间距与密度

Consistent spacing creates visual rhythm and professionalism.
Aura — adjust the base size:
css
html {
    --aura-base-size: 18;     /* unitless, range 12–24 */
    --aura-base-radius: 8;    /* unitless, range 0–10 */
}
Lumo — override individual spacing tokens:
css
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;
}
Applying spacing from Java (Lumo utility classes):
java
// Lumo theme only
card.addClassNames(
    LumoUtility.Padding.LARGE,
    LumoUtility.Gap.MEDIUM
);

section.addClassNames(
    LumoUtility.Padding.Horizontal.LARGE,
    LumoUtility.Padding.Vertical.XLARGE
);
Compact/dense variants:
java
// Cross-theme variants — work in both Aura and Lumo
grid.addThemeVariants(GridVariant.NO_BORDER);
textField.addThemeVariants(TextFieldVariant.SMALL);
button.addThemeVariants(ButtonVariant.SMALL);

// Lumo-only: compact grid
grid.addThemeVariants(GridVariant.LUMO_COMPACT);
Key principle: Pick a density and stick with it. Data-dense dashboards should be consistently compact. Spacious marketing-style views should be consistently airy. Mixing densities looks unintentional.
一致的间距营造视觉韵律和专业感。
Aura——调整基础尺寸:
css
html {
    --aura-base-size: 18;     /* 无单位,范围12–24 */
    --aura-base-radius: 8;    /* 无单位,范围0–10 */
}
Lumo——覆盖单个间距令牌:
css
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;
}
从Java应用间距(Lumo工具类):
java
// 仅Lumo主题
card.addClassNames(
    LumoUtility.Padding.LARGE,
    LumoUtility.Gap.MEDIUM
);

section.addClassNames(
    LumoUtility.Padding.Horizontal.LARGE,
    LumoUtility.Padding.Vertical.XLARGE
);
紧凑/高密度变体:
java
// 跨主题变体——适用于Aura和Lumo
grid.addThemeVariants(GridVariant.NO_BORDER);
textField.addThemeVariants(TextFieldVariant.SMALL);
button.addThemeVariants(ButtonVariant.SMALL);

// 仅Lumo:紧凑网格
grid.addThemeVariants(GridVariant.LUMO_COMPACT);
关键原则:选择一种密度并保持一致。数据密集型仪表板应始终紧凑。宽敞的营销类视图应始终通透。混合密度会显得杂乱无章。

Shadows, Borders, and Elevation

阴影、边框与层级

Create depth and visual hierarchy through elevation.
Aura — surface level system:
css
.elevated-card {
    background: var(--aura-surface-color);
    --aura-surface-level: 2;
}

.sunken-area {
    background: var(--aura-surface-color);
    --aura-surface-level: -1;
}
Lumo — explicit shadow tokens:
css
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);
}
java
// Lumo theme only
card.addClassNames(
    LumoUtility.BoxShadow.SMALL,
    LumoUtility.BorderRadius.MEDIUM
);
Border radius customization:
css
/* 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 */
}
Subtle borders for separation (Lumo utility classes):
java
// Lumo theme only
section.addClassNames(
    LumoUtility.Border.BOTTOM,
    LumoUtility.BorderColor.CONTRAST_10
);
Key principle: Use elevation consistently to communicate hierarchy. Cards float above the surface, dialogs float above cards. Don't put heavy shadows on flat elements or flat shadows on floating elements.
通过层级营造深度和视觉层次。
Aura——表面层级系统:
css
.elevated-card {
    background: var(--aura-surface-color);
    --aura-surface-level: 2;
}

.sunken-area {
    background: var(--aura-surface-color);
    --aura-surface-level: -1;
}
Lumo——显式阴影令牌:
css
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);
}
java
// 仅Lumo主题
card.addClassNames(
    LumoUtility.BoxShadow.SMALL,
    LumoUtility.BorderRadius.MEDIUM
);
边框圆角定制:
css
/* Lumo */
html {
    --lumo-border-radius-s: 4px;
    --lumo-border-radius-m: 8px;
    --lumo-border-radius-l: 16px;
}

/* Aura——改为调整基础圆角 */
html {
    --aura-base-radius: 8;  /* 计算所有圆角值 */
}
用于分隔的细微边框(Lumo工具类):
java
// 仅Lumo主题
section.addClassNames(
    LumoUtility.Border.BOTTOM,
    LumoUtility.BorderColor.CONTRAST_10
);
关键原则:一致使用层级传达视觉层次。卡片悬浮于表面之上,对话框悬浮于卡片之上。不要在扁平元素上使用厚重阴影,也不要在悬浮元素上使用扁平阴影。

Motion and Animation

动效与动画

Vaadin's server-side model means animations should be CSS-driven. Keep them subtle and purposeful.
View transitions with CSS:
css
/* 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; }
java
content.addClassName("fade-in");

// Staggered card reveal
for (Component card : cards) {
    card.addClassName("stagger-item");
}
Hover and interaction effects:
css
.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);
}
Key principle: One or two well-crafted animations (like a staggered page-load reveal) create more impact than scattered micro-interactions everywhere. Prefer CSS transitions on hover/focus over complex keyframe animations. Keep durations under 400ms.
Vaadin的服务端模型意味着动画应基于CSS实现。保持动画微妙且有目的性。
基于CSS的视图过渡:
css
/* 视图内容淡入 */
.fade-in {
    animation: fadeIn 0.3s ease-out;
}

@keyframes fadeIn {
    from {
        opacity: 0;
        transform: translateY(8px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* 列表项 staggered 显示 */
.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; }
java
content.addClassName("fade-in");

// Staggered卡片显示
for (Component card : cards) {
    card.addClassName("stagger-item");
}
悬停与交互效果:
css
.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);  /* 或使用主题令牌 */
    transform: translateY(-2px);
}
关键原则:一两个精心设计的动画(如页面加载时的staggered显示)比零散的微交互更有冲击力。优先选择悬停/聚焦时的CSS过渡,而非复杂的关键帧动画。持续时间控制在400ms以内。

Building Polished Components

构建精致组件

Styled cards (Lumo utility classes)

样式化卡片(Lumo工具类)

java
// 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;
}
For Aura, use CSS classes with Aura's surface system instead of
LumoUtility
:
css
.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;
}
java
// 仅Lumo主题——使用LumoUtility类
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;
}
对于Aura,请使用CSS类结合Aura的表面系统替代
LumoUtility
css
.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;
}

Status badges

状态徽章

Badge is a preview feature in Vaadin 25.1 — enable it with the
badgeComponent
feature flag. Available variants:
SUCCESS
,
WARNING
,
ERROR
,
FILLED
,
ICON_ONLY
,
NUMBER_ONLY
.
java
// Badge component (Vaadin 25.1+ preview — requires badgeComponent feature flag)
Badge pending = new Badge("Pending");

Badge confirmed = new Badge("Confirmed");
confirmed.addThemeVariants(BadgeVariant.SUCCESS);

Badge warning = new Badge("Warning");
warning.addThemeVariants(BadgeVariant.WARNING);

Badge denied = new Badge("Denied");
denied.addThemeVariants(BadgeVariant.ERROR);

// With icon
Badge iconBadge = new Badge("Confirmed", VaadinIcon.CHECK.create());
iconBadge.addThemeVariants(BadgeVariant.SUCCESS);

// With number
Badge counter = new Badge("Inbox", 12);
counter.addThemeVariants(BadgeVariant.FILLED);
For Vaadin versions before 25.1, badges can be created with
Span
elements using the
theme="badge"
attribute, but this approach is deprecated.
Badge是Vaadin 25.1中的预览功能——需通过
badgeComponent
功能标志启用。可用变体:
SUCCESS
WARNING
ERROR
FILLED
ICON_ONLY
NUMBER_ONLY
java
// Badge组件(Vaadin 25.1+预览版——需badgeComponent功能标志)
Badge pending = new Badge("Pending");

Badge confirmed = new Badge("Confirmed");
confirmed.addThemeVariants(BadgeVariant.SUCCESS);

Badge warning = new Badge("Warning");
warning.addThemeVariants(BadgeVariant.WARNING);

Badge denied = new Badge("Denied");
denied.addThemeVariants(BadgeVariant.ERROR);

// 带图标
Badge iconBadge = new Badge("Confirmed", VaadinIcon.CHECK.create());
iconBadge.addThemeVariants(BadgeVariant.SUCCESS);

// 带数字
Badge counter = new Badge("Inbox", 12);
counter.addThemeVariants(BadgeVariant.FILLED);
对于Vaadin 25.1之前的版本,可使用
Span
元素并添加
theme="badge"
属性创建徽章,但此方法已被弃用。

Data-dense dashboard layout (Lumo utility classes)

数据密集型仪表板布局(Lumo工具类)

java
// 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 grid — uses CSS Grid for responsive layout
Div metrics = new Div(
    createCard("Revenue", "$48,200", "+12% from last month"),
    createCard("Users", "1,420", "+5% from last month"),
    createCard("Orders", "384", "+8% from last month")
);
metrics.addClassName("metrics-grid");
// Companion CSS:
// .metrics-grid {
//     display: grid;
//     grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
//     gap: var(--lumo-space-m);
//     width: 100%;
// }

dashboard.add(createSectionHeader("Overview"), metrics);
java
// 仅Lumo主题——使用LumoUtility类
VerticalLayout dashboard = new VerticalLayout();
dashboard.setPadding(true);
dashboard.setSpacing(false);
dashboard.addClassNames(
    LumoUtility.Gap.LARGE,
    LumoUtility.Background.CONTRAST_5
);

// 指标卡片网格——使用CSS Grid实现响应式布局
Div metrics = new Div(
    createCard("Revenue", "$48,200", "+12% from last month"),
    createCard("Users", "1,420", "+5% from last month"),
    createCard("Orders", "384", "+8% from last month")
);
metrics.addClassName("metrics-grid");
// 配套CSS:
// .metrics-grid {
//     display: grid;
//     grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
//     gap: var(--lumo-space-m);
//     width: 100%;
// }

dashboard.add(createSectionHeader("Overview"), metrics);

Styling Vaadin Components with CSS

使用CSS样式化Vaadin组件

When theme properties and theme variants aren't enough, use CSS.
Shadow DOM and
::part()
selectors
— Vaadin components use shadow DOM. Style internal parts with
::part()
:
css
/* 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;
}
Component-scoped styles — apply CSS to a specific view:
java
@StyleSheet("styles/views/dashboard-view.css")
@Route("dashboard")
public class DashboardView extends VerticalLayout {
    // ...
}
Key principle: Always prefer theme custom properties over hardcoded values in CSS. Use your active theme's tokens for colors, spacing, and sizing. This keeps styles consistent with the theme and makes dark mode, density changes, and future redesigns trivial.
当主题属性和主题变体不足以满足需求时,使用CSS。
Shadow DOM与
::part()
选择器
——Vaadin组件使用Shadow DOM。使用
::part()
样式化内部元素:
css
/* 样式化Grid表头单元格 */
vaadin-grid::part(header-cell) {
    background-color: var(--lumo-contrast-5pct);  /* Lumo */
    /* 或Aura使用: --aura-surface-level: 0; */
    font-weight: 600;
}

/* 样式化Dialog遮罩层 */
vaadin-dialog-overlay::part(overlay) {
    border-radius: 16px;
}

/* 样式化TextField输入框 */
vaadin-text-field::part(input-field) {
    border-radius: 8px;
}
组件作用域样式——将CSS应用于特定视图:
java
@StyleSheet("styles/views/dashboard-view.css")
@Route("dashboard")
public class DashboardView extends VerticalLayout {
    // ...
}
关键原则:始终优先使用主题自定义属性,而非CSS中的硬编码值。使用当前主题的令牌设置颜色、间距和尺寸。这能保持样式与主题一致,使深色模式、密度调整和未来重设计变得简单。

Common Anti-Patterns

常见反模式

  1. Hardcoded colors and sizes — always use your theme's custom properties. Hardcoded values break dark mode and make theming impossible.
  2. Overriding component internals instead of theme properties — if a color looks wrong, override the theme color token, not the individual component's CSS. One property change should update the entire app.
  3. Too many visual styles — pick 2-3 card styles, 2-3 text hierarchies, one primary action style. Consistency creates professionalism.
  4. Ignoring theme variants — check
    ButtonVariant
    ,
    GridVariant
    ,
    TextFieldVariant
    , etc. before writing custom CSS. The variant you need probably exists.
  5. Heavy animations on server-rendered updates — Vaadin rerenders from the server. Complex entrance animations on every server push look janky. Use animations for initial page loads and user-initiated transitions, not for every data update.
  6. Flat visual hierarchy — if everything looks the same, nothing stands out. Use size, weight, color, and elevation to guide the eye to what matters.
  1. 硬编码颜色和尺寸——始终使用主题的自定义属性。硬编码值会破坏深色模式,使主题定制无法实现。
  2. 覆盖组件内部样式而非主题属性——如果颜色显示异常,覆盖主题颜色令牌,而非单个组件的CSS。一次属性更改应能更新整个应用。
  3. 过多视觉样式——选择2-3种卡片样式、2-3种文本层次、一种主要操作样式。一致性营造专业感。
  4. 忽略主题变体——在编写自定义CSS前,先查看
    ButtonVariant
    GridVariant
    TextFieldVariant
    等。你需要的变体可能已经存在。
  5. 服务端渲染更新时使用复杂动画——Vaadin从服务端重新渲染。每次服务端推送时使用复杂的入场动画会显得卡顿。仅在初始页面加载和用户触发的过渡中使用动画,而非每次数据更新。
  6. 扁平视觉层次——如果所有元素看起来都一样,就没有突出的重点。使用尺寸、字重、颜色和层级引导用户关注重要内容。

Best Practices

最佳实践

  1. Start with theme properties — customize your theme's core properties first. This alone can transform the look.
  2. Use component theme variants — primary, tertiary, compact, badge themes. Use what's built in.
  3. Maintain a consistent color strategy — one primary/accent, one or two additional colors, semantic colors for status.
  4. Design with elevation — cards, dialogs, and menus should feel layered. Use your theme's elevation system consistently.
  5. Add motion sparingly — CSS transitions on hover, a page-load fade-in, staggered card reveals. Keep it under 400ms.
  6. Test in dark mode — if you customize any colors, verify both light and dark themes. Using theme properties makes this automatic.
  1. 从主题属性开始——先自定义主题的核心属性。仅这一步就能彻底改变外观。
  2. 使用组件主题变体——primary、tertiary、compact、badge等主题。利用内置功能。
  3. 保持一致的颜色策略——一种主色/强调色、一两种辅助色、用于状态的语义色。
  4. 结合层级设计——卡片、对话框和菜单应呈现分层效果。一致使用主题的层级系统。
  5. 谨慎添加动效——悬停时的CSS过渡、页面加载淡入、staggered卡片显示。持续时间控制在400ms以内。
  6. 在深色模式下测试——如果自定义了任何颜色,请验证浅色和深色主题。使用主题属性可自动适配。

Detailed Reference

详细参考

For component
::part()
selectors, CSS animation recipes, and color palette recipes, see
references/design-patterns.md
. For complete theme token tables and variant comparisons, see the theming skill's
references/theming-patterns.md
.
有关组件
::part()
选择器、CSS动画方案和调色板方案,请查看
references/design-patterns.md
。完整的主题令牌表和变体对比,请查看主题定制技能的
references/theming-patterns.md