---
slug: thumb-first-desktop
tags: [design-philosophy, ui, dashboards, admin, settings, ios, web]
ground: "#1c1c1e"
ink: "#f2f2f7"
accent: "#0a84ff"
mono: JetBrains Mono
---
# Thumb-first desktop: chunky, grouped, quiet

A desktop interface built as if every control had to be hit with a thumb. Rows are tall, targets are fat, settings live in rounded grouped lists like iOS and macOS System Settings, and almost nothing is written that the layout can say instead. It feels solid and calm, like hardware, and it is fast to use because there is less to read and nothing small to aim at. This is the philosophy behind a self-hosted container platform's admin UI, written down after the fact.

## When this applies

- **Fits:** admin panels, settings, dashboards, infra and dev tools, internal apps. Anywhere people configure things and come back often.
- **Fits with care:** dense data views (logs, big tables, code). Keep the chunky frame around them, but let the data itself stay compact and monospaced.
- **Doesn't fit:** editorial or marketing pages, and expert tools where density is the point (a trading terminal, a DAW). There, 52px rows waste the screen.
- **Needs the subtraction, not just the size.** Big rows full of paragraphs are just a bigger mess. Sections 3 and 4 are the half people skip.

## 1. Size for the thumb, even with a mouse

- **Rows are 52px minimum, buttons 38 to 44px, pills 30px or more.** Nothing clickable is under 30px tall.
- **The whole row is the target.** A row that navigates is a link edge to edge with a chevron `›`, not a small "Edit" link at the end.
- **Generous radius (14px groups, 10px controls, full pills).** Corners say "touchable object", not "box on a page".
- **Spacing is roomy and even.** 16px inside rows, 24 to 32px between groups.

Why: big targets remove aiming. People move faster when they never have to slow down to hit something.

## 2. Group, don't box

- **Settings live in grouped lists:** a small uppercase muted title above, one rounded panel, hairline separators between rows, an optional one-line footnote below.
- **One row, one fact or one action.** Label on the left, value or control on the right.
- **Toggles are switches,** not checkboxes. Values are inline inputs inside the row, and Save only appears when the value is dirty.
- **Sidebars and sections are lists too:** a tall selected row in solid accent, the rest quiet.

Why: grouped lists are the most practised settings pattern there is. People already know how to read them.

## 3. Fewer words

- **Labels are one to three words.** "Server", "Key", "Linked containers". Never a sentence where a noun works.
- **One footnote per group at most,** and only when the group would otherwise be misunderstood.
- **Status is a pill, not prose:** `on`, `Running`, `attached`, `Blocked`.
- **Empty states are one line** that says what to do: "None yet. Connect from Settings, Integrations."
- If you are writing a paragraph to explain a screen, redesign the screen.

## 4. Quiet surface, loud meaning

- **Dark grouped palette:** near-black ground, slightly lifted panels, soft gray text for anything secondary.
- **One accent (system blue)** for the primary action, focus and the selected row.
- **Status colours mean state only:** green running, amber pending or restart needed, red destructive or failed.
- **Destructive actions are red text or a red button,** placed last, separated from the safe ones.

## 5. Make the hardware feel solid

- **Sliders with big readouts** instead of bare number fields, with tappable tick labels and an "Exact" escape hatch.
- **Live context next to every limit:** "using 412 MB" beside a memory cap, a warning if the cap is below current use.
- **A sticky save dock** for multi-field forms: blurred bar, "Unsaved changes", Reset, Save. It only appears when something changed.
- **Small icons that encode kind** (app vs agent, workspace vs live) and repeat everywhere that kind appears, filters included.
- **Act immediately on routine actions.** Confirm only real data loss. Fast people hate dialogs.

## Applied well vs. misapplied

| Idea | Applied well | Misapplied |
|---|---|---|
| Thumb-sized | 52px rows, whole row clickable with a chevron | Big padding around a tiny text link |
| Grouped lists | Uppercase title, one rounded panel, hairline rows | Every setting in its own bordered card |
| Fewer words | "Key" with a masked value and Change | A paragraph explaining what the key is for |
| Pills for status | `Running` in green beside the name | "The container is currently running." |
| Switches | iOS switch on the right of a one-line row | A checkbox with a sentence-long label |
| Save dock | Appears on first edit, Reset and Save | A permanent Save button at the bottom of a long page |
| Confirmation | Only for deleting data | "Are you sure?" on restart, stop and kill |

