microsoft / microsoft/vscode

Predictable UI composition: relationship ownership

Open
#335,354 1 comment 4 reactions 1 assignee Claimed by @chryw View on GitHub
debt modernization
Dominant language
TypeScript
Stars
193k
Forks
42.4k
PR merge metrics
PR metrics pending

Description

> [!NOTE]
> This issue tracks a shared UI composition and maintainability problem discovered while investigating #331234. It relates to the design-system direction in #334285; the active implementation is limited to the linked PR stack.

## Summary

While investigating the tab-spacing report in #331234, we found a more general source of UI drift: one visual relationship can be assembled from several feature-local declarations with no single owner.

For example, an icon can add its own horizontal margin while its containing button or row also applies a gap or padding. The final distance is produced by multiple rules and is not clear from either declaration by itself. Changing one layer can then silently double or shrink the visible relationship, shift an icon and its replacement spinner differently, or require consumer-specific compensation.

Icon sizing and spacing provide a concrete pilot for clearer layout ownership in shared UI code.

In #331234, the Search tab feels visually imbalanced, but it is not obvious whether the icon box, tab padding, or surrounding composition should change.

A Search tab whose icon and label appear offset within the available horizontal space

That ambiguity is the maintenance problem: a visual relationship should have one discoverable owner.

## Visual analysis

The annotations use blue for control padding or bounds, amber for spacing currently owned by an icon or child, and green for the proposed control or parent-owned relationship.

### 1. Same geometry, clearer owner: editor tabs

#### Current: distributed ownership

