Skip to content
Component installs need the registry setup
VegaStack Design

Input

A styled Base UI input for every text-entry type — Field-aware state, invalid and disabled chrome, and a subtle darker border instead of a focus ring.

Status
stable
Since
0.1.0
Accessibility pattern
native input

Last updated

Install

Configure registry credentials first using the Quickstart. Before copying this item or any transitive registry dependency, verify the signed manifest and save the exact bytes for Input before installation. Retain the digest printed by this command.

pnpm exec vegastack-design verify input

Install with the pinned shadcn CLI. Bare shadcn does not verify VegaStack signature or integrity.

pnpm dlx shadcn@4.21.0 add @vegastack/input

Run the exact offline --post-write command printed by the preflight, using its saved item path and independently retained --expected-integrity digest. Use the saved preflight for every transitive registry dependency too; stop on any mismatch before using the components.

Usage

import { Input } from "@/components/ui/input";

<Input />;

Examples

Basic

A bare input with a placeholder.

Sizes

size="sm" is 28px, default 32px, and lg sets heading type — pair it with ghost for a big title.

Ghost

variant="ghost" drops the border and the fill, so the text sits on the page like a heading — the title field of a new-issue dialog. It paints no fill in any state — rest, hover or focus: the text cursor shows it is editable, the caret is the focus cue, and an invalid ghost field turns its placeholder destructive.

<Input variant="ghost" size="lg" placeholder="Issue title" aria-label="Title" />

Field

Field, FieldLabel and FieldDescription give the input a label and helper text.

Choose a unique username for your account.

Field Group

FieldGroup stacks several Field blocks and is how a form is built.

We'll send updates to this address.

Disabled

Use disabled, and add data-disabled to the Field so the label dims with it. The input keeps disabled:cursor-not-allowed.

This field is currently disabled.

Invalid

Use aria-invalid, and add data-invalid to the Field. The destructive border holds in every state: focus never replaces it with the darker focus border (FOC-14).

File

type="file" styles the native file button with the file: variants.

Select a picture to upload.

Inline

Field with orientation="horizontal" puts the input and a Button on one row.

Grid

A FieldGroup with a grid layout places two inputs side by side.

Required

required marks the field for the platform's own validation.

This field must be filled out.

Badge

A Badge inside the label highlights a beta or recommended field.

Input Group

For an icon, a text prefix or a button on the same surface, use Input Group.

https://

Button Group

For a button joined to the field's edge, use Button Group.

Form

Several inputs, a select and a submit row.

We'll never share your email with anyone.

RTL

Padding is written in logical properties, so a right-to-left document mirrors the control without any extra work.

As it appears on your passport.

كما يظهر في جواز سفرك.

Not a credential

A field that holds a record's name — a table, a field, a page title — is not a login, but password managers offer to fill any name-like input. credential={false} turns that off (autoComplete="off" and the 1Password, LastPass and Bitwarden ignore markers); an explicit autoComplete still wins.

<Input
  credential={false}
  aria-label="Table name"
  defaultValue="Sample requests"
/>

States

Rest, filled, invalid and disabled in one frame. Click or Tab into a field to see the focus state: the border darkens subtly (border-ring/50) over 150ms and no fill is painted; the invalid field keeps its destructive border while focused.

Playground

Try every type with the disabled and invalid states, then copy the generated JSX.

<Input placeholder="you@vegastack.com" />

API Reference

PropTypeDefaultDescription
credentialbooleantruefalse (FRM-17) marks a name-like field that is not a credential — a record, table or field name — so browsers and password managers stop offering to fill it: autoComplete="off", data-1p-ignore, data-lpignore="true", data-bwignore. An explicit autoComplete wins.
size"default" | "lg" | "sm" | null—
variant"default" | "ghost" | null—

Accessibility

  • Renders Base UI's Input, which is a native <input> wired to the surrounding Field, so the platform owns selection, IME composition and autofill.
  • Focus is a subtle darker border (border-ring/50, easing in over 150ms), not a ring, and no background change — text entry is the one exception to FOC-14's fixed border (MK 2026-09-27); the global outline is suppressed with outline-hidden (FOC-8). It fires on :focus, for mouse and keyboard alike, because a text field cannot tell them apart and both need the caret located. An invalid field keeps its destructive border while focused. type="file" is a button, so it keeps the global background tint instead.
  • Under forced-colors: active the border colour is replaced with every authored colour, so base.css paints a 2px Highlight outline on input:focus instead (FOC-7).
  • Give it a label: FieldLabel bound with htmlFor, or an aria-label.
ContractStates tested
Behaviourdefault, disabled, invalid, readonly
Accessibilitynative-or-base-ui-semantics, browser-accessibility-test, invalid, disabled
Visualdefault, focus, invalid, disabled

Do / Don't

Do
Use an Input for single-line text and pair it with a Field so the label, description and error are wired for you.
Don't
Put an icon or a button inside an Input by hand — that is Input Group.

Deviations

Upstream's file plus packages/ui/upstream/patches/input.patch. Every hunk:

  • FOC-1, FOC-3, FOC-6, FOC-8 — outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 becomes outline-hidden, with no ring and no glow. outline-hidden rather than outline-none, so the forced-colours fallback has something to repaint.
  • FOC-14 — the invalid border is aria-invalid:border-destructive (and its dark counterpart), held in every state, focused or not; the aria-invalid halo rings are removed with the rest.
  • FOC-14 text-entry exception (MK 2026-09-27) — the default variant adds focus:not-aria-invalid:not-[[type=file]]:border-ring/50 with transition-[color,background-color,border-color] duration-150 ease-out: a subtle darker border that eases in, and no fill (base.css excludes text inputs from the background tint). The not-aria-invalid guard keeps the destructive border; a file input is a button and keeps the tint. The ghost variant has no border and no focus fill — the caret is the cue.
  • FRM-4 — disabled:pointer-events-none is dropped, so a disabled input stays hoverable and a Tooltip can explain it. Base UI's Input has no focusableWhenDisabled, so the native disabled attribute stays: this is the pointer half of FRM-4 only.
  • API-26 — NO HUNK: Base UI's Input is Field.Control, so inside a Field it already takes its label, the rendered description and error ids, and aria-invalid from the Field.
  • DOC-2 — cn is imported from @vegastack/design.
  • FRM-17 — Input takes credential; false marks a non-credential name field so password managers leave it alone. MK, 2026-10-26.

On this page