Joined field: sui-input-group-addon segments, a native <input> or <select> and buttons share one border, in the order you slot them. The <input>, or any child with data-grow, takes the remaining width; addons and a select size to their content. Not form-associated: the native control inside posts itself. Label, help and error come from the shared shell.
import { Component } from "@angular/core";
import { SuiInputGroup } from "@shineui/angular";
@Component({
standalone: true,
imports: [SuiInputGroup],
template: `<sui-input-group label="Website" help="Public site." (suiBlur)="onChange($event)"><sui-input-group-addon>https://</sui-input-group-addon>
<input data-grow name="website" value="fallscitysupply.com"></sui-input-group>`,
})
export class Example {
onChange(e: CustomEvent) { console.log(e.detail); }
}
Basic
The group is not form-associated. It labels and paints the slotted control, wires aria-describedby onto it, and points the label at it, so clicking the label focuses the input.
A sui-button is a segment like any other: square, the height of the group, inside its border. Put it last for a search or an apply action. Focused, it draws its own ring inside the frame.
Any ShineUI field can be a segment. Give it data-grow and the group draws its edge, focus and fill, so a date picker or a select reads as part of one field. Name it with aria-label, since the label points at the first native control.
// setup(root): properties and events
setup(root) {
root.querySelector('#ig-owner').options = ['Davon Lane', 'Ava Chen', 'Marcus Reid'].map((n) => ({ value: n, label: n }));
}
With a select
A native select is a segment too: it takes the input skin and the chevron sui-select draws, and sizes to its longest option. The label points at the first control, so give a second one its own aria-label.
md is the default, one height with every select, picker and button. lg is the roomier group for a sign-in or marketing page; sm packs into table rows. The addons follow the group height.
The asterisk is drawn here; the constraint itself stays on the slotted input, which is what the form validates. An error replaces the help line under the whole group, never under one segment.
The name lives on the slotted input, not on the group, so each control posts one entry of its own. Disable the input as well as the group, or the grayed field keeps posting.
// setup(root): properties and events
setup(root) {
const form = root.querySelector('#ig-form');
const out = root.querySelector('#ig-out');
form.addEventListener('submit', (e) => {
e.preventDefault();
out.textContent = [...new FormData(form)].map(([k, v]) => `${k}=${v || '(empty)'}`).join(' ');
});
form.addEventListener('reset', () => {
out.textContent = 'Reset. Submit to see what posts.';
});
}
Events
The group is not form-associated and has no value events of its own; the slotted control keeps its native input and change. suiFocus and suiBlur fire as focus enters and leaves the group, a move to the button included. Focus in a list or calendar a control opened still counts as the control's.
Moves through the slotted controls in order: an addon that is text is skipped, a button or a select takes its own stop.
Printable charactersEnterSpace
Native: each slotted control keeps its own keys.
API
<sui-input-group>
Properties
Property
Attribute
Type
Default
Description
disabled
disabled
boolean
false
Paints the group disabled. Disable the slotted input too, or it keeps posting.
error
error
stringstring | undefined
Error message; paints the group border in the danger tone.
help
help
stringstring | undefined
Help text under the group. Replaced by error when set.
invalid
invalid
boolean
false
Danger border without a message.
label
label
stringstring | undefined
Field label above the group. Without one the slotted control takes the host's aria-label or aria-labelledby as its name, unless it carries its own; a visible label wins over both.
required
required
boolean
false
Draws the required asterisk beside the label. The constraint itself stays on the slotted control (<input required>), which is the element the form validates: this element is not form-associated, so it has no validity of its own.
size
size
"lg" | "md" | "sm"
'md'
Group height: sm 28 for table rows, md 34 for forms and toolbars (the default, shared with the select family and the buttons, so a form built from defaults is one height), lg 42 for a sign-in or marketing page. The addons follow it.
Events
Event
Detail
React
Vue
Description
suiBlur
void
onSuiBlur
@sui-blur
Fires when a control in the group loses focus, a move to the next control in the group included; no detail. Focus moving into the list or calendar a control opened is not a blur, wherever that panel sits: the group keeps its focus border, and fires once focus has left the control and its panel. Does not bubble.
suiFocus
void
onSuiFocus
@sui-focus
Fires when a control in the group takes focus, as the group paints its focus border; no detail. The same pair is on every text field. Focus coming back to a control from the list or calendar it opened is not a new focus. Does not bubble.
Slots
Slot
Description
(default)
The segments in order: sui-input-group-addon, <input>, <select>, sui-button.
<sui-input-group-addon>
Slots
Slot
Description
(default)
Addon content: text or an icon.
Theming
Every design token this element can render with, read from its own stylesheets and the ones it imports. Set a token on :root or on any ancestor and every use below follows it. The values shown are the ones in the theme you are viewing. The list is read per stylesheet, so an element that imports a shared drawing module lists that module's tokens too, whether or not it paints with all of them.