## Using this in your own work

1. Set the floor: 52px rows, 38px buttons, 14px group radius. Do not go below it.
2. Turn every settings page into grouped lists before styling anything.
3. Cut every label to three words and every group to one footnote.
4. Replace status sentences with pills and checkboxes with switches.
5. Add the save dock, live usage beside limits, and kind icons. Then remove one more sentence.

> Make it big enough to hit without looking, and short enough to read without reading.

## Brand guide

```html example Palette | half
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<style>
*{box-sizing:border-box}body{margin:0;padding:32px;background:#1c1c1e;color:#f2f2f7;font:15px/1.4 -apple-system,BlinkMacSystemFont,"SF Pro Text","Segoe UI",Inter,Roboto,sans-serif;-webkit-font-smoothing:antialiased}
.gt{font-size:12px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:#8e8e93;margin:24px 4px 8px}.gt:first-child{margin-top:0}
.box{background:#2c2c2e;border-radius:14px;overflow:hidden}
.r{display:flex;align-items:center;gap:14px;min-height:52px;padding:8px 16px;border-top:1px solid rgba(84,84,88,.55)}.r:first-child{border-top:0}
.sw{width:32px;height:32px;border-radius:9px;flex:none;box-shadow:inset 0 0 0 1px rgba(255,255,255,.08)}
.n{flex:1;font-weight:500}.h{font:13px ui-monospace,Menlo,monospace;color:#8e8e93}.u{color:#8e8e93;font-size:13px;width:190px;text-align:right}
.ft{color:#8e8e93;font-size:13px;margin:8px 4px 0}
</style></head><body>
<div class="gt">Ground</div><div class="box">
<div class="r"><span class="sw" style="background:#1c1c1e"></span><span class="n">Background</span><span class="u">Page</span><span class="h">#1C1C1E</span></div>
<div class="r"><span class="sw" style="background:#2c2c2e"></span><span class="n">Panel</span><span class="u">Groups, sidebar</span><span class="h">#2C2C2E</span></div>
<div class="r"><span class="sw" style="background:#3a3a3c"></span><span class="n">Panel 2</span><span class="u">Buttons, inputs, hover</span><span class="h">#3A3A3C</span></div>
<div class="r"><span class="sw" style="background:#545458"></span><span class="n">Separator</span><span class="u">Hairlines at 55%</span><span class="h">#545458</span></div></div>
<div class="gt">Ink</div><div class="box">
<div class="r"><span class="sw" style="background:#f2f2f7"></span><span class="n">Text</span><span class="u">Labels, values</span><span class="h">#F2F2F7</span></div>
<div class="r"><span class="sw" style="background:#8e8e93"></span><span class="n">Muted</span><span class="u">Group titles, footnotes</span><span class="h">#8E8E93</span></div></div>
<div class="gt">Accent</div><div class="box">
<div class="r"><span class="sw" style="background:#0a84ff"></span><span class="n">Blue</span><span class="u">Primary, focus, selected</span><span class="h">#0A84FF</span></div></div>
<div class="gt">Status only</div><div class="box">
<div class="r"><span class="sw" style="background:#30d158"></span><span class="n">Green</span><span class="u">Running, on, attached</span><span class="h">#30D158</span></div>
<div class="r"><span class="sw" style="background:#ff9f0a"></span><span class="n">Amber</span><span class="u">Pending, restart needed</span><span class="h">#FF9F0A</span></div>
<div class="r"><span class="sw" style="background:#ff453a"></span><span class="n">Red</span><span class="u">Destructive, failed</span><span class="h">#FF453A</span></div></div>
<p class="ft">Blue is the only colour you choose. Green, amber and red are states, never decoration.</p>
</body></html>
```

