Input
Single-line field with mono readout text and a 2px signal focus ring.
"use client";import { Input } from "@/registry/okibi/ui/input";import { Label } from "@/registry/okibi/ui/label";export default function InputDemo() { return ( <div className="grid w-full max-w-xs gap-2"> <Label htmlFor="input-demo-operator">Operator ID</Label> <Input id="input-demo-operator" placeholder="Enter operator ID" /> </div> );}npx shadcn@latest add https://okibi.cndr.dev/r/input.jsonInstallation
Install the theme first, then add the component:
npx shadcn@latest add https://okibi.cndr.dev/r/input.jsonInstall the required dependencies:
npm install class-variance-authorityCopy the source from the Code tab above into components/ui/input.tsx. It uses the cn helper and the okibi theme tokens — install the theme first if you haven't.
Usage
import { Input } from "@/components/ui/input"
<Input type="email" placeholder="operator@relay" />Examples
With label
Pair the field with a Label (matching htmlFor/id) so the control is named for assistive tech and the label is a click target.
import { Input } from "@/registry/okibi/ui/input";import { Label } from "@/registry/okibi/ui/label";export default function InputWithLabelDemo() { return ( <div className="w-64 space-y-2"> <Label htmlFor="input-with-label-grid">Grid reference</Label> <Input id="input-with-label-grid" placeholder="Enter grid ref" /> </div> );}With button
Sit the field next to a Button in a flex row for inline submit fields — a callsign transmit, a search, an invite.
import { Button } from "@/registry/okibi/ui/button";import { Input } from "@/registry/okibi/ui/input";export default function InputWithButtonDemo() { return ( <div className="flex w-full max-w-sm gap-2"> <Input placeholder="Signal payload" /> <Button variant="signal">Transmit</Button> </div> );}File
type="file" gets the same keyline and mono treatment as the text field.
import { Input } from "@/registry/okibi/ui/input";import { Label } from "@/registry/okibi/ui/label";export default function InputFileDemo() { return ( <div className="w-64 space-y-2"> <Label htmlFor="input-file-telemetry">Telemetry dump</Label> <Input id="input-file-telemetry" type="file" /> </div> );}Disabled
The native disabled attribute blocks interaction and dims the field.
import { Input } from "@/registry/okibi/ui/input";export default function InputDisabledDemo() { return ( <Input disabled placeholder="Enter operator ID" defaultValue="OP-7741 // LOCKED" className="w-64" /> );}Invalid
Set aria-invalid to flip the focus ring and border to the destructive color for inline validation errors.
SEC // malformed channel reference.
import { Input } from "@/registry/okibi/ui/input";export default function InputInvalidDemo() { return ( <div className="w-64 space-y-2"> <Input aria-invalid defaultValue="CH-0XZ" /> <p className="font-mono text-xs text-destructive-spark"> SEC // malformed channel reference. </p> </div> );}API Reference
Input
| Prop | Type | Default | Description |
|---|---|---|---|
size | "sm" | "default" | "lg" | "default" | Field height, to compose with the Button/Select size scale. |
type | string | "text" | Native input type (email, file, password, …). |
Accessibility
- Renders a native
input, so keyboard, autofill, and type-specific UI (date, file, number) all work out of the box. - It ships no built-in label — associate a
LabelviahtmlFor/id, or wrap the field, so the control is named. - Set
aria-invalidto flag an error: the border, a faint fill, and the focus ring all swap to destructive. - Focus-visible draws a 2px inset signal ring;
disableddrops opacity to 60% and blocks pointer events. - There is no built-in error-message slot — render help/error text yourself and wire it with
aria-describedby.
Guidelines
Do
- Pair every field with a
Labelbound byhtmlFor— the placeholder is not a label. - Match
sizeto theButton/Selectit sits beside (sm/default/lg). - Set the right
type(email,password,number) so mobile keyboards and validation adapt. - Drive errors through
aria-invalidand link the message witharia-describedby.
Don't
- Don't rely on the uppercase placeholder to carry the field's name — it vanishes on input.
- Don't hand-roll heights via
classNamewhen asizetoken exists. - Don't use a destructive border for an error without also setting
aria-invalid. - Don't expect a built-in error slot — the primitive leaves message rendering to you.