用途
该技能用于指导 Claude 在 Vaadin 25 中构建响应式布局,使 UI 能够适配不同的屏幕尺寸。在用户要求“make a layout…”等涉及响应式布局的场景时使用。
当对具体 API 细节不确定时,使用 Vaadin MCP 工具(search_vaadin_docs、get_component_java_api)查阅最新文档,并始终将 vaadin_version 设为 "25"、ui_language 设为 "java"。
设计理念
Vaadin 中的响应式意味着让 UI 最好地利用可用屏幕空间,而不只是压缩或拉伸元素。目标是针对每种视口尺寸呈现恰当的信息量和交互方式。
按投入递增,有三种策略:
- 在小屏幕上减少功能——隐藏次要筛选器、折叠面板。适合移动端只是偶尔访问方式的场景。
- 构建响应式 UI——功能相同,布局不同。使用 CSS media/container queries 与响应式组件。这是多数应用推荐的方案。
- 构建独立 UI——移动端与桌面端体验各自独立。只有当交互模式存在根本差异(例如基于滑动的移动端 vs 数据网格桌面端)时才值得投入。
除非有充分理由,应从策略 2 开始。
内置响应式组件
在编写自定义响应式逻辑之前,优先利用这些会自动处理适配的组件:
- AppLayout——在小视口中抽屉自动折叠为汉堡菜单
- Dashboard——网格布局自动随屏幕尺寸调整
- FormLayout——根据宽度调整列数与标签位置
- MenuBar——当项目放不下时显示溢出菜单
- Tabs——标签溢出时出现水平滚动按钮
- Context Menu——移动端停靠在屏幕底部
- Dialog / Confirm Dialog——窄视口下按钮工具栏切换为垂直排列
- CRUD——小视口下编辑器切换为覆盖层
- Date Picker / Select——移动端覆盖层停靠到底部
CSS 媒体查询
基于视口进行响应式处理的主要工具。定义在特定视口宽度下激活的样式。
/* In your view's CSS file */
.filter-panel {
display: flex;
}
@media (max-width: 640px) {
.filter-panel {
display: none;
}
}
从 Java 应用 CSS 类名,让 CSS 处理响应式逻辑。这样能把响应式行为留在它应在的 CSS 中,而不是在服务端尝试检测屏幕尺寸。
CSS 容器查询
当响应式应基于组件所属容器的宽度而非视口时使用。适用于可调整大小的面板、出现在不同上下文中的可复用组件以及仪表盘小部件。
.sidepanel {
container-type: inline-size;
container-name: sidepanel;
}
.sidepanel .footer {
display: none;
}
@container sidepanel (min-width: 400px) {
.footer {
display: flex;
}
}
容器查询让组件自成一体——它们适应自身可用空间,而不是假定某个特定视口尺寸。
响应式设计的工具类(仅 Lumo)
Vaadin 的 Lumo 工具类提供类似 Tailwind CSS 的移动优先响应式系统,是无需编写自定义 CSS 即可添加响应式行为的最快方式。
注意:这些工具类仅适用于 Lumo 主题。如果使用 Aura,请改用 CSS 媒体查询或容器查询。
在 Vaadin 25 中的设置(必需):
@StyleSheet(Lumo.STYLESHEET)
@StyleSheet(Lumo.UTILITY_STYLESHEET)
@StyleSheet("styles.css")
public class Application implements AppShellConfigurator {
}
注意:Vaadin 25 不再支持通过 theme.json 加载 Lumo Utility Classes,请改用 @StyleSheet 导入。
断点(移动优先):
| Breakpoint | Min width | Java constant prefix |
|---|---|---|
| (default) | 0px | LumoUtility.Display.FLEX etc. |
| Small | 640px | Display.Breakpoint.Small.* |
| Medium | 768px | Display.Breakpoint.Medium.* |
| Large | 1024px | Display.Breakpoint.Large.* |
| XLarge | 1280px | Display.Breakpoint.XLarge.* |
| XXLarge | 1536px | Display.Breakpoint.XXLarge.* |
示例——仅在小屏幕上显示移动端工具栏:
mobileToolbar.addClassNames(
LumoUtility.Display.FLEX, // visible by default (mobile)
LumoUtility.Display.Breakpoint.Small.HIDDEN // hidden at 640px+
);
示例——在断点处从垂直布局切换为水平布局:
container.addClassNames(
LumoUtility.Display.FLEX,
LumoUtility.FlexDirection.COLUMN, // stack vertically (mobile)
LumoUtility.FlexDirection.Breakpoint.Medium.ROW // row at 768px+
);
工具类遵循移动优先模式:将移动端样式定义为默认,然后在更大的断点处覆盖。这与 CSS 约定一致,代码更清晰。
响应式模式
模式:可折叠筛选面板(Lumo 工具类)
桌面端显示筛选侧栏,移动端将其隐藏在切换按钮之后。
// Lumo theme only — uses LumoUtility classes
// Filter panel — hidden on mobile, shown on desktop
VerticalLayout filterPanel = new VerticalLayout();
filterPanel.addClassNames(
LumoUtility.Display.HIDDEN, // hidden by default
LumoUtility.Display.Breakpoint.Medium.FLEX // shown at 768px+
);
// Toggle button — shown on mobile, hidden on desktop
Button filterToggle = new Button("Filters");
filterToggle.addClassNames(
LumoUtility.Display.INLINE_FLEX, // shown by default
LumoUtility.Display.Breakpoint.Medium.HIDDEN // hidden at 768px+
);
模式:响应式卡片网格
使用 CSS Grid 实现可调整列数的卡片布局。
.card-grid {
display: grid;
gap: 1rem; /* or use theme token: var(--lumo-space-m) for Lumo */
grid-template-columns: 1fr; /* 1 column on mobile */
}
@media (min-width: 640px) {
.card-grid {
grid-template-columns: repeat(2, 1fr); /* 2 columns */
}
}
@media (min-width: 1024px) {
.card-grid {
grid-template-columns: repeat(3, 1fr); /* 3 columns */
}
}
Div cardGrid = new Div();
cardGrid.addClassName("card-grid");
// Add Card components to cardGrid
模式:桌面侧栏 → 移动端底部面板
使用带 drawer placement 的 AppLayout。在小视口中,AppLayout 会自动将抽屉转换为覆盖层。如需完全自定义,可结合媒体查询将导航移至底部栏。
最佳实践
- 使用 CSS 实现响应式,而非服务端检测——避免用 Page.retrieveExtendedClientDetails() 或 UI.getCurrent().getPage().addBrowserWindowResizeListener() 做布局决策。CSS media/container queries 性能更好,且不需要服务端往返。
- 移动优先——将移动端布局定义为默认,再为更大屏幕增加复杂度。这与 Lumo 工具类断点的工作方式一致。
- 利用内置响应式组件——AppLayout、FormLayout、Dashboard、MenuBar 已处理适配,不要重复造轮子。
- 可复用组件优先使用容器查询——如果组件可能出现在不同宽度的容器中,容器查询能让它自适应。
- 面向流式尺寸设计——不要针对特定设备分辨率。用户会调整浏览器大小、使用分屏和缩放。应在多种宽度下测试,而不只是“手机”和“桌面”。
- 测试换行行为——对 HorizontalLayout 使用 setWrap(true) 或在 CSS 中使用 flex-wrap 时,验证项目在中间尺寸下也能优雅换行,而不只是目标断点处。
详细参考
关于 Lumo 工具类断点与响应式常量的完整列表,参见 references/responsive-patterns.md。