![Current editor tab with tab padding plus icon-owned padding producing the label position](https://github.com/user-attachments/assets/f917cc5a-92b8-4aae-ada6-b92c269139a9)

Tab padding (6px) + icon pseudo-element (3px + 16px + 3px) = 28px before the label. The icon partly controls where the label begins.

#### Explicit ownership

![Editor tab with the rendered icon owning its box and the tab owning the icon-to-label gap](https://github.com/user-attachments/assets/ed0fe1b6-86de-4212-bdcf-76df17ccc2a0)

Tab padding (6px) + rendered-icon box (16px) + control-owned relationship (6px) = the same 28px footprint. The icon owns only its size and fitting.

> **What changes is the ownership, not the rendered geometry.** This is an illustration of the model, not an additional editor-tab migration in the active PR stack.

### 2. Internal composition: shared button

#### Current: icon and control both contribute

Current Add workspace button with control padding plus icon-owned leading and trailing margins

The icon's leading margin stacks with button padding, making the left inset larger than the right. Its trailing margin also participates in label spacing.

#### Explicit ownership

Add workspace button with unchanged control padding and a control-owned icon-to-label relationship

The icon has no external margin. The control owns its inset and the icon-to-label relationship separately.

The active `WorkbenchButtonBar` pilot applies this responsibility to a leading slot: an action icon and its replacement spinner use one stable control-owned box and relationship.

### 3. One control across conditional states

A shared contract must remain coherent when the label, leading icon, inline label icons, busy spinner, emphasis, or disabled state changes.

![Button composition states in dark theme](https://github.com/user-attachments/assets/e8838a03-2cab-4d4e-a65a-cd8e09d98669)

Same state matrix in the light theme

![Button composition states in light theme](https://github.com/user-attachments/assets/4b357b13-36b4-4994-a855-bf495ac87a28)

### 4. Child margin versus parent relationship

The Agents Window prompt combines prominent selectors, compact pickers, repository controls, and icon-only actions, making the child/parent boundary visible.

#### Current: child-owned margin

![Current Agents Window prompt with child labels owning six-pixel left margins](https://github.com/user-attachments/assets/05757233-fb8a-4f8b-8ff9-90fe009544e3)

Each highlighted child label supplies its own 6px left margin. The containing control has no explicit icon-to-label relationship.

#### Explicit ownership

![Agents Window prompt with parent controls owning six-pixel gaps and unchanged bounds](https://github.com/user-attachments/assets/aed0bb6a-6df1-4620-805d-0e1306fd84bd)

The parent control supplies the same 6px separation through `column-gap`; the child margin is zero and the measured control bounds remain unchanged.

> This is a composition validation from the proposal, not an additional Agents Window migration in the active PR stack.

## Proposal: one relationship, one owner

> **Express a visual relationship once, in the lowest component contract that understands all participating elements.**

| Layer | Owns | Does not own |
|---|---|---|
| Icon source | Glyph path, SVG, raster pixels, or product-icon-theme lookup | Surrounding layout |
| Rendered icon | Semantic box, contain-fit, centering, and approved optical correction | Hit target or label separation |
| Control composition | Internal padding and relationships among icon, label, spinner, and actions | Spacing to neighboring controls |
| Parent composition | Spacing between controls and neighboring content | A child control's internal relationships |

Ownership is responsibility, not a required CSS mechanism. Depending on the composition, the owner may use `gap`, padding, or a targeted child margin.

> [!IMPORTANT]
> Optical correction stays inside the rendered icon's fixed geometry. It must not move the label, alter the control's external footprint, or resize the hit target.

The contract applies consistently to Codicons, product and file icons, SVGs, raster images, and themed icons.

### Signals to re-check the owner

Common signs that one relationship is distributed across layers include:

- horizontal padding or margin on an icon-generating pseudo-element;
- a child label margin combined with parent padding or `gap`;
- negative icon offsets compensating for surrounding geometry;
- different spacing rules for glyph and image render paths that produce the same control;
- conditional icon and spinner states that do not reserve the same alignment area.

These patterns are not automatically wrong, but they should prompt the reviewer to identify the intended relationship and its lowest knowledgeable owner.

Detailed geometry examples from the proposal

Comparable icon-label relationships currently emerge from different combinations of padding, margin, and alignment slots:

| Composition | Current geometry | Explicit ownership model |
|---|---|---|
| File or product icon | 3px + 16px icon + 3px on the icon pseudo-element | 16px rendered-icon box + 6px control-owned relationship |
| Text button | Icon with roughly 3.2px margin on each side | 16px rendered-icon box + 6px control-owned relationship |
| Subordinate status | 12px icon + 4px icon margin | 12px rendered-icon box + 4px control-owned relationship |
| Compact picker | 12px icon in a 16px alignment area + 6px icon margin | 12px icon in a 16px control-owned area + 6px control-owned relationship |

Other recurring signals from the original analysis:

| Pattern | Ownership concern |
|---|---|
| Dropdown indicator with `margin-left: -3px` | The icon compensates for surrounding geometry rather than fitting within an explicit control contract |
| `IconLabel` pseudo-element | Width, height, and right padding combine icon fitting with label separation |
| `IconLabel` image element | The same label relationship is expressed through an image margin instead |

The `IconLabel` comparison is useful evidence that representation-specific render paths can encode the same relationship differently. It does not imply that the full resource/list-label widget should be embedded in or extended for `Button`; both already share lower-level icon-label rendering helpers.

Shared size vocabulary and supporting icon-role evidence

Icon geometry and spacing describe different things and should use distinct token families.

![Five representation-neutral icon-box tiers from 12px through 32px, shown with representative VS Code roles](https://github.com/user-attachments/assets/fca3104d-f585-4970-a107-3595422907ad)

The same tiers and representative uses are provided as text in the table below.

### Representation-neutral icon boxes

| Tier | Size | Representative current use |
|---|---:|---|
| Extra small | 12px | Subordinate inline status and dense secondary chrome |
| Small | 16px | Default Codicons, editor tabs, and ordinary controls |
| Medium | 20px | Icon selectors and emphasized cards |
| Large | 24px | Activity Bar and prominent navigation |
| Extra large | 32px | Welcome-state and orientation cues |

These boxes apply across icon representations. Existing Codicon font-size tokens remain available for font-specific rendering and compact-glyph selection.

### Spacing

The shared spacing ramp is:

`0, 1, 2, 3, 4, 6, 8, 10, 12, 16, 20, 24, 28, 32, 36, 40px`

The 1px and 3px additions cover fine composition already used throughout the workbench:

| Step | Representative use |
|---:|---|
| 1px | Tight inset or separation where 2px is visibly too large |
| 3px | Compact control padding and tightly composed internal relationships |

Spacing tokens describe layout relationships. A 1px border still uses the stroke-thickness token, while an optical transform remains part of icon fitting.

Additional icon-role evidence

A subordinate warning row demonstrates a legitimate compact icon role within a larger control:

![A command confirmation card with a subordinate warning row beneath the command](https://github.com/user-attachments/assets/044c341c-2e8a-4714-87fe-f696fbb89cd2)

Notification rows show a different problem: an 18px severity glyph constrained by a 16px alignment area. The representation-neutral size describes the intended box independently from the severity artwork.

#### Current: glyph and alignment area disagree

![Notification rows with 18px severity glyphs constrained by a 16px icon area](https://github.com/user-attachments/assets/5ca48502-9218-427e-b02a-2830a9841309)

An 18px severity glyph is constrained by a 16px alignment area.

#### Explicit size role

![Notification rows with 16px severity glyphs aligned to the 16px icon area](https://github.com/user-attachments/assets/7e3bc816-79bf-4776-8556-4cc098838209)

The rendered-icon box and alignment area both use the 16px role.

These examples distinguish icon geometry from control density, padding, and hit-target size. They are supporting analysis, not additional product changes in the active PR stack.

## Evidence that this is a recurring pattern

An initial source audit found:

| Signal | Result | Why it matters |
|---|---:|---|
| Near-miss Codicon sizes | 25 declarations using 13-15px across 18 CSS files | Named 12px and 16px tiers make density choices reviewable |
| Fine icon-adjacent spacing | 44 occurrences across 31 CSS files: 14 at 1px and 30 at 3px | The spacing ramp can represent recurring fine relationships |
| Off-ramp icon-adjacent spacing | 9 occurrences at 5px across 8 CSS files | Value validation can identify candidates for ownership review |
| Existing shared render paths | 156 `renderIcon()` and 71 `renderLabelWithIcons()` references | Shared paths can reduce repeated feature-specific CSS |

These are review signals, not automatic defects. A value can be on the ramp but owned by the wrong layer, and a static match does not prove that a migration is needed.

Related product evidence

- #333053 — workspace-picker feedback provides direct icon/name gap evidence for control-owned spacing.
- #330786 — extension-contributed Panel icons show the need for consistent sizing across icon representations.
- #324632 — Chat panel icon positioning supports stable geometry with internal fitting and alignment.
- #324633 — Chat send-button alignment supports optical correction as an internal transform that does not affect layout.
- #324217 — Activity Bar compact mode shows why icon size, control padding, and hit target must remain separate.
- #325571 — compact sidebar feedback explicitly asks to reduce padding rather than shrink icons.
- #325070 — Agents Window feedback asks for consistent spacing between neighboring icon controls.
- #331483 — multi-row tabs need control-owned space for transient modification icons so content does not reflow.
- #332001 — compact-density work benefits from separating icon size, relationship, and hit target.

## Expected impact

- Shared UI controls have fewer independent values and compensating rules to maintain.
- Equivalent controls inherit the same geometry, so visual differences are more likely to represent an intentional role or hierarchy.
- Icon and spinner substitutions keep stable control geometry.
- Existing debt can be migrated incrementally by recurring pattern instead of through a broad rewrite.
- Shared contracts give both hand-authored and generated UI fewer raw sizes and feature-local rules to invent.

Codification and incremental adoption

The ownership model spans several authoring surfaces, each with a different responsibility:

| Surface | Responsibility |
|---|---|
| Size registry | Provide the neutral spacing and icon-box vocabulary |
| Token instructions | Distinguish spacing, stroke thickness, icon geometry, and optical correction |
| Design and layout guidance | Teach the icon source → rendered icon → control → parent decision model |
| Shared controls | Implement common internal relationships with behavior-safe defaults |
| Production-derived fixtures | Cover complete compositions, conditional states, themes, geometry, and accessible names |
| Advisory validation | Identify precise recurring candidates only after pilots demonstrate a low-noise rule |

Incremental sequence:

1. Land the shared size vocabulary and keep the validator aligned.
2. Publish the ownership model as review guidance.
3. Prove the model in representative shared controls with existing behavior preserved by default.
4. Migrate feature UI as recurring patterns are touched, starting with high-leverage shared paths.
5. Consider warning-only ownership checks or generated-UI guidance only where accepted pilots provide measurable, precise evidence.

The current active stack covers the first shared vocabulary, guidance, and `WorkbenchButtonBar` pilot. It does not propose blanket migration or automatic enforcement.

## Active implementation

1. #335345 — Add the recurring spacing steps and representation-neutral icon-size scale.
2. #335346 — Document the composition ownership heuristic and token usage.
3. #335350 — Let `WorkbenchButtonBar` own one leading icon/spinner slot and its label relationship; the Changes action row selects its existing wider spacing without adding a second container contribution.

This work is intentionally incremental. Existing UI geometry should remain unchanged unless a product change is explicitly proposed and reviewed.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.