```html example Type | half
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;600&display=swap" rel="stylesheet">
<style>
*{box-sizing:border-box}body{margin:0;padding:32px;background:#1c1c1e;color:#f2f2f7;font:15px/1.4 -apple-system,BlinkMacSystemFont,"SF Pro Text","Segoe UI",Inter,Roboto,sans-serif;-webkit-font-smoothing:antialiased}
.gt{font-size:12px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:#8e8e93;margin:24px 4px 8px}.gt:first-child{margin-top:0}
.box{background:#2c2c2e;border-radius:14px;overflow:hidden}
.spec{display:grid;grid-template-columns:1fr 260px}.big{padding:24px;font-size:52px;font-weight:700;letter-spacing:-.02em;display:flex;align-items:center}
.big.m{font:600 34px "JetBrains Mono",ui-monospace,Menlo,monospace}
dl{margin:0;padding:16px 20px;background:#3a3a3c;display:grid;grid-template-columns:80px 1fr;gap:6px 10px;font-size:13px;align-content:center}dt{color:#8e8e93}dd{margin:0}
.r{display:flex;align-items:baseline;gap:16px;min-height:52px;padding:12px 16px;border-top:1px solid rgba(84,84,88,.55)}.r:first-child{border-top:0}
.k{width:150px;color:#8e8e93;font-size:13px;flex:none}
@media(max-width:640px){.spec{grid-template-columns:1fr}}
</style></head><body>
<div class="gt">Interface: system sans</div><div class="box spec"><div class="big">Containers</div>
<dl><dt>Use</dt><dd>Everything you read</dd><dt>Weights</dt><dd>400, 500, 600, 700</dd><dt>Tracking</dt><dd>-0.02em at 28px+</dd><dt>Fallback</dt><dd>Segoe UI, Inter, Roboto</dd></dl></div>
<div class="gt">Data: mono</div><div class="box spec"><div class="big m">smtp:25</div>
<dl><dt>Use</dt><dd>Hosts, keys, paths, IDs</dd><dt>Weights</dt><dd>400, 600</dd><dt>Tracking</dt><dd>0</dd><dt>Fallback</dt><dd>SF Mono, Menlo, Consolas</dd></dl></div>
<div class="gt">Scale</div><div class="box">
<div class="r"><span class="k">Display 28 / 700</span><span style="font-size:28px;font-weight:700;letter-spacing:-.02em">Billing Bot</span></div>
<div class="r"><span class="k">Heading 22 / 700</span><span style="font-size:22px;font-weight:700">Integrations</span></div>
<div class="r"><span class="k">Row 15 / 500</span><span style="font-weight:500">Allow containers to connect</span></div>
<div class="r"><span class="k">Group title 12 / 600</span><span style="font-size:12px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:#8e8e93">Linked containers</span></div>
<div class="r"><span class="k">Footnote 13 / 400</span><span style="font-size:13px;color:#8e8e93">Agents never see it.</span></div>
<div class="r"><span class="k">Mono 13 / 400</span><span style="font:13px 'JetBrains Mono',monospace">/hooks/billing-bot</span></div></div>
</body></html>
```

