ux-css-layout について
VS Code の UI を実装・保守する開発者向けの 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 - 機能固有のクラスには、プレフィックスを付けずにケバブケースを使用します。.my-feature、
.outline-pane、.welcome-view-content
3. 標準サイズ
| 要素 | サイズ |
|---|---|
| パーツタイトルの高さ | 35px |
| タイトルのパディング(水平) | 8px |
| タイトルラベルの内側のパディング | 12px |
| アクション領域のパディング | 5px |
| アクションアイコンのサイズ | 16px |
| 本文のフォントサイズ | 13px(ワークベンチ)、11px(HTML 本文) |
| 行の高さ | 1.4em |
| 検証メッセージのフォントサイズ | 12px(line-height: 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
2D レイアウト(エディターグループで使用)の場合:
const grid = new Grid(initialView);
grid.addView(newView, Sizing.Distribute, referenceView, Direction.Right);
7. スクロール可能なコンテンツ
用途に応じて、次の 3 つのクラスを使い分けます。
| クラス | 使用する場面 |
|---|---|
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)
次の3つのプロパティの組み合わせが必須です。3つすべてを指定する必要があります。
.my-label {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
省略記号が必要な一般的な箇所
| 要素 | 理由 |
|---|---|
パートのタイトルラベル(h2、パンくずリスト) | サイドバーやパネルは、タイトルより狭くなるようにサイズ変更できます |
| ビューペインのヘッダータイトル | ビューコンテナーは狭くなることがあります |
| リストやツリーの行ラベル | 行の幅はリストコンテナーによって固定されます |
| タブラベル(エディタータブ) | 多数のタブは、収まるように幅が縮小されます |
| ウェルカムビューのボタンラベル | ボタンには max-width の制約があります |
| ステータスバーの項目 | 多数の項目が水平方向の領域を奪い合います |
| 通知メッセージのテキスト | 通知トーストや通知センターの幅は固定されています |
| ツールチップやホバーの見出し | ホバーには最大幅があります |
| ドロップダウンや選択項目 | 選択ボックスの幅には上限があります |
| バッジのテキストや説明 | 制約のある列内の補助テキストです |
Flexコンテナーで注意すべき点
Flexの子要素は、デフォルトで min-width: auto になります。そのため、Flexアイテムがコンテンツ幅より小さくなるのを拒み、text-overflow: ellipsis が機能しなくなります。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も必ず設定してください。 - 絶対に、ローカライズされる可能性のあるテキストに固定ピクセル幅を使用しないでください。ローカライズ後の文字列は長さが異なります。
- 絶対に、
overflow: hiddenとwhite-space: nowrapを指定せずにtext-overflow: ellipsisを使用しないでください。3つすべてが必須です。 - 絶対に、切り詰める必要のあるFlexの子要素に
min-width: 0を設定し忘れないでください。 - 絶対に、全文を表示するホバーやツールチップを用意せずにテキストを切り詰めないでください。
10. デザインシステムのサイズトークン(スペーシング、角丸、フォント、コーディコン、ストローク)
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 でも通常のグリフを使用してください。
ストローク — 境界線の幅
ストロークの太さは 1 種類のみです。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 |