every namethatui element, padded only at the leaves
containers arrange with flex / grid + gap, bg, radius, border, overflow-hidden. only leaves (text, buttons, inputs, badges, icon wrappers) own px / py. sibling spacing is parent gap. fills, dividers, sticky headers, and hover states therefore reach every edge with zero negative margins. toggle the inspector to see which nodes own padding. each element is shown twice: side a commits the classic mistake (the container pads, or margins replace gap) and side b is the leaf-only build. the sample under each pair is short enough to copy.
alert
the action row is a sibling band: the alert arranges, the cells pad themselves. pad the alert and the hover cells stop at the inset, never reaching the rounded edge.
a: the alert pads
this cannot be undone.
b: the leaves pad
this cannot be undone.
<div class="gm-alert"><span class="gm-alert-icon">!</span><strong class="gm-alert-q">delete "notes"?</strong><div class="gm-alert-actions">cancel / delete</div></div>.gm-alert { display:flex; flex-direction:column; gap:8px; overflow:hidden; border-radius:12px; }.gm-alert-actions { display:grid; grid-template-columns:1fr 1fr; border-top:1px solid; }.gm-alert-actions button { padding:8px 0; } /* cells pad; row is full-bleed */
color well
the swatch is the shape; its box and caret are padded leaves sharing one border. pad the swatch and the halves drift off the shared edge.
a: the swatch pads
b: the leaves pad
<label class="gm-swatch"><span class="gm-swatch-box"><input type="color"></span><span class="gm-swatch-caret"><svg></svg></span></label>.gm-swatch { display:flex; overflow:hidden; border-radius:6px; border:1px solid; }.gm-swatch-box { padding:4px; } /* each half pads itself */.gm-swatch-caret { padding:6px 8px; border-left:1px solid; }
column view
columns arrange; file rows pad themselves so selection reaches each divider. pad the window and every selection stops short of the column line.
a: the window pads
- apps
- docs
- music
- work
- taxes
- plan.md
- notes.md
b: the leaves pad
- apps
- docs
- music
- work
- taxes
- plan.md
- notes.md
<div class="gm-window"><div class="gm-titlebar">lights + title</div><div class="gm-columns"><ul class="gm-filelist"><li class="gm-file">docs</li>…</ul>…</div></div>.gm-columns { display:grid; grid-template-columns:1fr 1fr 1fr; divide-x; }.gm-file { display:flex; justify-content:space-between; padding:4px 12px; } /* selection reaches the divider */
disclosure triangle
the summary row and option rows pad themselves; depth comes from the leaf's own padding-left. pad the details and child rows can no longer highlight edge to edge.
a: the details pads
advanced
b: the leaves pad
advanced
<details class="gm-disclose"><summary class="gm-disclose-head">▸ advanced</summary><label class="gm-disclose-opt"><input type="checkbox"> verbose logs</label></details>.gm-disclose-head { display:flex; gap:4px; padding:4px 8px; }.gm-disclose-opt { display:flex; gap:8px; padding:4px 0 4px 28px; } /* indent is leaf padding-left */
dock badge
icons are leaves margining themselves inside the dock fill (leaf margin allowed); badges anchor to the icon corner. pad the dock and the icon row drifts off the dock frame.
a: the dock pads
b: the leaves margin themselves
<div class="gm-dock"><div class="gm-dock-app"><span class="gm-dock-icon"></span><span class="gm-dock-badge">3</span></div>…</div>.gm-dock { display:flex; align-items:flex-end; gap:12px; border-radius:16px; }.gm-dock-icon { margin:8px; width:48px; height:48px; border-radius:12px; } /* leaf margin allowed */.gm-dock-badge { position:absolute; top:0; right:0; border-radius:9999px; padding:0 6px; }
focus ring
the ring is drawn outside each leaf, and the gap reserves its room. swap the gap for per-child margins and the rhythm drifts; take the room away and rings overlap the neighbor.
a: margins replace the gap
b: gap reserves the ring's room
<div class="gm-focus"><input class="gm-focus-input" placeholder="click"><button class="gm-focus-btn">default</button></div>.gm-focus { display:flex; align-items:center; gap:16px; } /* gap = room for the ring */.gm-focus-input:focus { outline:none; box-shadow:0 0 0 4px sky; } /* drawn outside the leaf */
insertion caret
the caret is a zero-padding sibling between two padded text leaves, so the text hugs the border. pad the field and the text floats off it.
a: the field pads
b: the leaves pad
<div class="gm-caret-field"><span class="gm-caret-before">hello wor</span><span class="gm-caret"></span><span class="gm-caret-after">ld</span></div>.gm-caret-field { display:flex; align-items:center; border:1px solid; border-radius:6px; }.gm-caret-before { padding:4px 0 4px 8px; } /* text hugs the border */.gm-caret { width:1px; height:16px; }
inspector
section bands are self-padding regions and the input is inset by a gutter track, not a right margin. pad the window and the bands stop short of the frame.
a: the window pads
layout
fill
b: the leaves pad
layout
fill
<label class="gm-inspector-row"><span class="gm-inspector-key">width</span><input class="gm-inspector-input" value="320"></label>.gm-inspector-band { padding:4px 12px; border-bottom:1px solid; } /* band spans the panel */.gm-inspector-row { display:grid; grid-template-columns:1fr 1fr 12px; } /* 3rd track = gutter, no mr */
level indicator
segments are gap-separated siblings and the capacity track has no inset, so the fill reaches its rounded ends. pad the track and the fill falls short of them.
a: the capacity track pads
b: the leaves pad
<div class="gm-capacity"><span class="gm-seg is-full"></span> …</div>.gm-capacity { display:flex; gap:2px; overflow:hidden; border-radius:2px; } /* no inset */.gm-seg { width:16px; height:12px; } /* fixed-size siblings, gap-spaced */
outline view
depth is each row's own padding-left, so the selected fill still spans the full width. pad the list and selection stops short of the edges.
a: the list pads
- src
- components
- button.tsx
- card.tsx
- public
b: the leaves pad
- src
- components
- button.tsx
- card.tsx
- public
<ul class="gm-outline"><li class="gm-outline-row is-d1">▸ src</li><li class="gm-outline-row is-d3 is-active">button.tsx</li></ul>.gm-outline-row { padding:4px 8px 4px 8px; } /* base inset on the leaf */.gm-outline-row.is-d3 { padding-left:36px; } /* depth = leaf padding-left, fill still spans */
panel
the title strip and list rows pad themselves; the frame only arranges. pad the panel and the title fill stops short of the frame.
a: the panel pads
- helvetica
- monolisa
- sf pro
b: the leaves pad
- helvetica
- monolisa
- sf pro
<div class="gm-panel"><div class="gm-panel-head">title + close</div><ul class="gm-panel-list"><li class="gm-panel-row is-active">monolisa</li>…</ul></div>.gm-panel-head { display:flex; justify-content:space-between; } /* strip is flush */.gm-panel-title { padding:4px 8px; }.gm-panel-close { padding:4px 8px; } /* hover fills to the frame edge */
pointer
each cell is a padded leaf and a cursor target; the grid spaces them with one gap value. swap gap for per-child margins and the rhythm drifts cell by cell.
a: margins replace the gap
b: gap on the grid
<div class="gm-pointers"><span class="gm-pointer is-pointer">pointer</span> …</div>.gm-pointers { display:grid; grid-template-columns:repeat(4,1fr); gap:8px; }.gm-pointer { padding:16px 12px; text-align:center; border-radius:4px; } /* cell is the cursor target */
popover
the arrow overlaps with translate, not negative margin, and the action row is a full-width band. pad the body and the action stops short of the rounded edge.
a: the body pads
b: the leaves pad
<div class="gm-pop"><div class="gm-pop-arrow"></div><div class="gm-pop-body">title / sub / action</div></div>.gm-pop-arrow { translate:0 6px; rotate:45deg; } /* overlap by transform, never negative margin */.gm-pop-action { padding:8px 0; border-top:1px solid; } /* full-width band */
popup / pulldown / combo box
each control is a shape; its value and arrow cap are padded leaves. pad the controls and the cap fill stops short of the ends.
a: the controls pad
b: the leaves pad
<label class="gm-select"><select class="gm-select-el">medium</select><span class="gm-select-cap">▲▼</span></label>.gm-select { display:flex; overflow:hidden; border-radius:6px; border:1px solid; }.gm-select-el { padding:4px 24px 4px 8px; appearance:none; } /* value pads itself */.gm-select-cap { padding:4px; } /* cap fills the control's right end */
save panel
rows arrange on a gutter-track grid, and the button band keeps its inset with min-height plus an after spacer. no field or button margins anywhere. pad the window and every band stops short of the sheet.
a: the window pads
b: the leaves pad
<div class="gm-save-body"><label class="gm-save-row"><span class="gm-save-key">save as:</span><Input className="gm-save-input h-8 rounded-md px-2 text-xs" defaultValue="report.pdf" /></label><div class="gm-save-actions">cancel / save</div></div>.gm-save-body { display:flex; flex-direction:column; gap:12px; } /* rows space on gap */.gm-save-row { display:grid; grid-template-columns:5rem 1fr 1rem; gap:8px; } /* 3rd track = gutter */.gm-save-actions { min-height:48px; align-items:center; justify-content:flex-end; gap:8px; }.gm-save-actions::after { content:""; width:12px; } /* band inset by spacer, never mr */
scroll view
row fills run under the scrollbar track with no gutter inset. pad the scroller and the stripes stop short of the frame.
a: the scroller pads
row 1
row 2
row 3
row 4
row 5
row 6
row 7
row 8
row 9
row 10
row 11
row 12
b: the leaves pad
row 1
row 2
row 3
row 4
row 5
row 6
row 7
row 8
row 9
row 10
row 11
row 12
<div class="gm-scrollview"><p class="gm-scrollrow">row 1</p> …</div>.gm-scrollview { height:128px; overflow-y:scroll; border-radius:8px; } /* no gutter inset */.gm-scrollrow { padding:6px 12px; }.gm-scrollrow:nth-child(odd) { background:subtle; } /* stripes run under the track */
search field
icon, input and clear are padded leaves; the field shape is the parent's radius. pad the field and the content floats off the shape.
a: the field pads
b: the leaves pad
<label class="gm-search"><span class="gm-search-icon">🔍</span><input class="gm-search-el" placeholder="search"><button class="gm-search-clear">✕</button></label>.gm-search { display:flex; align-items:center; overflow:hidden; border-radius:6px; }.gm-search-icon { padding-left:8px; } /* leaves pad; the shape is the parent's radius */
segmented control
the thumb is the checked leaf's fill and the track has no padding; a real macos inset would be a leaf margin, never parent padding. pad the track and the thumb stops acting like a full-bleed segment.
a: the track pads
b: the leaves pad
<div class="gm-segmented"><label class="gm-segment"><input type="radio" checked> week</label> …</div>.gm-segmented { display:flex; gap:2px; border-radius:8px; } /* track has no padding */.gm-segment:has(:checked) { background:fill; box-shadow:shadow; } /* thumb = checked leaf */
sheet
the sheet hangs flush from the title bar; text and button cells are self-padding regions. pad the sheet and the action band stops short of the bottom edge.
a: the sheet pads
save changes before closing?
b: the leaves pad
save changes before closing?
<div class="gm-sheet"><p class="gm-sheet-text">save changes before closing?</p><div class="gm-sheet-actions">don't save / save</div></div>.gm-sheet { border:1px solid; border-top:0; border-radius:0 0 8px 8px; } /* hangs flush */.gm-sheet-text { padding:12px 16px; }.gm-sheet-btn { padding:8px 0; } /* cells pad; band is full-bleed */
slider
the track fills its flex slot and the icons are padded leaves spaced by one gap. swap gap for margins and the row's rhythm becomes per-child edits.
a: margins replace the gap
b: gap on the row
<div class="gm-slider"><span class="gm-slider-icon">♪</span><input type="range" value="60"><span class="gm-slider-value">60</span></div>.gm-slider { display:flex; align-items:center; gap:8px; }.gm-slider-el { flex:1; } /* track fills its slot; only leaves pad */
split view
the divider is a full-height sibling line and the panes only arrange. pad the split and the divider no longer reaches the frame.
a: the split pads
b: the leaves pad
<div class="gm-split"><div class="gm-split-side">list</div><div class="gm-split-divider"></div><div class="gm-split-main">detail</div></div>.gm-split { display:flex; overflow:hidden; border-radius:8px; }.gm-split-divider { width:1px; cursor:col-resize; } /* full-height sibling line */.gm-split-label { padding:8px 12px; }
stepper
the readout pads itself and the button halves hover to their own rounded corners. pad the stack and the hover fills stop inside it.
a: the button stack pads
b: the leaves pad
<div class="gm-stepper"><input class="gm-stepper-readout" value="12"><div class="gm-stepper-buttons">▲ | ▼</div></div>.gm-stepper-buttons { display:flex; flex-direction:column; overflow:hidden; border-radius:4px; }.gm-stepper-btn { padding:2px 4px; } /* each half hovers to its rounded corner */
token field
tokens wrap on gap and the field border hugs them with zero inset. pad the field and the tokens float off the border.
a: the field pads
b: the leaves pad
<div class="gm-tokens"><span class="gm-tokens-key">to:</span><span class="gm-token">ada</span><input class="gm-tokens-el"></div>.gm-tokens { display:flex; flex-wrap:wrap; align-items:center; gap:4px; border-radius:6px; }.gm-token { padding:2px 8px; border-radius:9999px; } /* tokens wrap on gap; border hugs them */
toolbar
toolbar items pad themselves, so hover fills run the full toolbar height. pad the toolbar and every hover fill stops short.
a: the toolbar pads
b: the leaves pad
<div class="gm-toolbar"><button class="gm-toolbar-btn">‹</button><span class="gm-toolbar-title">documents</span><button class="gm-toolbar-btn">🔍</button></div>.gm-toolbar { display:flex; align-items:stretch; overflow:hidden; border-radius:12px 12px 0 0; }.gm-toolbar-btn { padding:8px 12px; } /* hover fills run the full toolbar height */
traffic lights
no padding anywhere: fixed-size shapes spaced by the parent's one gap value. swap gap for per-child margins and the cluster's rhythm becomes three edits instead of one.
a: margins replace the gap
b: gap on the row
<div class="gm-traffic"><span class="gm-traffic-light is-rose">✕</span> …</div>.gm-traffic { display:flex; gap:8px; } /* no padding anywhere */.gm-traffic-light { width:12px; height:12px; border-radius:9999px; } /* fixed-size shapes */
vibrancy
the blur material reaches the window edge and selection rows tint on top of it. pad the material and the tint stops short of the frame.
a: the material pads
b: the leaves pad
<div class="gm-vibrancy"><nav class="gm-vibrancy-nav"><a class="gm-vibrancy-item is-active">airdrop</a> …</nav></div>.gm-vibrancy { overflow:hidden; border-radius:12px; } /* material reaches the window edge */.gm-vibrancy-nav { backdrop-filter:blur(24px); }.gm-vibrancy-item { padding:6px 12px; } /* selection tints on top of the material */
window
titlebar and status bar are bands that run edge to edge; the content region is the only padded leaf. pad the window and both bands stop short.
a: the window pads
content region
b: the leaves pad
content region
<div class="gm-window"><div class="gm-titlebar">lights + title</div><div class="gm-window-body"><p class="gm-window-content">content region</p><div class="gm-window-status">12 items / 2 gb free</div></div></div>.gm-titlebar, .gm-window-status { border-*:1px solid; } /* bands run edge to edge */.gm-window-content { padding:12px 16px; } /* the only padded region */