mcpField
V
vaadin-frontend-design

vaadin-frontend-design

github.com/vaadin设计与 UI7112 次安装1 天前更新
代码仓库

关于 vaadin-frontend-design

用于使用 Java 和 CSS 构建具有鲜明风格的 Vaadin 界面。它指导开发者选择主题、设计视觉样式并细化组件,并通过 Vaadin MCP 文档工具查询 API 细节。

主要功能

  1. 01规划一致的视觉方向
  2. 02定制 Aura 和 Lumo 主题
  3. 03调整字体、颜色和间距
  4. 04通过变体和 CSS 设置组件样式
  5. 05使用 Vaadin MCP 工具查询文档

适用场景

  1. 01设计品牌化 Vaadin 仪表盘
  2. 02完善字体和配色体系
  3. 03突破默认样式定制 Vaadin 组件
  4. 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。两者都提供:

  1. CSS 自定义属性——用于定义颜色、字体、间距、尺寸、边框和阴影的基础。覆盖这些属性即可改变整个应用的外观。
  2. 组件主题变体——预先定义的视觉样式(例如 primary、tertiary、compact)。使用它们优先于编写自定义 CSS。
  3. 自定义 CSS——用于主题未涵盖的样式。使用 @StyleSheet 配合视图范围的 CSS 文件。

此外,Lumo 还提供:

  1. 实用类——类似 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 中硬编码值。使用当前主题的令牌设置颜色、间距和尺寸。这样能使样式与主题保持一致,并让深色模式、密度调整和未来的重新设计都变得轻而易举。

常见反模式

  1. 硬编码颜色和尺寸 — 始终使用主题的自定义属性。硬编码值会破坏深色模式,也会让主题化变得不可能。
  2. 覆盖组件内部样式,而不是主题属性 — 如果颜色看起来不对,请覆盖主题颜色令牌,而不是单个组件的 CSS。更改一个属性就应更新整个应用。
  3. 视觉样式过多 — 选择 2-3 种卡片样式、2-3 个文本层级,以及一种主要操作样式。保持一致才能显得专业。
  4. 忽略主题变体 — 编写自定义 CSS 前,先检查 ButtonVariant、GridVariant、TextFieldVariant 等。你需要的变体很可能已经存在。
  5. 在服务端渲染的更新中使用繁重动画 — Vaadin 会从服务器重新渲染。每次服务器推送都执行复杂的入场动画会显得不流畅。动画适用于初始页面加载和用户发起的过渡,而不是每次数据更新。
  6. 视觉层级扁平 — 如果所有内容看起来都一样,就没有任何内容能脱颖而出。使用尺寸、字重、颜色和高度,引导视线关注重要内容。

最佳实践

  1. 从主题属性着手 — 先自定义主题的核心属性。仅此一项就能彻底改变外观。
  2. 使用组件主题变体 — 主要、三级、紧凑、徽章主题。使用内置选项。
  3. 保持一致的配色策略 — 使用一种主色/强调色、一到两种辅助色,以及用于表示状态的语义颜色。
  4. 利用高度进行设计 — 卡片、对话框和菜单应体现层次感。保持主题的高度系统一致。
  5. 适度添加动效 — 悬停时使用 CSS 过渡、页面加载时淡入、卡片依次显现。时长控制在 400ms 以内。
  6. 在深色模式下测试 — 如果自定义了任何颜色,请验证浅色和深色主题。使用主题属性即可自动处理这一点。

详细参考

有关组件 ::part() 选择器、CSS 动画方案和调色板方案,请参阅 references/design-patterns.md。有关完整的主题令牌表,请参阅 aura-theme 技能的 references/property-values.md(Aura),或调用 get_theme_css_properties(Aura、Lumo 或 base)。有关组件的主题变体,请调用 get_component_styling。

评论

支持 Markdown