Light

Input

Displays a form input field or a component that looks like an input field.

Spec · from metadata

When to use

  • Text, email, password, number, date, and other standard input types
  • File uploads (styled file input)
  • Search fields
  • Inside FormControl for validated form fields

When not to use

  • Multi-line text — use Textarea instead
  • Selection from a list — use Select or Combobox
  • Boolean toggle — use Checkbox or Switch
  • Rich text editing — use a dedicated editor component

Anti-patterns

Avoid<Input placeholder="Email" />
Prefer<div className="grid gap-2">
  <Label htmlFor="email">Email</Label>
  <Input id="email" placeholder="you@example.com" />
</div>

Inputs must always have an associated label for accessibility. Placeholder is not a substitute.

Avoid<input className="border rounded p-2" />
Prefer<Input />

Use the Input component for consistent styling, focus rings, and error states.

Accessibility

  • Required ARIAid (when paired with Label htmlFor)aria-invalid (auto-set by FormControl)
  • Screen readerWhen inside FormControl, aria-describedby is auto-set to link to FormDescription and FormMessage.
  • ContrastUses bg-input-bg for background and text-muted-foreground for placeholder, meeting WCAG AA.

Token bindings

TokenCategoryUsage
bg-input-bgcolorInput background
border-inputcolorDefault border color
border-ringcolorFocus border color
text-muted-foregroundcolorPlaceholder text
border-destructivecolorError state border
rounded-mdradiusBorder radius
shadow-xsshadowSubtle input shadow

Import

import { Input } from "@timelycare/helix-ui"
import { Label } from "@timelycare/helix-ui"

Props

interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
  type?: "text" | "email" | "password" | "file" | "number" | "search" | "tel" | "url"
  placeholder?: string
  disabled?: boolean
  className?: string
}

Dimensions

PropertyValue
Height36px (h-9)
Paddingpx-3 py-1 (12px / 4px)
Backgroundbg-input-bg
Border radiusrounded-md (8px)
Border1px solid
Shadowshadow-xs
Icon size16px (size-4)
Icon gapgap-1 (4px)

Variants

VariantUse ForStructure
defaultText, email, password, numberStandard input field
fileFile uploads"Choose file" button + filename text
with-iconSearch, with prefix iconIcon + input (icon size-4, gap-1)

Layout Options

LayoutStructureUse For
Vertical (Horizontal Layout=No)Label → Input → Description (stacked)Most forms, default
Horizontal (Horizontal Layout=Yes)Label | Input (inline) + Description belowCompact/inline forms

Spacing

  • Label to input: gap-2 (8px) — built into space-y-2
  • Input to description: gap-2 (8px)

States

StateBorderRingText
Defaultborder-inputtext-muted-foreground (placeholder)
Focusborder-ringring-[3px] ring-ring/50
Filledborder-inputtext-foreground
Disabledborder-inputopacity-50 cursor-not-allowed
Errorborder-destructive
Error + Focusborder-destructivering-[3px] ring-destructive/50

Common Patterns

Vertical Layout (Default)

<div className="space-y-2">
  <Label htmlFor="email">Label</Label>
  <Input id="email" placeholder="Placeholder" />
  <p className="text-sm text-muted-foreground">This is an input description.</p>
</div>

With Leading Icon

<div className="space-y-2">
  <Label htmlFor="search">Search</Label>
  <div className="relative">
    <Search className="absolute left-3 top-1/2 -translate-y-1/2 size-4 text-muted-foreground" />
    <Input id="search" placeholder="Search..." className="pl-9" />
  </div>
</div>

With Forgot Password Link

<div className="space-y-2">
  <div className="flex items-center justify-between">
    <Label htmlFor="password">Password</Label>
    <a href="/forgot" className="text-sm text-muted-foreground underline">
      Forgot your password?
    </a>
  </div>
  <Input id="password" type="password" placeholder="••••••••" />
</div>

Horizontal Layout

<div className="space-y-2">
  <div className="flex items-center gap-4">
    <Label htmlFor="name" className="w-16 shrink-0 text-right">Label</Label>
    <Input id="name" placeholder="Placeholder" />
  </div>
  <p className="text-sm text-muted-foreground ml-20">This is an input description.</p>
</div>

File Input

<div className="space-y-2">
  <Label htmlFor="file">Label</Label>
  <Input 
    id="file" 
    type="file" 
    className="file:mr-3 file:px-1.5 file:py-px file:border-0 
      file:bg-transparent file:text-sm file:font-semibold file:text-foreground" 
  />
  <p className="text-sm text-muted-foreground">This is an input description.</p>
</div>

Input + Button

<div className="flex">
  <Input placeholder="Placeholder" className="rounded-r-none" />
  <Button className="rounded-l-none">Button</Button>
</div>

Error State

<div className="space-y-2">
  <Label htmlFor="email">Label</Label>
  <Input 
    id="email" 
    placeholder="Placeholder" 
    className="border-destructive focus-visible:ring-destructive/50" 
  />
  <p className="text-sm text-destructive">This field is required.</p>
</div>

Accessibility

  • Standard HTML input enhanced with design tokens
  • Keyboard: standard text input behavior
  • Screen reader: label announced via htmlFor/id association
  • Requirements: aria-describedby for hints, aria-invalid for errors
  • Focus visible ring applied automatically

Gotchas

ProblemSolution
Label not linkedMatch htmlFor to input id
Error ring wrong colorAdd focus-visible:ring-destructive/50
Horizontal description misalignedAdd ml-20 (or label width) to description
File button unstyledUse file: prefix classes
Input + Button gapUse rounded-r-none / rounded-l-none
Icon not alignedUse absolute left-3 top-1/2 -translate-y-1/2
Input with icon paddingAdd pl-9 to input when icon present
Focus ring too thickUse ring-[3px] not ring-2

See Also


Last updated: February 24, 2026