Open demo

CSS code

HTML
<div><span class="lbl">Currency prefix</span>
<div class="f">
<label for="g1">Monthly budget<span class="sr-only"> in pounds</span></label>
<div class="ig"><span class="addon">£</span><input id="g1" name="budget" type="text" inputmode="decimal" autocomplete="off" value="1,200"><span class="addon">.00</span></div>
</div>
</div>
<div><span class="lbl">Domain suffix</span>
<div class="f">
<label for="g2">Workspace</label>
<div class="ig"><input id="g2" name="workspace" type="text" autocomplete="off" autocapitalize="none" spellcheck="false" value="analytical-engines" aria-describedby="g2-suffix"><span class="addon" id="g2-suffix">.uicookies.com</span></div>
</div>
</div>
<div><span class="lbl">Attached button</span>
<div class="f">
<label for="g3">Discount code</label>
<div class="ig"><input id="g3" name="discount" type="text" autocomplete="off" autocapitalize="characters" placeholder="SUMMER26"><button type="button">Apply</button></div>
</div>
</div>
<div><span class="lbl">Split field</span>
<fieldset class="f">
<legend>Card details</legend>
<div class="split">
<div class="part sm"><label for="g4">Expiry</label><input id="g4" name="cc-exp" type="text" inputmode="numeric" autocomplete="cc-exp" placeholder="MM / YY"></div>
<div class="part sm"><label for="g5">Security code</label><input id="g5" name="cc-csc" type="text" inputmode="numeric" autocomplete="cc-csc" maxlength="4" placeholder="CVC"></div>
<div class="part"><label for="g6">Postcode</label><input id="g6" name="postcode" type="text" autocomplete="postal-code" placeholder="SW1A 1AA"></div>
</div>
</fieldset>
</div>
CSS
/* Input groups: an input joined to text add-ons or a button, and a split field grouped by a fieldset. */
.f{--f-text:#0f172a;--f-label:#334155;--f-muted:#475569;--f-bg:#fff;--f-addon:#f1f5f9;--f-border:#8492a6;
  --f-focus:#2563eb;--f-ring:#dbeafe;--f-radius:9px;--f-btn:#0f172a;display:flex;flex-direction:column;gap:7px;
  max-width:360px;min-width:0;margin:0;padding:0;border:0}
.f label,.f legend{font-size:.82rem;font-weight:600;color:var(--f-label)}
.f legend{padding:0;margin-bottom:7px}
.f input{box-sizing:border-box;width:100%;font:inherit;font-size:.92rem;padding:11px 14px;color:var(--f-text);
  background:var(--f-bg);border:1px solid var(--f-border);border-radius:var(--f-radius);transition:border-color .16s,box-shadow .16s}
