Components · Forms
Input
A text field with a 3:1 border and an invalid state.
39% of the 2,062 sites in The UI Index have a form (806 sites).
Use lowercase letters, numbers and dashes.
Use lowercase letters, numbers and dashes.
Both previews render the same markup; the HTML tab below is that markup without a framework.
Install
- React
- npx shadcn@
latest add https:// www.themes.dev/ caseroom/ r/ input.json - Astro
- npx shadcn@
latest add https:// www.themes.dev/ caseroom/ r/ astro/ input.json
Or copy the files below into your project. They need clsx, tailwind-merge, and the Caseroom theme. The HTML needs only the compiled CSS.
import type { InputHTMLAttributes } from 'react';
import { cn } from '@/lib/utils';
import { inputClasses } from './input.variants';
export type InputProps = InputHTMLAttributes<HTMLInputElement>;
/** A text field. Always pair it with a <Label htmlFor>; set aria-invalid and aria-describedby for errors. */
export function Input({ className, type = 'text', ...props }: InputProps) {
return <input type={type} className={cn(inputClasses, className)} {...props} />;
}/** Shared by input and textarea. The border has 3:1 contrast (WCAG 1.4.11); aria-invalid turns it destructive. */
export const fieldClasses =
'w-full min-w-0 rounded-md border border-input bg-background px-3 text-sm text-foreground transition-colors placeholder:text-muted-foreground enabled:hover:border-foreground/85 enabled:active:bg-muted aria-invalid:enabled:hover:border-destructive aria-invalid:enabled:hover:bg-[color-mix(in_oklab,var(--destructive)_6%,var(--background))] aria-invalid:enabled:active:bg-[color-mix(in_oklab,var(--destructive)_10%,var(--background))] focus-visible:border-ring focus-visible:outline-2 focus-visible:outline-offset-0 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:outline-destructive';
export const inputClasses = `${fieldClasses} h-9 py-1 file:mr-3 file:border-0 file:bg-transparent file:text-sm file:font-medium`;---
import type { HTMLAttributes } from 'astro/types';
import { cn } from '@/lib/utils';
import { inputClasses } from './input.variants';
type Props = HTMLAttributes<'input'>;
const { class: className, type = 'text', ...rest } = Astro.props;
---
<input type={type} class={cn(inputClasses, className)} {...rest} />/** Shared by input and textarea. The border has 3:1 contrast (WCAG 1.4.11); aria-invalid turns it destructive. */
export const fieldClasses =
'w-full min-w-0 rounded-md border border-input bg-background px-3 text-sm text-foreground transition-colors placeholder:text-muted-foreground enabled:hover:border-foreground/85 enabled:active:bg-muted aria-invalid:enabled:hover:border-destructive aria-invalid:enabled:hover:bg-[color-mix(in_oklab,var(--destructive)_6%,var(--background))] aria-invalid:enabled:active:bg-[color-mix(in_oklab,var(--destructive)_10%,var(--background))] focus-visible:border-ring focus-visible:outline-2 focus-visible:outline-offset-0 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:outline-destructive';
export const inputClasses = `${fieldClasses} h-9 py-1 file:mr-3 file:border-0 file:bg-transparent file:text-sm file:font-medium`;<div class="grid w-full max-w-sm gap-6">
<div class="grid gap-2">
<label class="flex items-center gap-2 text-sm leading-none font-medium has-[input]:items-start has-[input]:leading-snug has-[input]:[&>*:first-child]:mt-px select-none peer-disabled:cursor-not-allowed peer-disabled:opacity-50" for="ex-email">
Email
</label>
<input type="email" class="w-full min-w-0 rounded-md border border-input bg-background px-3 text-sm text-foreground transition-colors placeholder:text-muted-foreground enabled:hover:border-foreground/85 enabled:active:bg-muted aria-invalid:enabled:hover:border-destructive aria-invalid:enabled:hover:bg-[color-mix(in_oklab,var(--destructive)_6%,var(--background))] aria-invalid:enabled:active:bg-[color-mix(in_oklab,var(--destructive)_10%,var(--background))] focus-visible:border-ring focus-visible:outline-2 focus-visible:outline-offset-0 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:outline-destructive h-9 py-1 file:mr-3 file:border-0 file:bg-transparent file:text-sm file:font-medium" id="ex-email" placeholder="you@example.com" autocomplete="email">
</div>
<div class="grid gap-2">
<label class="flex items-center gap-2 text-sm leading-none font-medium has-[input]:items-start has-[input]:leading-snug has-[input]:[&>*:first-child]:mt-px select-none peer-disabled:cursor-not-allowed peer-disabled:opacity-50" for="ex-slug">
Project slug
</label>
<input type="text" class="w-full min-w-0 rounded-md border border-input bg-background px-3 text-sm text-foreground transition-colors placeholder:text-muted-foreground enabled:hover:border-foreground/85 enabled:active:bg-muted aria-invalid:enabled:hover:border-destructive aria-invalid:enabled:hover:bg-[color-mix(in_oklab,var(--destructive)_6%,var(--background))] aria-invalid:enabled:active:bg-[color-mix(in_oklab,var(--destructive)_10%,var(--background))] focus-visible:border-ring focus-visible:outline-2 focus-visible:outline-offset-0 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:outline-destructive h-9 py-1 file:mr-3 file:border-0 file:bg-transparent file:text-sm file:font-medium" id="ex-slug" value="My project" aria-invalid="true" aria-describedby="ex-slug-error">
<p id="ex-slug-error" class="text-sm text-destructive">Use lowercase letters, numbers and dashes.</p>
</div>
</div>