7.8 KiB
InputGroup
(no submodules)
- input-group.variants — `inputGroupVariants`, `input…-
input-group.variants —
inputGroupVariants,inputGroupAddonVariants,inputGroupInputVariants,inputGroupTextareaVariants, and `inputGro… -
input-group.variants —
inputGroupVariants, `input… -
input-group.variants —
inputGroupVariants,inputGroupAddonVariants,inputGroupInputVariants,inputGroupTextareaVariants, and `inputGro… -
input-group.variants —
inputGroupVariants, `input… -
input-group.variants —
inputGroupVariants,inputGroupAddonVariants,inputGroupInputVariants,inputGroupTextareaVariants, and `inputGro… -
input-group.variants —
inputGroupVariants, `input… -
input-group.variants —
inputGroupVariants,inputGroupAddonVariants,inputGroupInputVariants,inputGroupTextareaVariants, and `inputGro… -
input-group.variants —
inputGroupVariants,inputGroupAddonVariants,inputGroupInputVariants,inputGroupTextareaVariants, and `inputGro…
Purpose
<InputGroup> is the chassis container that wraps an input alongside leading/trailing addons (icons, prefix/suffix text, inline buttons). It hoists the focus ring and invalid border onto the wrapper itself so the inner control can drop its own ring; the dedicated <InputGroupInput> and <InputGroupTextarea> primitives strip the standard border / shadow / focus ring so the wrapper carries them.
Per the shadcn skill rule, NEVER drop a raw <Input> or <Textarea> inside <InputGroup> — the inner control needs the wrapped surface variants to remove its own border and focus ring. The chassis provides <InputGroupInput> and <InputGroupTextarea> for the input case. Inline buttons inside inputs use <InputGroupAddon position="none"> + <InputGroupButton> (renders an icon-button without a separator border).
Features
- Wrapper container shares focus ring + invalid border with every child via
focus-within:anddata-[invalid=true]:. <InputGroupAddon>with three positions (start/end/none) controlling which separator border is drawn.<InputGroupInput>strips the standard<Input>border / shadow / focus ring.<InputGroupTextarea>strips the standard<Textarea>border / shadow / focus ring.<InputGroupButton>for inline icon-buttons (toggle password visibility, clear input, attach file).placeholderon<InputGroupInput>/<InputGroupTextarea>typed asTranslationKey | undefinedand resolved via active i18n.data-invalid="true"anddata-disabled="true"on the wrapper drive cva selectors.- Refs forwarded on every primitive.
motion-reduce:transition-nonecollapses transitions forprefers-reduced-motion: reduce.- Theme-driven variant overrides via
useThemedVariants("InputGroup" | "InputGroupAddon" | "InputGroupInput" | "InputGroupTextarea" | "InputGroupButton").
Usage: Basic Example
import {
InputGroup,
InputGroupAddon,
InputGroupInput,
} from "@suaveplan/react-ui";
export function EmailField() {
return (
<InputGroup>
<InputGroupAddon>@</InputGroupAddon>
<InputGroupInput type="email" />
</InputGroup>
);
}
Usage: Advanced Example
import {
InputGroup,
InputGroupAddon,
InputGroupButton,
InputGroupInput,
} from "@suaveplan/react-ui";
import { EyeIcon, EyeOffIcon } from "lucide-react";
import { useState } from "react";
export function PasswordWithReveal() {
const [show, setShow] = useState(false);
const [pwd, setPwd] = useState("");
const tooShort = pwd.length > 0 && pwd.length < 8;
return (
<InputGroup invalid={tooShort}>
<InputGroupAddon>Pwd</InputGroupAddon>
<InputGroupInput
type={show ? "text" : "password"}
value={pwd}
aria-invalid={tooShort}
onChange={(e) => setPwd(e.currentTarget.value)}
/>
<InputGroupAddon position="none">
<InputGroupButton
aria-label={show ? "Hide password" : "Show password"}
onClick={() => setShow((s) => !s)}
>
{show ? <EyeOffIcon /> : <EyeIcon />}
</InputGroupButton>
</InputGroupAddon>
</InputGroup>
);
}
API Reference
<InputGroup>
forwardRef<HTMLDivElement, InputGroupProps> — renders a <div> wrapper.
InputGroupProps
| Prop | Type | Default | Description |
|---|---|---|---|
invalid |
boolean |
false |
Drives data-invalid="true" on the wrapper so border + focus ring flip to destructive. |
disabled |
boolean |
false |
Drives data-disabled="true" for opacity decay. Pass to inner <InputGroupInput> separately for native semantics. |
all native HTMLAttributes<HTMLDivElement> |
native | native | Forwarded as-is. |
<InputGroupAddon>
forwardRef<HTMLDivElement, InputGroupAddonProps> — renders a <div> for decorative content.
InputGroupAddonProps
| Prop | Type | Default | Description |
|---|---|---|---|
position |
"start" | "end" | "none" |
"start" |
Which side the addon hugs and which separator border is drawn (none = no separator, used for inline buttons). |
<InputGroupInput>
forwardRef<HTMLInputElement, InputGroupInputProps> — renders an <input> with the standard border / shadow / focus ring stripped.
InputGroupInputProps
Inherits every prop from InputHTMLAttributes<HTMLInputElement> minus size / placeholder. Adds:
| Prop | Type | Default | Description |
|---|---|---|---|
placeholder |
TranslationKey |
none | Resolved via active i18n. |
<InputGroupTextarea>
forwardRef<HTMLTextAreaElement, InputGroupTextareaProps> — renders a <textarea> with the standard border / shadow / focus ring stripped.
InputGroupTextareaProps
Inherits every prop from TextareaHTMLAttributes<HTMLTextAreaElement> minus placeholder. Adds:
| Prop | Type | Default | Description |
|---|---|---|---|
placeholder |
TranslationKey |
none | Resolved via active i18n. |
<InputGroupButton>
forwardRef<HTMLButtonElement, InputGroupButtonProps> — renders a small icon-button with type="button" by default so it does not accidentally submit the surrounding form.
InputGroupButtonProps
Inherits every prop from ButtonHTMLAttributes<HTMLButtonElement>. The default type="button" can be overridden if intentional.
Cva resolvers
inputGroupVariants, inputGroupAddonVariants, inputGroupInputVariants, inputGroupTextareaVariants, and inputGroupButtonVariants are all exported as bare cva resolvers for external composition.
Implementation Notes
- Per the shadcn skill rule, the chassis enforces the
<InputGroupInput>/<InputGroupTextarea>wrappers — raw<Input>/<Textarea>inside<InputGroup>carries an inner border + focus ring that double up with the wrapper's own. - The
position="none"value on<InputGroupAddon>is the supported way to render an inline button without a separator border. The button supplies its own padding so the addon does not need one. <InputGroupButton>defaults totype="button"so a button rendered inside an input group does not submit the surrounding form when clicked. Override withtype="submit"only when intentional.- The wrapper carries the focus ring via
focus-within:so any inner element gaining focus surfaces the ring on the wrapper. The inner<InputGroupInput>removes its own ring viafocus:ring-0to avoid the visual double-ring. - Refs target the underlying DOM element on every primitive — consumers can
.focus()the input, button, addon, or wrapper programmatically.