关于 vaadin-frontend-design
用于使用 Java 和 CSS 构建具有鲜明风格的 Vaadin 界面。它指导开发者选择主题、设计视觉样式并细化组件,并通过 Vaadin MCP 文档工具查询 API 细节。
主要功能
- 01规划一致的视觉方向
- 02定制 Aura 和 Lumo 主题
- 03调整字体、颜色和间距
- 04通过变体和 CSS 设置组件样式
- 05使用 Vaadin MCP 工具查询文档
适用场景
- 01设计品牌化 Vaadin 仪表盘
- 02完善字体和配色体系
- 03突破默认样式定制 Vaadin 组件
- 04实现时查询 Vaadin API 细节
MCP 相关
它建议使用 `search_vaadin_docs`、`get_component_styling` 和 `get_component_java_api` 等 Vaadin MCP 工具查询文档及组件详情。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ vaadin/ agent-skills --skill vaadin-frontend-design安装后在 Claude Code 中输入/vaadin-frontend-design确认已加载。
附带文件references/design-patterns.md
详细介绍
创建独具特色的 Vaadin 界面
如果不确定某项具体 API 的细节,请使用 Vaadin MCP 工具(search_vaadin_docs、get_component_styling、get_component_java_api)查阅最新文档。始终将 vaadin_version 设为 "25",将 ui_language 设为 "java"。
本技能指导你创建独具特色、精致的 Vaadin 界面,超越默认主题样式。目标是编写达到生产级别的代码,并真正注重美学细节,而非套用通用默认样式。
设计思路
编写代码前,先了解使用场景,并确定明确的美学方向:
- 用途:这个界面解决什么问题?谁会使用它?管理后台与面向客户的门户有不同的设计需求。
- 基调:选择一个方向:简洁专业、温暖亲切、大胆且数据密集、明亮通透、深色聚焦、编辑风、活泼,或精致。设计方向应服务于用户和内容。
- 约束:Vaadin 组件库、Vaadin 主题系统(Aura 或 Lumo)、服务端渲染模型、无障碍要求。
- 差异化:哪些设计让界面显得精心打造,而非套用默认样式?用户会注意到哪些细节?
重要:选择明确的设计方向,并始终如一地贯彻。每种颜色、间距和字体选择都应强化同一种美学风格。协调统一的简洁设计,始终胜过风格不一的繁复设计。
随后编写可运行的代码(Java + CSS),并确保代码:
- 达到生产级别且功能完善
- 视觉风格协调统一,审美方向明确
- 基于 Vaadin 组件库和主题系统构建
- 在间距、色彩、字体和视觉层次上精益求精
Vaadin 主题系统
所有视觉自定义都从你选择的主题开始——Aura 或 Lumo。两者都提供:
- CSS 自定义属性——用于定义颜色、字体、间距、尺寸、边框和阴影的基础。覆盖这些属性即可改变整个应用的外观。
- 组件主题变体——预先定义的视觉样式(例如 primary、tertiary、compact)。使用它们优先于编写自定义 CSS。
- 自定义 CSS——用于主题未涵盖的样式。使用
@StyleSheet配合视图范围的 CSS 文件。
此外,Lumo 还提供:
- 实用类——类似 Tailwind 的类(
LumoUtility.*),用于布局、间距和颜色。可在 Java 中快速应用。Aura 不提供这些类。
始终自上而下地进行调整:先修改主题属性,再编写组件专属 CSS;先使用主题变体,再编写自定义样式。
如需查看完整的 Aura 设计令牌集,请参阅 aura-theme 技能及其 references/property-values.md。如需查看 Lumo 令牌以及两个主题共用的 --vaadin-* 基础属性,请调用 get_theme_css_properties,并将 theme 设为 "lumo" 或 "base"。如需了解主题的选择和加载方式,请使用 search_vaadin_docs。
字体排版
字体排版决定整个界面的基调。
自定义字体系列(两个主题均适用):
/* 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;
}
调整字体比例:
Aura — 调整一个值即可计算整个比例:
html {
--aura-base-font-size: 15; /* unitless number, represents px */
}
Lumo — 覆盖各个令牌:
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 实用类):
// 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
);
对于 Aura,请使用 CSS 类或内联样式,而非 LumoUtility。
关键原则:建立清晰的层次——一种主导标题样式、一种正文样式、一种次要或说明文字样式。结合使用字号、字重和颜色来区分层级。单个视图中应避免使用超过 3–4 种不同的文本样式。
颜色与主题
颜色是赋予 Vaadin 应用鲜明个性的最快方式。
Aura — 自定义强调色与调色板:
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 — 覆盖调色板:
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 注解。对于自定义深色模式颜色,Aura 使用带有 -light/-dark 后缀的属性(参见 aura-theme 技能)。Lumo 使用 CSS light-dark() 函数,例如 --lumo-primary-color: light-dark(blue, lightskyblue),该函数适用于所有配色方案模式;只有在配色方案设为 ColorScheme.Value.DARK 时,[theme~="dark"] 选择器才会匹配。
通过 Java 设置强调色和语义颜色(Lumo 实用工具类):
// Lumo theme only
badge.addClassNames(
LumoUtility.Background.PRIMARY,
LumoUtility.TextColor.PRIMARY_CONTRAST
);
warningCard.addClassNames(
LumoUtility.Background.WARNING_10PCT,
LumoUtility.TextColor.WARNING
);
关键原则:坚持使用协调统一的调色板。选择一种鲜明的主色/强调色,并使用主题的变体系统(Lumo 的不透明度变体、Aura 的计算变体)来满足其他用途。以一种主色为主、谨慎搭配强调色,比平均分布的彩虹色更出色。
间距与密度
一致的间距能营造视觉节奏感和专业感。
Aura — 调整基础尺寸:
html {
--aura-base-size: 18; /* unitless, range 12–24 */
--aura-base-radius: 8; /* unitless, range 0–10 */
}
Lumo — 覆盖单个间距令牌:
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 实用工具类):
// Lumo theme only
card.addClassNames(
LumoUtility.Padding.LARGE,
LumoUtility.Gap.MEDIUM
);
section.addClassNames(
LumoUtility.Padding.Horizontal.LARGE,
LumoUtility.Padding.Vertical.XLARGE
);
紧凑/高密度变体:
// 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);
关键原则:选择一种密度并保持一致。数据密集型仪表板应始终保持紧凑。宽敞的营销风格视图应始终保持疏朗。混用不同密度会显得缺乏规划。
阴影、边框与层级
通过层级营造纵深感和视觉层次。
Aura — 表面层级系统:
.elevated-card {
background: var(--aura-surface-color);
--aura-surface-level: 2;
}
.sunken-area {
background: var(--aura-surface-color);
--aura-surface-level: -1;
}
Lumo — 显式阴影令牌:
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 实用工具类):
// Lumo theme only
section.addClassNames(
LumoUtility.Border.BOTTOM,
LumoUtility.BorderColor.CONTRAST_10
);
关键原则:始终使用层级来表达视觉层次。卡片浮于表面之上,对话框浮于卡片之上。不要给平面元素添加浓重阴影,也不要给悬浮元素添加平淡的阴影。
动效与动画
Vaadin 的服务端模型意味着动画应由 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; }
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);
}
关键原则:一两个精心设计的动画(例如错开显示的页面加载效果),比到处零散的微交互更有影响力。悬停/聚焦时优先使用 CSS 过渡,而非复杂的关键帧动画。持续时间应低于 400ms。
构建精致的组件
样式化卡片(Lumo 实用工具类)
// 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;
}
对于 Aura,请使用基于 Aura 表面系统的 CSS 类,而不是 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;
}
状态徽章
Badge 是 Vaadin 25.1 中的预览功能——使用 badgeComponent 功能标志启用。可用变体:SUCCESS、WARNING、ERROR、FILLED、ICON_ONLY、NUMBER_ONLY。
// 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);
对于早于 25.1 的 Vaadin 版本,可以使用带有 theme="badge" 属性的 Span 元素创建徽章,但此方法已弃用。
数据密集型仪表板布局(Lumo 实用工具类)
// 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);
使用 CSS 设置 Vaadin 组件样式
当主题属性和主题变体无法满足需求时,请使用 CSS。
Shadow DOM 和 ::part() 选择器 — Vaadin 组件使用 Shadow DOM。使用 ::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;
}
组件作用域样式 — 将 CSS 应用于特定视图:
@StyleSheet("styles/views/dashboard-view.css")
@Route("dashboard")
public class DashboardView extends VerticalLayout {
// ...
}
关键原则:始终优先使用主题自定义属性,而不是在 CSS 中硬编码值。使用当前主题的令牌设置颜色、间距和尺寸。这样能使样式与主题保持一致,并让深色模式、密度调整和未来的重新设计都变得轻而易举。
常见反模式
- 硬编码颜色和尺寸 — 始终使用主题的自定义属性。硬编码值会破坏深色模式,也会让主题化变得不可能。
- 覆盖组件内部样式,而不是主题属性 — 如果颜色看起来不对,请覆盖主题颜色令牌,而不是单个组件的 CSS。更改一个属性就应更新整个应用。
- 视觉样式过多 — 选择 2-3 种卡片样式、2-3 个文本层级,以及一种主要操作样式。保持一致才能显得专业。
- 忽略主题变体 — 编写自定义 CSS 前,先检查
ButtonVariant、GridVariant、TextFieldVariant等。你需要的变体很可能已经存在。 - 在服务端渲染的更新中使用繁重动画 — Vaadin 会从服务器重新渲染。每次服务器推送都执行复杂的入场动画会显得不流畅。动画适用于初始页面加载和用户发起的过渡,而不是每次数据更新。
- 视觉层级扁平 — 如果所有内容看起来都一样,就没有任何内容能脱颖而出。使用尺寸、字重、颜色和高度,引导视线关注重要内容。
最佳实践
- 从主题属性着手 — 先自定义主题的核心属性。仅此一项就能彻底改变外观。
- 使用组件主题变体 — 主要、三级、紧凑、徽章主题。使用内置选项。
- 保持一致的配色策略 — 使用一种主色/强调色、一到两种辅助色,以及用于表示状态的语义颜色。
- 利用高度进行设计 — 卡片、对话框和菜单应体现层次感。保持主题的高度系统一致。
- 适度添加动效 — 悬停时使用 CSS 过渡、页面加载时淡入、卡片依次显现。时长控制在 400ms 以内。
- 在深色模式下测试 — 如果自定义了任何颜色,请验证浅色和深色主题。使用主题属性即可自动处理这一点。
详细参考
有关组件 ::part() 选择器、CSS 动画方案和调色板方案,请参阅 references/design-patterns.md。有关完整的主题令牌表,请参阅 aura-theme 技能的 references/property-values.md(Aura),或调用 get_theme_css_properties(Aura、Lumo 或 base)。有关组件的主题变体,请调用 get_component_styling。