Input group
Wrap a .re-input in .re-input-group to attach prefix/suffix affixes,
inline action buttons, or a trailing button — all reading as a single control.
The group owns the border and the focus ring (via :focus-within); the inner
input’s own frame is stripped so there’s no double outline. Size follows the
inner input’s data-size.
Affixes
Section titled “Affixes”.re-input-group__text is a tinted, non-interactive prefix or suffix — units,
currency, a protocol, an @.
<label class="re-field">
<span class="re-field__label">Website</span>
<span class="re-input-group">
<span class="re-input-group__text">https://</span>
<input class="re-input" type="text" placeholder="example" />
<span class="re-input-group__text">.com</span>
</span>
</label>
<label class="re-field">
<span class="re-field__label">Price</span>
<span class="re-input-group">
<span class="re-input-group__text">$</span>
<input class="re-input" type="number" inputmode="decimal" placeholder="0.00" />
<span class="re-input-group__text">USD</span>
</span>
</label> Attached button
Section titled “Attached button”Drop a .re-button in the group as a trailing control; its corners are
clipped to the group.
<span class="re-input-group" role="search">
<input class="re-input" type="search" aria-label="Search" placeholder="Search…" />
<button class="re-button" type="submit">Search</button>
</span> States
Section titled “States”Disabled and invalid are reflected from the contained control (:disabled,
aria-invalid / :user-invalid).
<label class="re-field">
<span class="re-field__label">Disabled</span>
<span class="re-input-group">
<span class="re-input-group__text">@</span>
<input class="re-input" type="text" value="handle" disabled />
</span>
</label>
<label class="re-field">
<span class="re-field__label">Invalid</span>
<span class="re-input-group">
<span class="re-input-group__text">@</span>
<input class="re-input" type="text" value="bad value" aria-invalid="true" />
</span>
</label> Accessibility
Section titled “Accessibility”The input group is CSS-only — a passive flex wrapper with no behavior of its own. Keyboard, focus order, and announcements all come from the native controls you place inside it; the group only relocates the field’s frame and focus ring.
- Keyboard — native.
Tabreaches the inner.re-input(type to edit) and any attached.re-button(Enter/Spaceto activate) in source order. The.re-input-group__textaffix is non-interactive (user-select: none) and takes no focus. - Focus — the group, not the field, shows the ring:
:focus-withinrecolours its border to--re-color-focus-ringand paints--re-shadow-focusoutside the border box, while the inner input’s own:focus-visiblering is stripped so there’s no double outline. An optional.re-input-group__actionicon button carries its own inset:focus-visiblering so it stays visible against the clipped, rounded group. - Semantics — associate a label by wrapping the group in
<label class="re-field">with a.re-field__labelspan (as the affix and state demos do); a standalone group such as the search example labels its input directly witharia-label. Affix text is real visible text, so it is read in reading order — not hidden. Setaria-invalid="true"on the inner input to recolour the group’s border and ring to--re-color-danger-border/--re-color-danger-500, announced as invalid;:user-invalidstyles the same way after the user has interacted. The search demo marks the grouprole="search"so it’s exposed as a search landmark. - Notes — invalid and disabled tinting is reflected with
:has(), purely cosmetic: on engines without:has()the styling is skipped but the contained control and its native:disabled/aria-invalidstate are unaffected. See the field, input, and button pages and the accessibility guide.