v1.2.0
  1. ds
  2. Components
  3. Input group

Input group

Prefixes, suffixes, search, steppers and secret fields.

Code
<label class="fld"><span>Webhook URL</span>
  <div class="inp-group"><span class="addon">https://</span><input class="inp" value="hooks.acme.dev/pipeline-events" /></div>
</label>
<label class="fld"><span>Bucket prefix</span>
  <div class="inp-group mono"><span class="addon">s3://acme-raw/</span><input class="inp" placeholder="orders/" /><span class="addon">/*.parquet</span></div>
</label>
<div class="two">
  <label class="fld"><span>Retention</span>
    <div class="inp-group"><input class="inp" type="number" value="30" /><select class="inp" aria-label="Unit"><option>days</option><option>weeks</option><option>months</option></select></div>
  </label>
  <label class="fld"><span>Memory limit</span>
    <div class="inp-group"><input class="inp" value="512" inputmode="numeric" /><span class="addon">MiB</span></div>
  </label>
</div>

One border around an input and the things that belong to it. Addons are mono on the sunk background, so a fixed prefix like https:// reads as part of the value but clearly not editable. The focus ring goes on the whole group.

Search

Code
<div class="inp-group" role="search"><svg class="i" viewBox="0 0 24 24" aria-hidden="true"><path d="m21 21-4.34-4.34"/><circle cx="11" cy="11" r="8"/></svg><input class="inp" type="search" placeholder="Search tables, pipelines and runs" aria-label="Search" /><kbd class="kbd">/</kbd></div>
<div class="inp-group lg"><svg class="i" viewBox="0 0 24 24" aria-hidden="true"><path d="m21 21-4.34-4.34"/><circle cx="11" cy="11" r="8"/></svg><input class="inp" type="search" placeholder="Filter 1,204 events" aria-label="Filter events" /><button class="btn icon" aria-label="Filters"><svg class="i" viewBox="0 0 24 24" aria-hidden="true"><path d="M10 20a1 1 0 0 0 .553.895l2 1A1 1 0 0 0 14 21v-7a2 2 0 0 1 .517-1.341L21.74 4.67A1 1 0 0 0 21 3H3a1 1 0 0 0-.742 1.67l7.225 7.989A2 2 0 0 1 10 14z"/></svg></button></div>

A leading icon in the faint colour and, where a shortcut opens it, the key on the right.

With buttons

Code
<label class="fld"><span>Invite by email</span>
  <div class="inp-group"><input class="inp" type="email" placeholder="name@acme.dev" /><button class="btn primary">Send invite</button></div>
</label>
<label class="fld"><span>Database password</span>
  <div class="inp-group mono"><input class="inp" id="pw" type="password" value="hunter2-but-longer" autocomplete="off" /><button class="btn icon" type="button" aria-label="Show password" aria-pressed="false" data-reveal="pw"><svg class="i" viewBox="0 0 24 24" aria-hidden="true"><path d="M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0"/><circle cx="12" cy="12" r="3"/></svg></button><button class="btn" type="button" data-copy="hunter2-but-longer"><svg class="i" viewBox="0 0 24 24" aria-hidden="true"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg><span>Copy</span></button></div>
</label>

Buttons inside a group lose their own border and sit behind a hairline. A primary button keeps its fill: it is the action. The reveal button is a toggle with aria-pressed.

Stepper

Code
<label class="fld"><span>Workers</span>
  <div class="inp-group stepper"><button class="btn icon" type="button" aria-label="Fewer workers" data-step="-1"><svg class="i" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12h14"/></svg></button><input class="inp" type="number" value="4" min="1" max="64" aria-label="Workers" /><button class="btn icon" type="button" aria-label="More workers" data-step="1"><svg class="i" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12h14"/><path d="M12 5v14"/></svg></button></div>
</label>
<label class="fld"><span>Parallelism</span>
  <div class="inp-group stepper"><button class="btn icon" type="button" aria-label="Decrease" data-step="-1"><svg class="i" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12h14"/></svg></button><input class="inp" type="number" value="16" min="1" max="128" aria-label="Parallelism" /><button class="btn icon" type="button" aria-label="Increase" data-step="1"><svg class="i" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12h14"/><path d="M12 5v14"/></svg></button></div>
</label>

For small whole numbers where people nudge more than they type. The input is still a real number field, so arrow keys and typing work. Past twenty steps, use a range.

States

Code
<label class="fld"><span>Subdomain</span>
  <div class="inp-group" aria-invalid="true"><input class="inp" value="acme prod" aria-invalid="true" /><span class="addon">.fin1te.app</span></div>
  <span class="err">Use letters, numbers and hyphens.</span>
</label>
<label class="fld"><span>Project id</span>
  <div class="inp-group mono"><span class="addon">id</span><input class="inp" value="prj_8c1f02" disabled /></div>
</label>

API

ClassOnWhat it does
.inp-groupdivThe frame. Children: .inp, .addon, select.inp, an icon, a .kbd, buttons.
.addonspanFixed text before or after the input. Mono on the sunk background.
.isvgA leading icon, faint.
.btnbuttonBorderless inside the group; .primary and .accent keep their fill.
.lg / .mono.inp-groupTaller, or mono text in the input.
.stepper.inp-groupMinus, a centred number, plus.
aria-invalid="true".inp-groupRed border. Put it on the input too.
Do
  • Units and fixed prefixes as addons, not in the placeholder.
  • One action button at most.
  • The label on the outside, in a .fld.
Don't
  • Addons that change on their own. If it is a choice, make it a select.
  • Two inputs in one group.
  • Icons that are only decoration.

Accessibility

  • The label wraps the group, so clicking the label focuses the input. Selects and buttons inside need their own aria-label.
  • Addon text is not part of the value. If a screen reader needs it, add it to the label: "Memory limit, in MiB".