Status: Canonical (v1.0)
Last updated: August 10, 2026
Audience: Design Systems, Product Design, Front-End Engineering
Applies to: Visual sizing of generic controls and component-affordance geometry (button, input, icon button, list/menu/table rows, progress and slider graphics)
Companion specs:
cem-dimension.md) — gaps, insets, layout rhythmcem-coupling.md) — operability safety contract (zone/guard/halo); coupling modes drive Controls geometry overridescem-shape.md) — consumes --cem-control-height for round-end bend geometrycem-stroke.md) — focus indicator placement on controlscem-voice-fonts-typography.md) — text inside controlsVisual control geometry (height, padding, icon-button size, row heights) and operability safety (zone/guard/halo) historically lived together in D2 Coupling. Splitting them clarifies responsibilities:
--cem-coupling-zone-min, --cem-coupling-guard-min, --cem-coupling-halo, and the mode-invariant rules that prevent intent mis-coupling regardless of input modality.The two contracts are coupled by policy: visual geometry MUST NOT shrink the operable zone below --cem-coupling-zone-min, and adjacency MUST respect --cem-coupling-guard-min regardless of how Controls geometry shrinks under compact.
See cem-controls-reasoning.md for the rationale behind the split.
D2c. Controls (this canvas)
D2. Coupling & Compactness (from cem-coupling.md)
D1. Space & Rhythm (from cem-dimension.md)
Boundary heuristic:
:root {
/* Generic control geometry */
--cem-control-height: 2.5rem;
--cem-control-padding-x: 0.75rem;
--cem-control-padding-y: 0.5rem;
/* Icon buttons: typically make the visible container meet the operable zone */
--cem-icon-button-size: var(--cem-coupling-zone-min);
--cem-icon-button-icon-size: 1.25rem;
/* Standalone icons */
--cem-icon-size: 2rem;
--cem-icon-size-small: 1rem;
--cem-icon-size-large: 3rem;
/* Lists / menus */
--cem-list-row-height: 3rem;
--cem-list-popup-rows: 8;
--cem-menu-row-height: 3rem;
/* Data tables */
--cem-table-row-height: 2.5rem;
}
| Token | Value | Description | tier |
|---|---|---|---|
--cem-control-height | 2.5rem | Generic control height | recommended |
--cem-control-height-small | 2.5rem | Small control profile minimum visible height | recommended |
--cem-control-height-medium | 3rem | Medium control profile minimum visible height | recommended |
--cem-control-height-large | 4rem | Large control profile minimum visible height | recommended |
--cem-control-height-x-large | 6rem | X-large control profile minimum visible height | recommended |
--cem-control-height-xx-large | 8rem | XX-large control profile minimum visible height; content may grow beyond it | recommended |
--cem-control-padding-x | 0.75rem | Generic control inline padding | recommended |
--cem-control-padding-y | 0.5rem | Generic control block padding | recommended |
--cem-icon-button-size | var(--cem-coupling-zone-min) | Icon button visible container size; meets zone minimum by default | recommended |
--cem-icon-button-icon-size | 1.25rem | Icon glyph size within an icon button | recommended |
--cem-icon-size | 2rem | Normal standalone icon size | recommended |
--cem-icon-size-small | 1rem | Small standalone icon size | recommended |
--cem-icon-size-large | 3rem | Large standalone icon size | recommended |
--cem-list-row-height | 3rem | List row height | recommended |
--cem-list-popup-rows | 8 | Default maximum visible rows in a transient list popup | recommended |
--cem-menu-row-height | 3rem | Menu row height | recommended |
--cem-table-row-height | 2.5rem | Data table row height | recommended |
data-cem-size="small|medium|large|x-large|xx-large" selects an inherited control-height
profile on any container. Components may expose size with the same values and
select the corresponding public height token on their host. Omission (or an unknown
value) adds no local height override. Removing an explicit size restores the
container's geometry. Local custom properties can override the selected profile.
| Size | Height |
|---|---|
small | var(--cem-control-height-small) |
medium | var(--cem-control-height-medium) |
large | var(--cem-control-height-large) |
x-large | var(--cem-control-height-x-large) |
xx-large | var(--cem-control-height-xx-large) |
These are minimum visible heights, not fixed boxes. Width and stretch remain owned by content and CSS layout. Typography, icon sizes and padding retain their existing inherited values. Coupling modes remain independent; an explicit size profile selects height while coupling continues to govern padding and safety. On the same element, a size profile wins over a coupling height override.
Choose the profile for available space and prominence; an icon alone does not require a large control. Image/title and hero compositions may use x-large and xx-large while growing with their content. Size does not select a content layout or change an action into a navigation link. See the dimension decision.
Small/default geometry below D2's minimum reserves a transparent native-button border inside the operable bounds. Paint is clipped to the padding box; focus surrounds the complete target. The inset is half the positive difference between the operable minimum and requested visual height, otherwise zero. Layout growth can enlarge the surface without enlarging the allocated box. This is not a visible outline and does not replace zebra. Containers must leave at least the D2 guard between targets. Size profiles never change the safety minimum.
Circular progress is non-interactive, so its visual size is not an operable-zone claim and does not grow or shrink with coupling mode. D2c still owns the single-component geometry: the outer diameter and the visible track thickness. D0 owns track/indicator color, and D7 owns any indeterminate cycle.
| Token | Value | Description | tier |
|---|---|---|---|
--cem-progress-spinner-size | 3rem | Default circular progress diameter | required |
--cem-progress-track-thickness | 0.25rem | Circular or linear progress track thickness | required |
Slider track thickness and visible thumb diameter are single-component visual
geometry. D2c owns those sizes; D2 continues to own the larger operable target
through --cem-coupling-zone-min, and D5 owns focus-outline geometry. A slider
implementation MUST keep its native thumb hit target at least zone-min even
when the visible thumb is smaller.
| Token | Value | Description | tier |
|---|---|---|---|
--cem-slider-track-thickness | 0.25rem | Visible remaining and active value-range track thickness | required |
--cem-slider-thumb-size | 1.25rem | Visible circular slider thumb diameter inside the D2 operable target | required |
Controls geometry varies by coupling mode. The mode selector is owned by D2 Coupling
(data-cem-coupling="forgiving|balanced|compact"); Controls only emits the visual override values for those modes.
The safety contract (zone-min, guard-min) does NOT change.
:root[data-cem-coupling="forgiving"] {
--cem-control-height: 2.75rem;
--cem-control-padding-x: 1rem;
--cem-control-padding-y: 0.625rem;
--cem-list-row-height: 3.25rem;
--cem-menu-row-height: 3.25rem;
}
:root[data-cem-coupling="compact"] {
--cem-control-height: 2.25rem;
--cem-control-padding-x: 0.625rem;
--cem-control-padding-y: 0.375rem;
--cem-list-row-height: 2.75rem;
--cem-menu-row-height: 2.75rem;
}
| Token | forgiving | compact |
|---|---|---|
--cem-control-height | 2.75rem | 2.25rem |
--cem-control-padding-x | 1rem | 0.625rem |
--cem-control-padding-y | 0.625rem | 0.375rem |
--cem-list-row-height | 3.25rem | 2.75rem |
--cem-menu-row-height | 3.25rem | 2.75rem |
Governance rule: visual geometry shrinks under compact only as far as the operable contract allows. When visual size
drops below --cem-coupling-zone-min, components MUST use halo expansion (see D2 §6.1) so the operable zone still
meets the safety contract.
--cem-control-height as the default height basis for round-end bend:
--cem-bend-round: calc(var(--cem-shape-height, var(--cem-control-height)) / 2). This is why round-end geometry
follows control sizing across coupling modes.--cem-control-* tokens..cem-control {
block-size: var(--cem-control-height);
padding-inline: var(--cem-control-padding-x);
padding-block: var(--cem-control-padding-y);
/* Safety: do not let visual height collapse the operable zone */
min-block-size: var(--cem-coupling-zone-min);
}
.cem-icon-button {
inline-size: var(--cem-icon-button-size);
block-size: var(--cem-icon-button-size);
}
.cem-icon-button > svg {
inline-size: var(--cem-icon-button-icon-size);
block-size: var(--cem-icon-button-icon-size);
}
If a product wants visible icon containers below --cem-coupling-zone-min, wrap them with halo expansion (see
cem-coupling.md §6.1) so operability is preserved.
.cem-list-row { min-block-size: var(--cem-list-row-height); }
.cem-list-popup { max-block-size: calc(var(--cem-list-row-height) * var(--cem-list-popup-rows)); }
.cem-menu-row { min-block-size: var(--cem-menu-row-height); }
.cem-table-row { block-size: var(--cem-table-row-height); }
For data tables where row visuals fall below the safety zone, ensure interactive cells (selection, action buttons)
meet --cem-coupling-zone-min independently.
A draft becomes "canonical" when all of the following are true:
--cem-control-*, --cem-icon-*, list/menu/table geometry,
--cem-progress-* graphics, and --cem-slider-* visual geometry tokens.
Safety tokens (--cem-coupling-*) are NOT emitted here.--cem-coupling-zone-min, halo expansion is the
stated remedy.--cem-control-height is declared here so Shape's round-end formula resolves.| Source table | Section | Description |
|---|---|---|
cem-controls-geometry | §3.1 | Baseline visual control geometry: height, padding, icon-button, list/menu/table row sizes |
cem-controls-size-profiles | §3.1.1 | Size selectors referencing baseline profile tokens; no additional tokens |
cem-progress-geometry | §3.2 | Circular-progress diameter and shared progress-track thickness |
cem-slider-geometry | §3.3 | Slider track thickness and visible thumb diameter |
cem-controls-geometry-overrides | §3.4 | Forgiving/compact override values for visual geometry (generator-only; no new tokens) |
Generator derivation rules:
cem-controls-size-profiles → [data-cem-size] height overrides, emitted after coupling rules.cem-controls-geometry → token list (tier in last column).cem-progress-geometry → token list (tier in last column).cem-slider-geometry → token list (tier in last column).cem-controls-geometry-overrides → override data only; tokens are already declared in the base :root block.--cem-coupling-zone-min, --cem-coupling-guard-min, --cem-coupling-halo) are NOT declared here; they belong to D2 Coupling.