```html example UI sheet
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;600&display=swap" rel="stylesheet">
<style>
*{box-sizing:border-box}body{margin:0;padding:32px;background:#1c1c1e;color:#f2f2f7;font:15px/1.4 -apple-system,BlinkMacSystemFont,"SF Pro Text","Segoe UI",Inter,Roboto,sans-serif;-webkit-font-smoothing:antialiased}
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:24px;max-width:1000px}
.gt{font-size:12px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:#8e8e93;margin:0 4px 8px}
.box{background:#2c2c2e;border-radius:14px;overflow:hidden}
.r{display:flex;align-items:center;gap:12px;min-height:52px;padding:8px 16px;border-top:1px solid rgba(84,84,88,.55);flex-wrap:wrap}.r:first-child{border-top:0}
.g{flex:1}.m{color:#8e8e93;font-size:13px}.mono{font-family:"JetBrains Mono",ui-monospace,Menlo,monospace}
.btn{font:600 15px inherit;font-family:inherit;min-height:40px;padding:0 18px;border-radius:10px;border:0;background:#3a3a3c;color:#f2f2f7;cursor:pointer}
.btn.p{background:#0a84ff}.btn:disabled{opacity:.4;cursor:default}
.ib{width:44px;height:44px;border:0;background:#3a3a3c;color:#f2f2f7;display:grid;place-items:center;cursor:pointer;border-radius:10px}.ib.c{border-radius:50%}.ib.p{background:#0a84ff}.ib svg{width:20px;height:20px}
.sw{width:51px;height:31px;border-radius:99px;background:#30d158;position:relative;flex:none;border:0;padding:0;cursor:pointer}.sw::after{content:"";position:absolute;top:2px;left:22px;width:27px;height:27px;border-radius:50%;background:#fff;box-shadow:0 2px 4px rgba(0,0,0,.3);transition:left .15s}
.sw[aria-checked=false]{background:#3a3a3c}.sw[aria-checked=false]::after{left:2px}
.seg{display:flex;background:#3a3a3c;border-radius:10px;padding:3px;flex:1}.seg button{flex:1;min-height:34px;border:0;background:none;color:#f2f2f7;font:500 14px inherit;font-family:inherit;border-radius:8px;cursor:pointer}.seg button[aria-pressed=true]{background:#636366;box-shadow:0 2px 4px rgba(0,0,0,.3);font-weight:600}
.big{font-size:28px;font-weight:700;letter-spacing:-.02em}
input[type=range]{-webkit-appearance:none;appearance:none;width:100%;height:6px;border-radius:99px;background:linear-gradient(#0a84ff,#0a84ff) 0/var(--v,50%) 100% no-repeat,#3a3a3c;outline:none;margin:6px 0}
input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:28px;height:28px;border-radius:50%;background:#fff;box-shadow:0 2px 4px rgba(0,0,0,.3);cursor:pointer}
input[type=range]::-moz-range-thumb{width:28px;height:28px;border:0;border-radius:50%;background:#fff;box-shadow:0 2px 4px rgba(0,0,0,.3)}
.ticks{display:flex;justify-content:space-between;width:100%}.ticks span{color:#8e8e93;font-size:13px;min-height:30px;display:flex;align-items:center}
.dialw{display:flex;align-items:center;gap:20px;padding:16px}
.dial{width:96px;height:96px;border-radius:50%;flex:none;position:relative;background:conic-gradient(from 225deg,#0a84ff 0 var(--a,180deg),#3a3a3c 0 270deg,transparent 0);cursor:grab;touch-action:none}
.dial::before{content:"";position:absolute;inset:8px;border-radius:50%;background:#2c2c2e}
.knob{position:absolute;inset:14px;border-radius:50%;background:#3a3a3c;box-shadow:0 2px 6px rgba(0,0,0,.4);transform:rotate(calc(var(--a,180deg) - 135deg))}.knob::after{content:"";position:absolute;left:50%;top:6px;width:4px;height:14px;margin-left:-2px;border-radius:2px;background:#f2f2f7}
.in{font:14px "JetBrains Mono",ui-monospace,Menlo,monospace;min-height:38px;padding:0 12px;border-radius:10px;border:0;background:#3a3a3c;color:#f2f2f7;width:100%;outline:none}
.in::placeholder{color:#8e8e93}.in.f,.in:focus{box-shadow:0 0 0 3px rgba(10,132,255,.5)}
.srch{position:relative;flex:1}.srch svg{position:absolute;left:11px;top:50%;margin-top:-8px;width:16px;height:16px;color:#8e8e93}.srch .in{padding-left:34px;font-family:inherit;font-size:15px}
.img{height:120px;display:block;width:100%}
.ct{padding:12px 16px 4px}.ct b{display:block;font-size:17px}
.cal{padding:12px 16px 16px}.calh{display:flex;align-items:center;justify-content:space-between;min-height:40px;font-weight:600}
.days{display:grid;grid-template-columns:repeat(7,1fr);gap:2px;text-align:center;font-size:14px}.days span{min-height:34px;display:grid;place-items:center;border-radius:50%}
.days .w{color:#8e8e93;font-size:12px;font-weight:600;min-height:24px}.days .o{color:#545458}.days .t{color:#0a84ff;font-weight:700}.days .s{background:#0a84ff;color:#fff;font-weight:600}
.pill{display:inline-flex;align-items:center;gap:6px;min-height:30px;padding:0 12px;border-radius:99px;font-size:13px;font-weight:600}.pill i{width:8px;height:8px;border-radius:50%;background:currentColor}
.ok{background:rgba(48,209,88,.16);color:#30d158}.wa{background:rgba(255,159,10,.16);color:#ff9f0a}.er{background:rgba(255,69,58,.16);color:#ff453a}
.ch{color:#8e8e93;font-size:22px}
@media(max-width:480px){body{padding:16px}}
</style></head><body><div class="grid">

<section data-ui="button"><div class="gt">Button</div><div class="box"><div class="r"><button class="btn p">Save</button><button class="btn">Restart</button><button class="btn" disabled>Disabled</button></div></div></section>

<section data-ui="icon-button"><div class="gt">Icon button</div><div class="box"><div class="r">
<button class="ib p" aria-label="Add"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round"><path d="M12 5v14M5 12h14"/></svg></button>
<button class="ib" aria-label="Restart"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 12a8 8 0 1 1-2.3-5.6M20 4v5h-5"/></svg></button>
<button class="ib c" aria-label="Settings"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"><path d="M4 7h10M18 7h2M4 17h2M10 17h10"/><circle cx="16" cy="7" r="2"/><circle cx="8" cy="17" r="2"/></svg></button>
<button class="ib c" aria-label="Delete" style="color:#ff453a"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 7h16M9 7V4h6v3M6 7l1 13h10l1-13"/></svg></button>
</div></div></section>

<section data-ui="toggle"><div class="gt">Toggle</div><div class="box">
<div class="r"><span class="g">Send email</span><button class="sw" role="switch" aria-checked="true" aria-label="Send email"></button></div>
<div class="r"><span class="g">Webhooks</span><button class="sw" role="switch" aria-checked="false" aria-label="Webhooks"></button></div></div></section>

<section data-ui="segmented"><div class="gt">Segmented</div><div class="box"><div class="r"><div class="seg" role="group" aria-label="Priority"><button aria-pressed="false">Low</button><button aria-pressed="true">Medium</button><button aria-pressed="false">High</button></div></div></div></section>

<section data-ui="slider"><div class="gt">Slider</div><div class="box"><div class="r"><span class="g">Memory</span><span class="big mono" id="sv">512 MB</span>
<input type="range" id="sl" min="128" max="1024" step="128" value="512" aria-label="Memory"><div class="ticks"><span>128</span><span>512</span><span>1024</span></div></div></div></section>

<section data-ui="dial"><div class="gt">Dial</div><div class="box"><div class="dialw"><div class="dial" id="dl" role="slider" aria-label="CPU" aria-valuemin="0" aria-valuemax="100" aria-valuenow="67" tabindex="0" style="--a:180deg"><div class="knob"></div></div>
<div><div class="big mono" id="dv">67%</div><div class="m">CPU share</div></div></div></div></section>

<section data-ui="input"><div class="gt">Input</div><div class="box">
<div class="r"><div class="srch"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round"><circle cx="11" cy="11" r="7"/><path d="M20 20l-4-4"/></svg><input class="in" placeholder="Search" aria-label="Search"></div></div>
<div class="r"><span class="m" style="width:48px">Server</span><input class="in f" style="flex:1;width:auto" value="smtp.example.com" aria-label="Server"></div></div></section>

<section data-ui="card"><div class="gt">Card</div><div class="box">
<svg class="img" viewBox="0 0 300 120" preserveAspectRatio="xMidYMid slice" aria-hidden="true"><rect width="300" height="120" fill="#3a3a3c"/><g fill="#2c2c2e"><rect x="40" y="24" width="220" height="22" rx="6"/><rect x="40" y="52" width="220" height="22" rx="6"/><rect x="40" y="80" width="220" height="22" rx="6"/></g><g fill="#30d158"><circle cx="56" cy="35" r="4"/><circle cx="56" cy="63" r="4"/></g><circle cx="56" cy="91" r="4" fill="#ff9f0a"/><g fill="#545458"><rect x="70" y="32" width="70" height="6" rx="3"/><rect x="70" y="60" width="96" height="6" rx="3"/><rect x="70" y="88" width="58" height="6" rx="3"/></g></svg>
<div class="ct"><b>billing-bot</b><span class="m">3 containers</span></div>
<div class="r" style="border-top:0"><span class="g"></span><button class="btn p">Open</button></div></div></section>

<section data-ui="calendar"><div class="gt">Calendar</div><div class="box cal">
<div class="calh"><span>October 2026</span><span class="ch">›</span></div>
<div class="days"><span class="w">M</span><span class="w">T</span><span class="w">W</span><span class="w">T</span><span class="w">F</span><span class="w">S</span><span class="w">S</span>
<span class="o">28</span><span class="o">29</span><span class="o">30</span><span>1</span><span>2</span><span>3</span><span>4</span>
<span>19</span><span>20</span><span>21</span><span>22</span><span class="t">23</span><span class="s">24</span><span>25</span></div></div></section>

<section data-ui="status"><div class="gt">Status</div><div class="box">
<div class="r"><span class="g">web</span><span class="pill ok"><i></i>Running</span></div>
<div class="r"><span class="g">worker</span><span class="pill wa"><i></i>Restart needed</span></div>
<div class="r"><span class="g">mailer</span><span class="pill er"><i></i>Failed</span></div></div></section>

</div><script>
document.querySelectorAll('.sw').forEach(s=>s.onclick=()=>s.setAttribute('aria-checked',s.getAttribute('aria-checked')!=='true'));
document.querySelectorAll('.seg button').forEach(b=>b.onclick=()=>{b.parentNode.querySelectorAll('button').forEach(x=>x.setAttribute('aria-pressed',x===b))});
var sl=document.getElementById('sl'),sv=document.getElementById('sv');function us(){sl.style.setProperty('--v',(sl.value-sl.min)/(sl.max-sl.min)*100+'%');sv.textContent=sl.value+' MB'}sl.oninput=us;us();
var dl=document.getElementById('dl'),dv=document.getElementById('dv'),v=67;function ud(){dl.style.setProperty('--a',v*2.7+'deg');dv.textContent=v+'%';dl.setAttribute('aria-valuenow',v)}
dl.onpointerdown=e=>{dl.setPointerCapture(e.pointerId);var y=e.clientY,s=v;dl.onpointermove=m=>{v=Math.max(0,Math.min(100,Math.round(s+(y-m.clientY)/2)));ud()}};dl.onpointerup=()=>dl.onpointermove=null;
dl.onkeydown=e=>{if(e.key=='ArrowUp'||e.key=='ArrowRight')v=Math.min(100,v+5);else if(e.key=='ArrowDown'||e.key=='ArrowLeft')v=Math.max(0,v-5);else return;e.preventDefault();ud()};ud();
</script></body></html>
```

