Skip to content

Input

Terminal window
npx bambiui init
npx bambiui add input

The CLI copies self-contained Input source into your project. The installed output includes framework source, recipe.ts, types.ts, and input.css.

Import global tokens once:

@import "./styles/bambi.css";
import { Input, InputField, InputControl, InputLabel,
InputDescription, InputError, InputStart, InputEnd }
from './components/ui/input';

Use Input alone when you supply your own accessible name via aria-label or a separate <label>.

<Input type="email" aria-label="Email address" placeholder="[email protected]" />

InputField renders a complete, labelled form field in a single component. Pass input attributes directly — they are forwarded to the native <input>.

<InputField
label="Email address"
description="We will never share your email."
error={formErrors.email}
type="email"
name="email"
placeholder="[email protected]"
required
/>

labelMode="normal" renders the label above the input. This is the default.

<InputField label="Full name" labelMode="normal" type="text" />

labelMode="floating" renders the label inside the input area. The label floats above when the field is focused or has a value. The label is still a real <label> element — it is never replaced by a placeholder.

<InputField label="Full name" labelMode="floating" type="text" />

Requirements for floating labels:

  • A label prop must be provided; the label element remains the accessible name.
  • Never use placeholder as the accessible label.
  • If placeholder is also set, it appears after the label has floated.
  • The data-filled attribute on the field wrapper tracks value state. React, Svelte, and Vue track this reactively. Astro uses a progressive-enhancement inline script.
VariantDescription
outlineBordered input, default
filledFilled background, no border
flushedBottom border only
unstyledNo default styling
<InputField label="Outline" variant="outline" type="text" />
<InputField label="Filled" variant="filled" type="text" />
<InputField label="Flushed" variant="flushed" type="text" />
<InputField label="Unstyled" variant="unstyled" type="text" />
SizeUse case
smDense forms
mdDefault
lgProminent fields
<InputField label="Small" size="sm" type="text" />
<InputField label="Medium" size="md" type="text" />
<InputField label="Large" size="lg" type="text" />

Use tone to communicate semantic feedback state.

ToneMeaning
defaultNeutral (default)
dangerError or invalid
successValid
warningCaution
<InputField label="Username" tone="success" type="text" />
<InputField label="Password" tone="danger" error="Too short" type="password" />
<InputField label="Recovery email" tone="warning" type="email" />

When error is set, tone is automatically overridden to danger and aria-invalid="true" is applied to the input.

Provide prefix and suffix content with start and end props (React) or slots (Svelte, Vue, Astro).

<InputField
label="Website"
start="https://"
end=".com"
type="url"
/>

Decorative icons in adornments should be aria-hidden="true". If an adornment is interactive (e.g., a reveal button), it must have an accessible name and be keyboard reachable.

For advanced layouts, use the compound primitives directly. InputField provides context in React so InputLabel, InputDescription, and InputError receive their IDs automatically.

<InputField variant="filled" size="lg">
<InputLabel>Website</InputLabel>
<InputControl hasStart hasEnd>
<InputStart>https://</InputStart>
<Input type="url" />
<InputEnd>.com</InputEnd>
</InputControl>
<InputDescription>Enter your site without the protocol.</InputDescription>
<InputError>Please enter a valid URL.</InputError>
</InputField>
<InputField label="Username" type="text" disabled />
<InputField label="Account ID" type="text" value="acc_12345" readOnly />
<InputField label="Email" type="email" required />

Required inputs receive the native required attribute. A visual * indicator is rendered after the label (aria-hidden). Native browser validation and AT communicate required semantics through the native attribute.

<InputField
label="Password"
type="password"
error="Password must be at least 8 characters"
/>

error sets tone="danger", aria-invalid="true" on the input, and renders the error message with role="alert" and an aria-describedby connection to the input.

PropTypeDefaultDescription
labelstringVisible label text
labelMode'normal' | 'floating''normal'Label placement behaviour
descriptionstringHelper text below the input
errorstringError message; sets aria-invalid and tone="danger"
variant'outline' | 'filled' | 'flushed' | 'unstyled''outline'Visual style
size'sm' | 'md' | 'lg''md'Input height and font size
tone'default' | 'danger' | 'success' | 'warning''default'Semantic colour state
invalidbooleanMarks input invalid without an error message
fullWidthbooleanfalseStretches field to container width
requiredbooleanNative required attribute + visual indicator
disabledbooleanNative disabled attribute
readOnlybooleanNative readonly attribute
startReactNode (React) / slot (others)Start adornment
endReactNode (React) / slot (others)End adornment

All native <input> attributes (type, name, value, placeholder, autoComplete, onChange, etc.) are forwarded to the native element.

Input accepts all native <input> attributes plus variant, size, tone, invalid, and fullWidth from InputBaseProps.

Input styling is driven by stable data attributes on the field wrapper:

AttributeSource
data-variantvariant
data-sizesize
data-tonecomputed tone (error overrides)
data-label-modelabelMode
data-invalidinvalid or error
data-disableddisabled
data-readonlyreadOnly
data-requiredrequired
data-full-widthfullWidth
data-filledinput has a value
data-has-startstart adornment present (on control)
data-has-endend adornment present (on control)

Every input must have an accessible name. Use InputField with a label prop, or use Input with aria-label / an external <label>.

<!-- Preferred: visible label via InputField -->
<InputField label="Email" type="email" />
<!-- Acceptable: standalone Input with aria-label -->
<Input type="search" aria-label="Search" />

Floating labels are real <label> elements associated with the input via for/id. They must never be replaced by placeholder. Placeholder text is supplementary — it disappears when the user types and is not accessible to all AT. A floating label that has floated above the input is still the accessible name.

Description and error text are connected to the input via aria-describedby. When both exist, both IDs are included.

<InputField
label="Email"
description="We will never share your email."
error="Please enter a valid email address."
type="email"
/>
<!-- renders: aria-describedby="[desc-id] [err-id]" on the input -->

When error is provided or invalid is true, aria-invalid="true" is set on the native input. Assistive technology announces this to users.

Native required is set on the input. The visual asterisk is aria-hidden="true" to avoid double-announcement.

  • disabled uses the native disabled attribute.
  • readOnly uses the native readonly/readOnly attribute.
  • Focus remains possible on readonly inputs so users can still read and copy the value.

Never use placeholder as the only accessible name for an input. The placeholder disappears once typing begins and has low colour contrast by design. Always provide a real <label>.