Skip to content
Kappa
Input Group
@dicehub/kappav0.4.2

Input Group

Composes native inputs with logical addons, helper text, and compact actions.

Endpoint
https://
Enter an endpoint to continue
<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

Typical part hierarchy
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.Input for a one-line native input and InputGroup.Textarea for multiline content.
  • Use InputGroup.Addon for icons, text, or related actions. Use logical inline-* and block-* alignment.
  • Keep an addon after its control in DOM order. This preserves the focus and reading order while CSS handles placement.
  • Use InputGroup.Button only for an action. Icon-only buttons need aria-label or aria-labelledby.

Examples

Alignment

Use logical alignment values. Keep every addon after the control in DOM order; Kappa moves it visually.

Inline start
Inline end
Block start
Request path
Block end
Optional
<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.

$
USD
https://
.com
dicehub
<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.

0/280
<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.

Required
Archived
<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.

xs
sm
base
lg
<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, or aria-labelledby.
  • The root uses role="group". Add aria-label or aria-labelledby when the group needs a name.
  • Use invalid on the group or control when validation fails. Pair it with visible error text in the surrounding Field composition.
  • Use native disabled controls. Group-level disabled propagates 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.

PropTypeDefaultDescription
size"xs" | "sm" | "base" | "lg""base"Shared compact control density. A child part can override it.
disabledbooleanfalseDisables controls in the group and exposes aria-disabled on the group.
invalidbooleanfalseMarks the group and its input controls invalid.

InputGroupAddon

Logical addon wrapper for icons, text, and related actions.

PropTypeDefaultDescription
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.

PropTypeDefaultDescription
modelValuestring | number—Controlled value for v-model. Input events emit strings.
defaultValuestring | number—Initial value for uncontrolled use.
size"xs" | "sm" | "base" | "lg"parent sizeOverrides the shared group density for this control.
disabled / invalidbooleanfalseAdds 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.

PropTypeDefaultDescription
size"xs" | "sm" | "base" | "lg"parent sizeUses 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.
variantButtonVariant"ghost"Uses the Kappa Button visual variant; ghost is quiet inside a group.

Events

EventPayloadDescription
update:modelValuestringEmitted by Input and Textarea for v-model.
valueChangestringEmitted with the next native value after input.
native listenersEventInput, change, focus, click, and other native listeners pass to their part.

Slots

ComponentSlotDescription
InputGroupdefaultInput, textarea, addon, text, and button parts.
InputGroupAddondefaultIcons, text, buttons, or other addon content.
InputGroupButtondefaultButton label or icon content.
InputGroupTextdefaultCompact helper or prefix text.

Data Attributes

SlotElementDescription
input-groupdiv[role=group]Root group part.
input-group-addondivAddon part; data-align exposes logical placement.
input-group-controlinput / textareaUnified native control selector for focus styling.
input-group-textspanText helper part.
input-group-buttonbuttonMarker on a Kappa Button rendered inside an addon.

Exports

ExportDescription
InputGroupCompound root with Root, Addon, Button, Input, Textarea, and Text parts.
InputGroupRoot / Addon / Button / Input / Textarea / TextNamed component exports for granular composition.
InputGroup*Props / InputGroup*SlotsPublic prop and slot contracts for every part.
InputGroupSize / InputGroupAddonAlignPublic size and logical addon placement types.
resolveInputGroupSize / resolveInputGroupAddonAlignRuntime-safe default resolvers for component values.