```html example In use
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<style>
*{box-sizing:border-box}body{margin:0;padding:24px;background:#1c1c1e;color:#f2f2f7;font:15px/1.4 -apple-system,BlinkMacSystemFont,"SF Pro Text","Segoe UI",Inter,Roboto,sans-serif;-webkit-font-smoothing:antialiased}
.wrap{display:grid;grid-template-columns:200px 1fr;gap:24px;max-width:860px}
.nav{background:#2c2c2e;border-radius:14px;padding:6px;align-self:start}.nav a{display:flex;align-items:center;min-height:44px;padding:0 14px;border-radius:10px;color:#f2f2f7;text-decoration:none}.nav a.on{background:#0a84ff}.nav a.dz{color:#ff453a}
h1{font-size:28px;margin:0 0 4px;letter-spacing:-.02em;display:flex;align-items:center;gap:10px}.sub{color:#8e8e93;font-size:13px;margin:0 0 20px}
.gt{font-size:12px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:#8e8e93;margin:20px 4px 8px}
.box{background:#2c2c2e;border-radius:14px;overflow:hidden}.r{display:flex;align-items:center;gap:12px;min-height:52px;padding:8px 16px;border-top:1px solid rgba(84,84,88,.55)}.r:first-child{border-top:0}
.g{flex:1}.k{color:#8e8e93;width:80px}.mono{font:13px ui-monospace,Menlo,monospace;background:#3a3a3c;padding:6px 10px;border-radius:8px;flex:1}
.ft{color:#8e8e93;font-size:13px;margin:8px 4px 0}
.btn{font:600 14px inherit;font-family:inherit;min-height:36px;padding:0 14px;border-radius:10px;border:0;background:#3a3a3c;color:#f2f2f7}.btn.p{background:#0a84ff}
.pill{display:inline-flex;align-items:center;min-height:24px;padding:0 10px;border-radius:99px;font-size:12px;font-weight:600;background:rgba(48,209,88,.16);color:#30d158}
.sw{width:51px;height:31px;border-radius:99px;background:#30d158;position:relative}.sw::after{content:"";position:absolute;top:2px;left:22px;width:27px;height:27px;border-radius:50%;background:#fff}
.dock{margin-top:20px;display:flex;align-items:center;gap:10px;padding:10px 12px 10px 18px;border-radius:14px;background:rgba(58,58,60,.75);backdrop-filter:blur(16px)}
</style></head><body><div class="wrap">
<nav class="nav"><a>General</a><a>Members</a><a class="on">Integrations</a><a>Resources</a><a class="dz">Danger zone</a></nav>
<main><h1>Billing Bot <span class="pill">Running</span></h1><p class="sub">Background agent · /hooks/billing-bot</p>
<div class="gt">Email</div><div class="box">
<div class="r"><span class="g">Send email</span><span class="sw"></span></div>
<div class="r"><span class="k">Server</span><span class="mono">smtp.example.com:25</span><span style="color:#8e8e93;font-size:13px">20/hour</span></div>
<div class="r"><span class="k">To</span><span class="mono">ops@example.com</span></div></div>
<p class="ft">Approved addresses only.</p>
<div class="dock"><span class="g">Unsaved changes</span><button class="btn">Reset</button><button class="btn p">Save</button></div>
</main></div></body></html>
```

