关于 ux-css-layout
面向 VS Code 界面开发与维护者的 CSS 技能,涵盖样式规范、布局组件、响应式尺寸和文本溢出处理。
主要功能
- 01CSS 文件组织与命名
- 02SplitView 和 Grid 布局
- 03可滚动内容模式
- 04基于约束的响应式尺寸
- 05文本截断与悬停查看
- 06设计系统尺寸令牌
适用场景
- 01组织组件样式表
- 02构建可调整大小的窗格与网格
- 03处理列表和标签页中的窄文本
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ microsoft/ vscode --skill ux-css-layout安装后在 Claude Code 中输入/ux-css-layout确认已加载。
详细介绍
此技能涵盖 CSS 文件组织、命名、标准尺寸、程序化布局(SplitView、Grid、可滚动布局)、响应式模式和文本溢出处理。
1. 文件组织
CSS 文件与 TypeScript 组件放在一起:
src/vs/base/browser/ui/button/
button.ts
button.css
src/vs/workbench/contrib/myFeature/browser/
myFeature.ts
media/
myFeature.css
在 TS 文件中直接导入 CSS:
import './media/myFeature.css';
// or for base widgets:
import './button.css';
工作台级全局样式位于 src/vs/workbench/browser/media/。
2. 类命名
- 所有主要组件使用
monaco-前缀:.monaco-workbench、.monaco-split-view2、.monaco-scrollable-element - 修饰类:
.monaco-split-view2.vertical、.monaco-split-view2.horizontal - 状态类:
.visible、.focused、.active、.highlight - 功能专属类使用 kebab-case,不加前缀:
.my-feature、.outline-pane、.welcome-view-content
3. 标准尺寸
| 元素 | 尺寸 |
|---|---|
| 部件标题高度 | 35px |
| 标题内边距(水平) | 8px |
| 标题标签内部内边距 | 12px |
| 操作区域内边距 | 5px |
| 操作图标尺寸 | 16px |
| 正文字号 | 13px(工作台)、11px(HTML 正文) |
| 行高 | 1.4em |
| 验证消息字号 | 12px(行高:17px) |
对于
padding/margin/gap、border-radius、font-size/font-weight、codicon 尺寸和边框宽度,优先使用设计系统的尺寸令牌,而不是原始 px 值——参见 §10 设计系统尺寸令牌。权威参考:.github/instructions/design-tokens.instructions.md(会自动注入到src/vs/**/*.css)。
4. CSS 选择器质量
| 反模式(会被标记) | 正确模式 |
|---|---|
用 ID 选择器设置样式(#my-widget) | 类选择器(.my-widget) |
| 选择器过于具体 | 仅使用所需的最低特异性 |
| 样式放在错误的文件中 | 与组件放在一起 |
Flex 子项缺少 min-width: 0 | 避免截断问题 |
隐藏叠层未设置 pointer-events: none | 避免点击穿透问题 |
切勿新增 !important
不要在新增或修改的 CSS 中引入 !important。如果某条声明在层叠中失效,请检查冲突的选择器,并使用最小且恰当的组件、工作台或状态类前缀提高特异性。现有的 !important 声明可以保留,在无关修改期间不得机械地删除。不要复制或新增这类声明,也不要用它们来逃避理解选择器的归属。
狭义例外是共享焦点/激活轮廓的抑制,其中有意使用 outline: 0 !important 覆盖原生或全局焦点指示器,以避免指针激活时轮廓闪烁。此例外仅限于焦点指示器行为;功能样式仍必须通过选择器特异性解决层叠冲突。
5. SplitView 布局
文件:src/vs/base/browser/ui/splitview/splitview.ts
用于带可拖动分隔条的视图拆分(水平或垂直):
const splitView = new SplitView(container, {
orientation: Orientation.VERTICAL,
proportionalLayout: true,
styles: { separatorBorder: asCssVariable(sashBorder) }
});
// Each view implements IView:
const myView: IView = {
element: myDomNode,
minimumSize: 100,
maximumSize: Number.POSITIVE_INFINITY,
onDidChange: Event.None,
layout(size, offset) { /* resize content */ }
};
splitView.addView(myView, Sizing.Distribute);
使用 LayoutPriority.High / .Low 控制空间受限时优先调整大小的视图。使用 snap: true 允许视图吸附关闭。
6. Grid 布局
文件:src/vs/base/browser/ui/grid/grid.ts
用于二维布局(编辑器组使用):
const grid = new Grid(initialView);
grid.addView(newView, Sizing.Distribute, referenceView, Direction.Right);
7. 可滚动内容
三种适用于不同需求的类:
| 类 | 使用场景 |
|---|---|
SmoothScrollableElement | 动画滚动(SplitView、ListView) |
DomScrollableElement | 包装现有 DOM 内容(悬浮提示、菜单、面包屑) |
ScrollableElement | 基本的单向滚动条 |
const scrollable = new DomScrollableElement(contentNode, {
horizontal: ScrollbarVisibility.Auto,
vertical: ScrollbarVisibility.Auto
});
this._register(scrollable);
container.appendChild(scrollable.getDomNode());
scrollable.scanDomNode(); // call after content changes
8. 响应式布局
VS Code 不使用 CSS 媒体查询,而是采用基于约束的程序化布局系统:
IView.minimumSize/maximumSize— 视图声明其尺寸约束。SplitView和Grid根据约束和LayoutPriority分配空间。ResizeObserver用于基于容器的尺寸调整(例如,编辑器自动布局)。- 窗口被视为固定视口;空间通过基于分隔条的调整来分配。
构建响应式组件时:
- 适当设置
minimumSize/maximumSize。 - 对应优先收起的面板使用
LayoutPriority.Low。 - 对应空间过小时应贴合收起的面板使用
snap: true。 - 约束动态变化时触发
onDidChange。
9. 文本溢出与省略号
所有可能被可调整大小的容器截断的文本标签都必须使用省略号模式。文本被裁切却没有省略号属于视觉缺陷。
标准省略号模式(CSS)
以下三个属性必须同时设置:
.my-label {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
常见的省略号应用位置
| 元素 | 原因 |
|---|---|
Part 标题标签(h2、面包屑) | 侧边栏/面板宽度可能会缩小到标题宽度以下 |
| 视图窗格标题 | 视图容器可能很窄 |
| 列表/树行标签 | 行宽由列表容器固定 |
| 标签页标签(编辑器标签页) | 多个标签页会缩小以适应空间 |
| 欢迎视图中的按钮标签 | 按钮有 max-width 约束 |
| 状态栏项 | 多个项会争夺水平空间 |
| 通知消息文本 | 通知弹窗/中心的宽度固定 |
| 工具提示/悬停标题 | 悬停内容有最大宽度 |
| 下拉/选择项 | 选择框的宽度有限 |
| 徽标文本/描述 | 受限列中的辅助文本 |
Flex 容器注意事项
Flex 子项默认使用 min-width: auto,这会阻止 text-overflow: ellipsis 生效,因为 Flex 项拒绝缩小到其内容宽度以下。可在 Flex 子项上设置 min-width: 0 来解决:
/* WRONG — ellipsis will NOT trigger inside a flex container */
.flex-parent {
display: flex;
}
.flex-parent > .label {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* CORRECT — add min-width: 0 so the flex item can shrink */
.flex-parent > .label {
min-width: 0; /* ← this is the fix */
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
VS Code 中广泛使用此模式 — 例如,.monaco-icon-label-container 设置了 min-width: 0 和 flex: 1,使标签文本可以截断。
固定元素与弹性元素
当一行中同时包含固定尺寸元素(图标、操作按钮)和弹性文本时:
.row {
display: flex;
align-items: center;
}
.row > .icon {
flex-shrink: 0; /* icon never shrinks */
width: 16px;
}
.row > .label {
flex: 1; /* label takes remaining space */
min-width: 0; /* allows shrinking below content width */
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.row > .actions {
flex-shrink: 0; /* action buttons never shrink */
}
这是树行、列表项、标签页标签和视图窗格标题的标准模式。
悬停查看完整文本
文本被省略号截断时,必须能通过悬停工具提示查看完整文本。使用 IHoverService.setupDelayedHover() 并传入未截断的完整文本,方便用户阅读:
this._register(this.hoverService.setupDelayedHover(labelElement, {
content: fullLabelText,
}));
对于 IconLabel 和列表/树渲染器,此功能会自动处理。对于自定义 DOM,必须手动添加。
反模式(切勿这样做)
- 切勿让文本在没有省略号的情况下被裁切 — 如果设置了
overflow: hidden,也必须设置text-overflow: ellipsis。 - 切勿为可能需要本地化的文本依赖固定像素宽度 — 本地化字符串的长度各不相同。
- 切勿单独使用
text-overflow: ellipsis而不设置overflow: hidden和white-space: nowrap— 三者缺一不可。 - 切勿忘记为需要截断内容的 Flex 子项设置
min-width: 0。 - 切勿截断文本却不提供用于查看完整字符串的悬停提示/工具提示。
10. 设计系统尺寸令牌(间距、圆角、字体、codicon、描边)
VS Code 提供了一套设计系统尺寸级别,在 src/vs/platform/theme/common/sizes/baseSizes.ts 中注册,并以 --vscode-* CSS 变量的形式输出。编写或编辑 CSS 时,只要有对应的令牌,就优先使用令牌变量,而不是原始 px 值。完整表格和设计理由见自动注入的 .github/instructions/design-tokens.instructions.md(权威来源——请确保本节与其保持一致)。本节记录了更深入的样式任务所需的决策逻辑。
引用的每个
--vscode-*尺寸变量都必须已存在于build/lib/stylelint/vscode-known-variables.json(按字母顺序排列的"sizes"数组)中,否则 stylelint/代码卫生检查会失败。添加新令牌时,必须同时将其加入baseSizes.ts和该 JSON 文件。
间距 — padding、margin、gap
级别(px):0, 2, 4, 6, 8, 10, 12, 16, 20, 24, 28, 32, 36, 40 → --vscode-spacing-sizeNone、--vscode-spacing-size20 … --vscode-spacing-size400(令牌数字 = px × 10,因此 size200 = 20px)。
重要的是数值,而不是令牌。 是否采用 var() 是可选的——只要原始 px 值落在该级别内,就可以使用。破坏节奏的是不在级别内的数值(3、5、7、14、26px……)。将不在级别内的值调整到最接近的级别值,距离相同时向上取整(5px → 6px、3px → 4px、1px → 2px、26px → 28px)。简写中的每个长度值都会单独检查(0 5px → 0 6px)。保留 auto、%、em/rem、var()/calc() 不变。
圆角 — border-radius
| px | 变量 | 用途 |
|---|---|---|
| 2 | --vscode-cornerRadius-xSmall | 非常紧凑的元素 |
| 4 | --vscode-cornerRadius-small | 控件(按钮、输入框) |
| 6 | --vscode-cornerRadius-medium | 基础/内层表面 |
| 8 | --vscode-cornerRadius-large | 突出/外层表面 |
| 12 | --vscode-cornerRadius-xLarge | 非常突出的表面 |
| 9999 | --vscode-cornerRadius-circle | 完全圆角(胶囊、圆点) |
调整映射:对于不在级别内的字面值(距离相同时向上取整):2→xSmall、3,4→small、5,6→medium、7,8→large、10,11,12→xLarge、14,16,18,20→xLarge、999→circle。
- 胶囊形(圆角半径约等于元素高度的一半——例如
28h/14r、36h/18r、22×22/11r)→--vscode-cornerRadius-circle,而不是 xLarge。距离字面值最近的令牌会使它们变方,失去完全圆角的设计意图。 - 保留不变:
50%、0、0px、inherit,以及任何calc()/var()。保留!important。
字体大小和字重
通用 UI 级别——将大小令牌与字重令牌配对(“Strong” = 匹配的大小令牌 + semiBold,绝不是单独的大小):
| px | 大小变量 | 字重 |
|---|---|---|
| 26 | --vscode-fontSize-heading1 | semiBold |
| 18 | --vscode-fontSize-heading2 | semiBold |
| 13 | --vscode-fontSize-heading3 | semiBold |
| 13 | --vscode-fontSize-body1 | regular |
| 11 | --vscode-fontSize-body2 | regular |
| 12 | --vscode-fontSize-label1 | regular |
| 11 | --vscode-fontSize-label2 | regular |
| 10 | --vscode-fontSize-label3 | regular |
通用字重:--vscode-fontWeight-regular (400)、--vscode-fontWeight-semiBold (600)。
已弃用 — --vscode-bodyFontSize (13) → --vscode-fontSize-body1、--vscode-bodyFontSize-small (12) → --vscode-fontSize-label1、--vscode-bodyFontSize-xSmall (11) → --vscode-fontSize-body2。
旧版 Agents 专用的 --vscode-agents-fontSize-* 和 --vscode-agents-fontWeight-* 令牌也已弃用;请使用对应的通用令牌。
- 没有 medium (500)。
font-weight: 500不在级别内——调整为semiBold。同样,700/bold→ 调整为距离更近的 400 或 600。 - “Strong”不是单独的大小。 “Body 1 Strong” = 匹配的
--vscode-fontSize-*大小令牌 +semiBold。绝不要添加 strong 大小。 normal≡ 400 →regular。保留inherit、lighter、bolder、var()/calc()不变。
Codicon 大小 — 图标 font-size
Codicon 只有 16px 或 12px,绝不会是 14px 或介于两者之间的值。
| px | 变量 | 用途 |
|---|---|---|
| 16 | --vscode-codiconFontSize(基础值) | 默认图标大小 |
| 12 | --vscode-codiconFontSize-compact | 紧凑/行内界面元素 |
**紧凑字形约定:**将图标设为紧凑的 12px 大小时,也要将已注册的字形替换为对应的 *Compact 变体(例如,Codicon.close → Codicon.closeCompact、Codicon.add → Codicon.addCompact)。单独设置 CSS font-size 只会缩放图标,不会切换为经过视觉优化的紧凑字形;要切换字形,必须修改已注册的图标(Action2 icon: / renderIcon)。仅当没有 CSS 选择器针对原始字形类时,才替换字形(例如 .codicon-close);以字形类为目标的选择器(.codicon-add、.codicon-chevron-down)会在类名变为 -compact 时失效,因此也要更新这些选择器(或者通过与字形无关的包装类(如 .monaco-button)设置大小)。某些图标(settings/sliders、agent、vm、info、lock、plus)没有紧凑变体,应将常规字形保持为 12px。
描边 — 边框宽度
只有一种描边粗细:1px → --vscode-strokeThickness。适用于 border: 1px solid <color> 简写和 border-width: 1px。其他宽度没有对应的令牌,请保留原值。
/* prefer */ border: var(--vscode-strokeThickness) solid var(--vscode-widget-border);
/* avoid */ border: 1px solid var(--vscode-widget-border);
关键文件
| 区域 | 文件 |
|---|---|
| SplitView | src/vs/base/browser/ui/splitview/splitview.ts |
| Grid | src/vs/base/browser/ui/grid/grid.ts |
| 滚动条 | src/vs/base/browser/ui/scrollbar/scrollableElement.ts |
| 全局工作台样式 | src/vs/workbench/browser/media/style.css |