.f input::placeholder{color:#64748b}
.f input:focus{position:relative;outline:0;border-color:var(--f-focus);box-shadow:0 0 0 3px var(--f-ring)}
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
/* joined group */
.ig{display:flex}
.ig input{flex:1;min-width:0;border-radius:0}
.ig .addon{display:grid;place-items:center;padding:0 14px;background:var(--f-addon);border:1px solid var(--f-border);
  font-size:.88rem;color:var(--f-muted);white-space:nowrap}
.ig .addon:first-child{border-right:0;border-radius:var(--f-radius) 0 0 var(--f-radius)}
.ig .addon:last-child{border-left:0;border-radius:0 var(--f-radius) var(--f-radius) 0}
.ig input:first-child{border-radius:var(--f-radius) 0 0 var(--f-radius)}
.ig input:last-child{border-radius:0 var(--f-radius) var(--f-radius) 0}
.ig button{font:inherit;font-size:.88rem;font-weight:600;padding:0 20px;border:0;background:var(--f-btn);color:#fff;
  border-radius:0 var(--f-radius) var(--f-radius) 0;cursor:pointer;white-space:nowrap;transition:opacity .16s}
.ig button:hover{opacity:.88}
.ig button:focus-visible{outline:2px solid var(--f-focus);outline-offset:2px}
/* split field: one fieldset, a small visible label on each part */
.split{display:flex;gap:9px}
.split .part{display:flex;flex-direction:column;gap:5px;flex:1;min-width:0}
.split .part.sm{flex:none;width:110px}
.split .part label{font-size:.76rem;font-weight:500;color:var(--f-muted)}
@media (prefers-reduced-motion:reduce){.f input,.ig button{transition:none}}

Bootstrap 5 code

Requires: Bootstrap 5.3.8 CSS

HTML
<div><span class="lbl">Currency prefix</span>
<div class="fx">
<label for="g1" class="form-label">Monthly budget<span class="visually-hidden"> in pounds</span></label>
<div class="input-group"><span class="input-group-text">£</span><input id="g1" name="budget" class="form-control" type="text" inputmode="decimal" autocomplete="off" value="1,200"><span class="input-group-text">.00</span></div>
</div>
</div>
<div><span class="lbl">Domain suffix</span>
<div class="fx">
<label for="g2" class="form-label">Workspace</label>
<div class="input-group"><input id="g2" name="workspace" class="form-control" type="text" autocomplete="off" autocapitalize="none" spellcheck="false" value="analytical-engines" aria-describedby="g2-suffix"><span class="input-group-text" id="g2-suffix">.uicookies.com</span></div>
</div>
</div>
<div><span class="lbl">Attached button</span>
<div class="fx">
<label for="g3" class="form-label">Discount code</label>
<div class="input-group"><input id="g3" name="discount" class="form-control" type="text" autocomplete="off" autocapitalize="characters" placeholder="SUMMER26"><button class="btn" type="button">Apply</button></div>
</div>
</div>
<div><span class="lbl">Split field</span>
<fieldset class="fx">
<legend class="form-label">Card details</legend>
<div class="split">
<div class="part sm"><label for="g4" class="form-label">Expiry</label><input id="g4" name="cc-exp" class="form-control" type="text" inputmode="numeric" autocomplete="cc-exp" placeholder="MM / YY"></div>
<div class="part sm"><label for="g5" class="form-label">Security code</label><input id="g5" name="cc-csc" class="form-control" type="text" inputmode="numeric" autocomplete="cc-csc" maxlength="4" placeholder="CVC"></div>
<div class="part"><label for="g6" class="form-label">Postcode</label><input id="g6" name="postcode" class="form-control" type="text" autocomplete="postal-code" placeholder="SW1A 1AA"></div>
</div>
</fieldset>
</div>
CSS
/* Bootstrap .input-group with .input-group-text and .btn, plus a fieldset for the split field,
   restyled through --bs-* variables on .fx. */
.fx{--bs-body-color:#0f172a;--bs-body-bg:#fff;--bs-border-color:#8492a6;--bs-border-radius:9px;
  --bs-tertiary-bg:#f1f5f9;--bs-secondary-color:#64748b;--f-label:#334155;--f-muted:#475569;--f-focus:#2563eb;
  --f-ring:#dbeafe;max-width:360px;min-width:0}
.fx .form-label{display:block;float:none;width:auto;margin-bottom:7px;font-size:.82rem;font-weight:600;line-height:normal;color:var(--f-label)}
.fx .form-control{padding:11px 14px;font-size:.92rem;line-height:normal;transition:border-color .16s,box-shadow .16s}
.fx .form-control:focus{border-color:var(--f-focus);box-shadow:0 0 0 3px var(--f-ring)}
.fx .input-group-text{padding:0 14px;font-size:.88rem;line-height:normal;color:var(--f-muted)}
.fx .input-group>.btn{--bs-btn-padding-x:20px;--bs-btn-font-size:.88rem;--bs-btn-font-weight:600;--bs-btn-bg:#0f172a;
  --bs-btn-border-color:#0f172a;--bs-btn-hover-bg:#27303f;--bs-btn-hover-border-color:#27303f;--bs-btn-active-bg:#0f172a;
  --bs-btn-color:#fff;--bs-btn-hover-color:#fff;--bs-btn-active-color:#fff;--bs-btn-focus-box-shadow:none}
.fx .input-group>.btn:focus-visible{background-color:var(--bs-btn-bg);outline:2px solid var(--f-focus);outline-offset:2px}
/* split field */
.fx .split{display:flex;gap:9px}
.fx .part{display:flex;flex-direction:column;flex:1;min-width:0}
.fx .part.sm{flex:none;width:110px}
.fx .part .form-label{margin-bottom:5px;font-size:.76rem;font-weight:500;color:var(--f-muted)}
@media (prefers-reduced-motion:reduce){.fx .form-control,.fx .btn{transition:none}}

Tailwind 4 code

Requires: Tailwind CSS 4

HTML
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#64748b] uppercase">Currency prefix</span>
<div class="flex max-w-[360px] flex-col gap-[7px]">
<label for="g1" class="text-[.82rem] font-semibold text-[#334155]">Monthly budget<span class="sr-only"> in pounds</span></label>
<div class="flex"><span class="grid place-items-center rounded-l-[9px] border border-r-0 border-[#8492a6] bg-[#f1f5f9] px-[14px] text-[.88rem] whitespace-nowrap text-[#475569]">£</span><input id="g1" name="budget" type="text" inputmode="decimal" autocomplete="off" value="1,200" class="min-w-0 flex-1 border border-[#8492a6] bg-white px-[14px] py-[11px] text-[.92rem] text-[#0f172a] transition-[border-color,box-shadow] duration-[160ms] focus:relative focus:border-[#2563eb] focus:shadow-[0_0_0_3px_#dbeafe] focus:outline-none motion-reduce:transition-none"><span class="grid place-items-center rounded-r-[9px] border border-l-0 border-[#8492a6] bg-[#f1f5f9] px-[14px] text-[.88rem] whitespace-nowrap text-[#475569]">.00</span></div>
</div>
</div>
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#64748b] uppercase">Domain suffix</span>
<div class="flex max-w-[360px] flex-col gap-[7px]">
<label for="g2" class="text-[.82rem] font-semibold text-[#334155]">Workspace</label>
<div class="flex"><input id="g2" name="workspace" type="text" autocomplete="off" autocapitalize="none" spellcheck="false" value="analytical-engines" aria-describedby="g2-suffix" class="min-w-0 flex-1 rounded-l-[9px] border border-[#8492a6] bg-white px-[14px] py-[11px] text-[.92rem] text-[#0f172a] transition-[border-color,box-shadow] duration-[160ms] focus:relative focus:border-[#2563eb] focus:shadow-[0_0_0_3px_#dbeafe] focus:outline-none motion-reduce:transition-none"><span id="g2-suffix" class="grid place-items-center rounded-r-[9px] border border-l-0 border-[#8492a6] bg-[#f1f5f9] px-[14px] text-[.88rem] whitespace-nowrap text-[#475569]">.uicookies.com</span></div>
</div>
</div>
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#64748b] uppercase">Attached button</span>
<div class="flex max-w-[360px] flex-col gap-[7px]">
<label for="g3" class="text-[.82rem] font-semibold text-[#334155]">Discount code</label>
<div class="flex"><input id="g3" name="discount" type="text" autocomplete="off" autocapitalize="characters" placeholder="SUMMER26" class="min-w-0 flex-1 rounded-l-[9px] border border-[#8492a6] bg-white px-[14px] py-[11px] text-[.92rem] text-[#0f172a] placeholder:text-[#64748b] transition-[border-color,box-shadow] duration-[160ms] focus:relative focus:border-[#2563eb] focus:shadow-[0_0_0_3px_#dbeafe] focus:outline-none motion-reduce:transition-none"><button type="button" class="cursor-pointer rounded-r-[9px] bg-[#0f172a] px-5 text-[.88rem] font-semibold whitespace-nowrap text-white transition-opacity duration-[160ms] hover:opacity-[.88] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[#2563eb] motion-reduce:transition-none">Apply</button></div>
</div>
</div>
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#64748b] uppercase">Split field</span>
<fieldset class="flex max-w-[360px] min-w-0 flex-col">
<legend class="mb-[7px] text-[.82rem] font-semibold text-[#334155]">Card details</legend>
<div class="flex gap-[9px]">
<div class="flex w-[110px] flex-none flex-col gap-[5px]"><label for="g4" class="text-[.76rem] font-medium text-[#475569]">Expiry</label><input id="g4" name="cc-exp" type="text" inputmode="numeric" autocomplete="cc-exp" placeholder="MM / YY" class="w-full rounded-[9px] border border-[#8492a6] bg-white px-[14px] py-[11px] text-[.92rem] text-[#0f172a] placeholder:text-[#64748b] transition-[border-color,box-shadow] duration-[160ms] focus:border-[#2563eb] focus:shadow-[0_0_0_3px_#dbeafe] focus:outline-none motion-reduce:transition-none"></div>
<div class="flex w-[110px] flex-none flex-col gap-[5px]"><label for="g5" class="text-[.76rem] font-medium text-[#475569]">Security code</label><input id="g5" name="cc-csc" type="text" inputmode="numeric" autocomplete="cc-csc" maxlength="4" placeholder="CVC" class="w-full rounded-[9px] border border-[#8492a6] bg-white px-[14px] py-[11px] text-[.92rem] text-[#0f172a] placeholder:text-[#64748b] transition-[border-color,box-shadow] duration-[160ms] focus:border-[#2563eb] focus:shadow-[0_0_0_3px_#dbeafe] focus:outline-none motion-reduce:transition-none"></div>
<div class="flex min-w-0 flex-1 flex-col gap-[5px]"><label for="g6" class="text-[.76rem] font-medium text-[#475569]">Postcode</label><input id="g6" name="postcode" type="text" autocomplete="postal-code" placeholder="SW1A 1AA" class="w-full rounded-[9px] border border-[#8492a6] bg-white px-[14px] py-[11px] text-[.92rem] text-[#0f172a] placeholder:text-[#64748b] transition-[border-color,box-shadow] duration-[160ms] focus:border-[#2563eb] focus:shadow-[0_0_0_3px_#dbeafe] focus:outline-none motion-reduce:transition-none"></div>
</div>
</fieldset>
</div>

About this set

Fields joined to something else, with the borders merged so each group reads as one control. Currency prefix puts a pound sign before the amount and .00 after it, on a tinted add-on; the label carries visually hidden text so screen readers hear the currency too. Domain suffix appends .uicookies.com to a workspace name, and the suffix is linked to the input with aria-describedby so it is read out. Attached button puts an Apply button flush against a discount code field, for short one-step actions. The split field groups card expiry, security code and postcode in a fieldset with a Card details legend, and gives each part its own small visible label rather than relying on placeholders that vanish while typing. The demo fields use inputmode and autocomplete values (cc-exp, cc-csc, postal-code) so phones show a number pad and browsers can fill card details. The plain CSS version is a flex row with the inner corners squared; the Bootstrap version is the stock .input-group with .input-group-text and .btn; the Tailwind version uses rounded-l and rounded-r with border-l-0 and border-r-0.

What’s included

  • Currency prefix, domain suffix, attached button and split field
  • Split field grouped with fieldset and legend, with a label on every part
  • Suffix text linked to its input with aria-describedby
  • inputmode and autocomplete set for card and postcode fields
  • Bootstrap version uses .input-group, .input-group-text and .btn

Accessibility

Every input has a visible label; the split field's three parts sit in a fieldset whose legend names the group. The domain suffix is read as the input's description, and the currency is repeated in visually hidden label text. Borders reach 3:1 and add-on text 4.5:1. Inputs show a blue border and 3px ring on focus, and the button shows a 2px blue outline on keyboard focus. Transitions are removed under prefers-reduced-motion.

Customise it

In plain CSS change --f-border, --f-addon, --f-muted, --f-btn and --f-radius on .f. In Bootstrap set --bs-border-color, --bs-tertiary-bg and --bs-border-radius on .fx and the --bs-btn-* variables on the button; in Tailwind edit the border-, bg- and rounded- classes.