Input Group
One bordered field made of a control and its additions: icons, prefixes, shortcuts, and buttons. It powers search boxes, the workspace URL field, combobox inputs, picker searches, and the chat composer.
Sizes and inline additions
Default is 32px with 14px text, for toolbars and lists. Large is 44px with 16px text, for onboarding forms. Additions sit before or after the input.
default · leading icon · trailing shortcut
lg · leading icon · trailing shortcut
Search with a clear button
Chat and list searches put a magnifier first and an icon-xs clear button last. Clear empties the text and puts focus back in the input.
- Q3 board deck review
- Northwind renewal terms
- Hiring plan for support
- Weekly metrics digest
Chats · 14px icon · 640 characters
No agents match. Clear the search to see all.
Search field · long text · 120 characters
Workspace URL
Large group with a text prefix. Invalid input turns the whole group red, and the field is locked while the workspace saves.
Use 3 to 48 lowercase letters, numbers, or hyphens.
Invalid · aria-invalid on the input
Disabled while saving
Button sizes
xs and sm fit a short label; icon-xs and icon-sm are square. Buttons are ghost and type button unless you say otherwise.
xs · ghost · label
sm · secondary · icon and label
icon-xs · ghost
lg · icon-sm · outline
No button pressed yet.
Combobox input
A Base UI Combobox renders its input and buttons through the group. The trigger and clear buttons are icon-xs; once there is a choice the clear button replaces the trigger.
Trigger · type or press ↓ to open
Trigger and clear · chosen value
Disabled
Command search
Pickers put a quiet, filled group at the top of their list. The input is any element; the leading icon can follow it in markup.
Message composer
The composer variant stacks attachments, the message, and a toolbar. Toolbar buttons are sm with a label or icon-sm; send turns from secondary to default once there is text.
Question composer and custom controls
While an agent waits on a question, the composer border goes quiet. Any element marked data-slot input-group-control, like this rich text box, drives the group's focus and invalid states.
The agent asked: Which region should the Q3 report cover?
question-composer · rich text control
composer · disabled while read-only
Block additions
Block additions span a row above or below a textarea. Here a label sits on top and an icon-xs button and an sm default button sit underneath.
Nothing has been sent.
Usage
import {
InputGroup,
InputGroupAddon,
InputGroupButton,
InputGroupInput,
InputGroupText,
InputGroupTextarea,
} from "@tulipfarm/sprout/input-group";
// Search with a clear button
<InputGroup>
<InputGroupAddon><Search3 size={14} aria-hidden="true" /></InputGroupAddon>
<InputGroupInput ref={input} aria-label="Search chat titles" placeholder="Search chat titles"
maxLength={640} value={query} onChange={event => setQuery(event.target.value)} />
<InputGroupAddon align="inline-end">
{query && (
<InputGroupButton size="icon-xs" aria-label="Clear search"
onClick={() => { setQuery(""); input.current?.focus(); }}>
<X aria-hidden="true" />
</InputGroupButton>
)}
</InputGroupAddon>
</InputGroup>
// Large, with a text prefix
<InputGroup size="lg">
<InputGroupAddon><InputGroupText>app.tulipfarm.ai/</InputGroupText></InputGroupAddon>
<InputGroupInput id="workspace-slug" required maxLength={48}
aria-invalid={Boolean(error)} aria-describedby="workspace-slug-hint" />
</InputGroup>
// Composer
<InputGroup variant={questionPending ? "question-composer" : "composer"}>
<InputGroupAddon align="block-start">{attachments}</InputGroupAddon>
<InputGroupTextarea aria-label="Message" placeholder="Ask anything…" />
<InputGroupAddon align="block-end" className="border-t">
<DropdownMenuTrigger render={<InputGroupButton size="sm" aria-label="Add a file" />}>
<Paperclip aria-hidden="true" /> Files
</DropdownMenuTrigger>
<InputGroupButton type="submit" size="icon-sm"
variant={canSend ? "default" : "secondary"} aria-label="Send message">
<ArrowUp aria-hidden="true" />
</InputGroupButton>
</InputGroupAddon>
</InputGroup>
// Base UI Combobox renders through the group parts
<InputGroup>
<Combobox.Input render={<InputGroupInput />} aria-label="Choose a fruit" />
<InputGroupAddon align="inline-end">
<InputGroupButton size="icon-xs" render={<Combobox.Trigger />} aria-label="Show fruits">
<ChevronDown aria-hidden="true" />
</InputGroupButton>
<Combobox.Clear render={<InputGroupButton size="icon-xs" />} aria-label="Clear selection">
<X aria-hidden="true" />
</Combobox.Clear>
</InputGroupAddon>
</InputGroup>Props
InputGroup takes size: default(32px, 14px text) or lg (44px, 16px text), andvariant: default, composer (a stacked message box with a stronger border and panel radius), orquestion-composer (the same box with a quieter border while an agent waits on an answer). It is a div withrole="group" and takes every div prop.
InputGroupAddon takes align:inline-start (default), inline-end,block-start, or block-end. It can come before or after the control in markup. Block additions turn the group into a column; add border-b or border-t for a divider row.
InputGroupButton is a Sprõut Button withsizexs (default), sm,icon-xs, or icon-sm, any Buttonvariant (default ghost), andtype="button" unless set. Use render to make it a menu, popover, tooltip, or combobox trigger.
InputGroupInput and InputGroupTextarea are SprõutInput and Textarea without their own border and fill. They take every native prop and read a surroundingField. InputGroupText is muted text for prefixes, units, and shortcuts.
Behavior
The group draws the border, focus ring, and invalid and disabled states for any element marked data-slot="input-group-control".InputGroupInput and InputGroupTextarea set it; add it to a rich text editor to get the same states. A control without it, like a command list's search input, keeps the group quiet.
Clicking an addon anywhere except a button focuses the control, like a label. Inside a combobox popup the group skips its focus ring, since the list shows where focus is. In the composer, toolbar buttons grow to 44px on touch screens.
Accessibility
Name the control with a label or aria-label, and name icon-only buttons with aria-label. Icons in addons should be aria-hidden. Mark errors witharia-invalid and point aria-describedby at the message. Keyboard focus shows a 2px ring around the whole group; the composer swaps it for a focus-colored border.
Border, fill, and ring changes take 120ms and stop when reduced motion is requested. In forced colors mode the group keeps a system border and a highlight focus ring.
Theme tokens
The border uses --sprout-input (composer:--sprout-border), the fill --sprout-surface, and addon text --sprout-muted. Focus uses--sprout-focus, errors --sprout-danger, and disabled --sprout-hover. Radius follows--sprout-radius-control, or--sprout-radius-panel for composers. The opt-in TulipFarm theme tints the focus ring and send button through these tokens.