<script setup>
import { Link2, Search } from "@lucide/vue";
import { ref } from "vue";
import { InputGroup } from "@dicehub/kappa/components/input-group";
const endpoint = ref("");
</script>
<template>
<InputGroup aria-label="Repository endpoint">
<InputGroup.Input
v-model="endpoint"
aria-label="Repository endpoint"
placeholder="repository.example"
type="url"
/>
<InputGroup.Addon align="inline-start">
<InputGroup.Text><Link2 aria-hidden="true" /> https://</InputGroup.Text>
</InputGroup.Addon>
<InputGroup.Addon align="inline-end">
<InputGroup.Button aria-label="Inspect endpoint" shape="square">
<Search aria-hidden="true" />
</InputGroup.Button>
</InputGroup.Addon>
</InputGroup>
</template>Installation
Input Group is part of the main Kappa package. It uses native input and textarea elements, the Kappa Button primitive, and colocated CSS. It adds no state-machine dependency.
Barrel
import { InputGroup } from "@dicehub/kappa";Granular
import { InputGroup } from "@dicehub/kappa/components/input-group";Usage
Place the input or textarea first in DOM order. Place each addon after it, then usealign to position the addon. Give every native control its own accessible label; the group label does not replace the control label.
<script setup>
import { Search } from "@lucide/vue";
import { InputGroup } from "@dicehub/kappa/components/input-group";
</script>
<template>
<InputGroup aria-label="Search runs">
<InputGroup.Input aria-label="Search runs" placeholder="Search runs..." />
<InputGroup.Addon align="inline-start">
<Search aria-hidden="true" />
</InputGroup.Addon>
</InputGroup>
</template>Composition
InputGroup.Root
├── InputGroup.Input / InputGroup.Textarea
├── InputGroup.Addon
│ └── InputGroup.Text
└── InputGroup.Button (optional action)Input Group combines a control, logical addons, helper text, and related buttons. Ark UI has no matching Vue Input Group primitive, so Kappa owns the native composition and preserves browser input behavior. The base button size supports icons and text. InputGroupButton composes the existing Kappa Button for consistent variants.
- Use
InputGroup.Inputfor a one-line native input andInputGroup.Textareafor multiline content. - Use
InputGroup.Addonfor icons, text, or related actions. Use logicalinline-*andblock-*alignment. - Keep an addon after its control in DOM order. This preserves the focus and reading order while CSS handles placement.
- Use
InputGroup.Buttononly for an action. Icon-only buttons needaria-labeloraria-labelledby.
Examples
Alignment
Use logical alignment values. Keep every addon after the control in DOM order; Kappa moves it visually.
<script setup>
import { FileCode2 } from "@lucide/vue";
import { InputGroup } from "@dicehub/kappa/components/input-group";
</script>
<template>
<InputGroup aria-label="Request path">
<InputGroup.Input aria-label="Request path" placeholder="/runs" />
<InputGroup.Addon align="block-start">
<FileCode2 aria-hidden="true" /> Request path
</InputGroup.Addon>
</InputGroup>
</template>Text
Use InputGroup.Text for units, prefixes, suffixes, and short helper content.
<script setup>
import { InputGroup } from "@dicehub/kappa/components/input-group";
</script>
<template>
<InputGroup aria-label="Amount">
<InputGroup.Input aria-label="Amount" placeholder="0.00" />
<InputGroup.Addon align="inline-start">
<InputGroup.Text>$</InputGroup.Text>
</InputGroup.Addon>
<InputGroup.Addon align="inline-end">
<InputGroup.Text>USD</InputGroup.Text>
</InputGroup.Addon>
</InputGroup>
</template>Button
Use a quiet Kappa Button for a related action such as reveal, copy, or search.
<script setup>
import { Copy } from "@lucide/vue";
import { InputGroup } from "@dicehub/kappa/components/input-group";
</script>
<template>
<InputGroup aria-label="Access token">
<InputGroup.Input aria-label="Access token" readonly value="••••••••" />
<InputGroup.Addon align="inline-end">
<InputGroup.Button aria-label="Copy access token" shape="square">
<Copy aria-hidden="true" />
</InputGroup.Button>
</InputGroup.Addon>
</InputGroup>
</template>Textarea
Use the block-end addon for counts and submit actions without hiding the text area.
<script setup>
import { SendHorizontal } from "@lucide/vue";
import { InputGroup } from "@dicehub/kappa/components/input-group";
</script>
<template>
<InputGroup aria-label="Release note">
<InputGroup.Textarea
aria-label="Release note"
placeholder="Describe the solver change..."
rows="3"
/>
<InputGroup.Addon align="block-end">
<InputGroup.Text>0/280</InputGroup.Text>
<InputGroup.Button aria-label="Post release note" shape="square">
<SendHorizontal aria-hidden="true" />
</InputGroup.Button>
</InputGroup.Addon>
</InputGroup>
</template>States
Complete the group state with invalid, disabled, and loading controls.
<script setup>
import { InputGroup } from "@dicehub/kappa/components/input-group";
</script>
<template>
<InputGroup invalid aria-label="Invalid run name">
<InputGroup.Input aria-label="Invalid run name" value="bad/name" />
<InputGroup.Addon align="inline-end">
<InputGroup.Text>Required</InputGroup.Text>
</InputGroup.Addon>
</InputGroup>
<InputGroup disabled aria-label="Archived run name">
<InputGroup.Input aria-label="Archived run name" value="archived-run" />
</InputGroup>
<InputGroup aria-label="Loading run search">
<InputGroup.Input aria-label="Loading run search" placeholder="Searching..." />
<InputGroup.Addon align="inline-end">
<InputGroup.Button loading aria-label="Search in progress" shape="square" />
</InputGroup.Addon>
</InputGroup>
</template>Sizes
Use xs, sm, base, or lg to match nearby Kappa controls.
<script setup>
import { Search } from "@lucide/vue";
import { InputGroup } from "@dicehub/kappa/components/input-group";
</script>
<template>
<InputGroup size="sm" aria-label="Small search">
<InputGroup.Input aria-label="Small search" placeholder="Search" />
<InputGroup.Addon align="inline-start"><Search aria-hidden="true" /></InputGroup.Addon>
</InputGroup>
</template>Right to Left
Logical placement and spacing keep addons correct in right-to-left content.
المدخلات المركبة تحافظ على اتجاه النص والمسافات المنطقية.
<script setup>
import { Search } from "@lucide/vue";
import { InputGroup } from "@dicehub/kappa/components/input-group";
</script>
<template>
<div dir="rtl" lang="ar">
<InputGroup aria-label="بحث في التشغيلات">
<InputGroup.Input aria-label="بحث في التشغيلات" placeholder="ابحث..." />
<InputGroup.Addon align="inline-start"><Search aria-hidden="true" /></InputGroup.Addon>
<InputGroup.Addon align="inline-end">
<InputGroup.Text>١٢ نتيجة</InputGroup.Text>
</InputGroup.Addon>
</InputGroup>
</div>
</template>Accessibility
- Every input and textarea needs an accessible name from a visible label,
aria-label, oraria-labelledby. - The root uses
role="group". Addaria-labeloraria-labelledbywhen the group needs a name. - Use
invalidon the group or control when validation fails. Pair it with visible error text in the surrounding Field composition. - Use native disabled controls. Group-level
disabledpropagates to Input, Textarea, and InputGroup.Button. - Keep the addon after the control in DOM order. This keeps keyboard and screen-reader order predictable.
- Do not use a non-interactive addon as the only label. Use a real label or an explicit ARIA name on the control.
API Reference
InputGroup
Root div with role="group". Native attributes, ARIA properties, data attributes, and listeners pass through.
| Prop | Type | Default | Description |
|---|---|---|---|
size | "xs" | "sm" | "base" | "lg" | "base" | Shared compact control density. A child part can override it. |
disabled | boolean | false | Disables controls in the group and exposes aria-disabled on the group. |
invalid | boolean | false | Marks the group and its input controls invalid. |
InputGroupAddon
Logical addon wrapper for icons, text, and related actions.
| Prop | Type | Default | Description |
|---|---|---|---|
align | "inline-start" | "inline-end" | "block-start" | "block-end" | "inline-start" | Logical visual placement. Keep the addon after the control in DOM order. |
InputGroupInput / Textarea
Native controls with group styling. All other native input and textarea attributes pass through.
| Prop | Type | Default | Description |
|---|---|---|---|
modelValue | string | number | — | Controlled value for v-model. Input events emit strings. |
defaultValue | string | number | — | Initial value for uncontrolled use. |
size | "xs" | "sm" | "base" | "lg" | parent size | Overrides the shared group density for this control. |
disabled / invalid | boolean | false | Adds a local state to the shared group state. |
InputGroupButton
Existing Kappa Button composed inside an addon. All Kappa Button props and native listeners pass through.
| Prop | Type | Default | Description |
|---|---|---|---|
size | "xs" | "sm" | "base" | "lg" | parent size | Uses Kappa Button sizing and can override the group density. |
shape | "base" | "square" | "circle" | "base" | Uses the Kappa Button shape. Use square for icon-only actions. |
variant | ButtonVariant | "ghost" | Uses the Kappa Button visual variant; ghost is quiet inside a group. |
Events
| Event | Payload | Description |
|---|---|---|
update:modelValue | string | Emitted by Input and Textarea for v-model. |
valueChange | string | Emitted with the next native value after input. |
native listeners | Event | Input, change, focus, click, and other native listeners pass to their part. |
Slots
| Component | Slot | Description |
|---|---|---|
InputGroup | default | Input, textarea, addon, text, and button parts. |
InputGroupAddon | default | Icons, text, buttons, or other addon content. |
InputGroupButton | default | Button label or icon content. |
InputGroupText | default | Compact helper or prefix text. |
Data Attributes
| Slot | Element | Description |
|---|---|---|
input-group | div[role=group] | Root group part. |
input-group-addon | div | Addon part; data-align exposes logical placement. |
input-group-control | input / textarea | Unified native control selector for focus styling. |
input-group-text | span | Text helper part. |
input-group-button | button | Marker on a Kappa Button rendered inside an addon. |
Exports
| Export | Description |
|---|---|
InputGroup | Compound root with Root, Addon, Button, Input, Textarea, and Text parts. |
InputGroupRoot / Addon / Button / Input / Textarea / Text | Named component exports for granular composition. |
InputGroup*Props / InputGroup*Slots | Public prop and slot contracts for every part. |
InputGroupSize / InputGroupAddonAlign | Public size and logical addon placement types. |
resolveInputGroupSize / resolveInputGroupAddonAlign | Runtime-safe default resolvers for component values. |