Input Group
One field with inset prefix or suffix text, icons, and trailing actions (reveal, clear, copy). The group owns the border and radius — addons are not separate boxes.
Wrap the group in a Field for the label. For a submit beside the field, put a sibling button in af-inline — do not join a filled button onto the input.
Prefix and suffix
Section titled “Prefix and suffix”<div class="af-field">
<label class="af-field__label" for="ig-amount">Amount</label>
<div class="af-input-group">
<span class="af-input-group-addon">$</span>
<input type="text" id="ig-amount" class="af-input" inputmode="decimal" placeholder="0.00" />
<span class="af-input-group-addon">USD</span>
</div>
</div>Icon prefix
Section titled “Icon prefix”Decorative icons go in af-input-group-addon with aria-hidden="true". The field label still names the control.
<div class="af-field">
<label class="af-field__label" for="ig-search">Search</label>
<div class="af-input-group">
<span class="af-input-group-addon" aria-hidden="true">
<svg viewBox="0 0 24 24" fill="currentColor">
<path d="M15.5 14h-.79l-.28-.27A6.47 6.47 0 0 0 16 9.5 6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z" />
</svg>
</span>
<input type="search" id="ig-search" class="af-input" placeholder="Find packages" />
</div>
</div>Trailing actions
Section titled “Trailing actions”af-input-group-action is a compact in-field control — reveal, clear, copy. Use type="button" so it does not submit. Icon-only actions need aria-label. Toggle password visibility and clear in your own JavaScript; this pattern is CSS-only.
<div class="af-field">
<label class="af-field__label" for="ig-password">Password</label>
<div class="af-input-group">
<input type="password" id="ig-password" class="af-input" placeholder="••••••••" autocomplete="current-password" />
<button type="button" class="af-input-group-action" aria-label="Show password" aria-pressed="false">
<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z" />
</svg>
</button>
</div>
</div>Short text actions work the same way:
<div class="af-field">
<label class="af-field__label" for="ig-copy">Share link</label>
<div class="af-input-group">
<input type="text" id="ig-copy" class="af-input" value="https://www.airframeui.com" readonly />
<button type="button" class="af-input-group-action">Copy</button>
</div>
</div>Reveal and clear are your JavaScript. Toggle type and keep aria-label / aria-pressed in sync:
button.addEventListener('click', () => {
const show = input.type === 'password';
input.type = show ? 'text' : 'password';
button.setAttribute('aria-pressed', String(show));
button.setAttribute('aria-label', show ? 'Hide password' : 'Show password');
});Sibling button
Section titled “Sibling button”A primary action next to the field is a sibling, not an addon. Add af-grow so the group fills the row instead of staying width: 100%.
<div class="af-inline">
<div class="af-input-group af-grow">
<input type="search" class="af-input" placeholder="Find packages" aria-label="Search" />
</div>
<button type="submit" class="af-btn">Search</button>
</div>