```html example Do and don't | wide
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<style>
*{box-sizing:border-box}body{margin:0;padding:32px;background:#1c1c1e;color:#f2f2f7;font:15px/1.4 -apple-system,BlinkMacSystemFont,"SF Pro Text","Segoe UI",Inter,Roboto,sans-serif;-webkit-font-smoothing:antialiased}
.cols{display:grid;grid-template-columns:1fr 1fr;gap:24px}.h{font-weight:700;margin:0 0 10px;display:flex;gap:8px;align-items:center}.h i{font-style:normal;width:22px;height:22px;border-radius:50%;display:grid;place-items:center;font-size:13px}
.do i{background:#30d158;color:#000}.dont i{background:#ff453a;color:#fff}
.box{background:#2c2c2e;border-radius:14px;overflow:hidden}.r{display:flex;align-items:center;gap:12px;min-height:52px;padding:8px 16px;border-top:1px solid rgba(84,84,88,.55)}.r:first-child{border-top:0}
.g{flex:1}.m{color:#8e8e93;font-size:13px}.ch{color:#8e8e93;font-size:22px}
.bad{border:1px solid #48484a;border-radius:4px;padding:8px;margin-bottom:6px;font-size:12px}.bad p{margin:2px 0;color:#8e8e93;font-size:11px}.bad a{color:#0a84ff;font-size:11px}
.sw{width:51px;height:31px;border-radius:99px;background:#30d158;position:relative}.sw::after{content:"";position:absolute;top:2px;left:22px;width:27px;height:27px;border-radius:50%;background:#fff}
ul{margin:12px 0 0;padding-left:18px;color:#8e8e93;font-size:13px}li{margin:4px 0}
@media(max-width:640px){.cols{grid-template-columns:1fr}}
</style></head><body><div class="cols">
<div class="do"><p class="h"><i>✓</i>Do</p><div class="box">
<div class="r"><span class="g">Allow connections</span><span class="sw"></span></div>
<div class="r"><span class="g">Key <span class="m">vk_••••3f2a</span></span><span class="m">Change</span></div>
<div class="r"><span class="g">billing-bot</span><span class="ch">›</span></div></div>
<ul><li>52px rows, whole row clickable</li><li>Labels of one to three words</li><li>Switches, not checkboxes</li><li>One rounded group, hairline rows</li></ul></div>
<div class="dont"><p class="h"><i>✕</i>Don't</p>
<div class="bad"><label><input type="checkbox"> Allow containers belonging to this hub to establish a connection to the MCP server</label></div>
<div class="bad">API Key<p>This hub-wide key is used for built-in tools and is never exposed to agents running inside containers.</p><a href="#">edit</a></div>
<div class="bad">billing-bot <a href="#">view details</a></div>
<ul><li>Tiny text links as the only target</li><li>Sentences where a noun works</li><li>Every setting in its own bordered box</li><li>Explaining the screen instead of fixing it</li></ul></div>
</div></body></html>
```

## References

- [Anti-vibe-coded UI (ayeeye)](https://ayeeye.net/posts/claude-opus/anti-vibe-coded-ui) - the hub's baseline: choices made for the product, not defaults.
- [Apple Human Interface Guidelines: Layout](https://developer.apple.com/design/human-interface-guidelines/layout) - 44pt minimum hit targets, grouped lists.
- [Apple Human Interface Guidelines: Toggles](https://developer.apple.com/design/human-interface-guidelines/toggles) - switches for binary settings.
- [Apple Human Interface Guidelines: Color](https://developer.apple.com/design/human-interface-guidelines/color) - the dark system palette these tokens follow.
- [Fitts's law (Nielsen Norman Group)](https://www.nngroup.com/articles/fitts-law/) - why bigger, closer targets are faster.
- [Vercel's design philosophy (ayeeye)](https://ayeeye.net/posts/claude-opus/vercel-design-philosophy) - the restraint this borrows, at a bigger size.
