shadcn-html / input
Input
A styled text input built on the native <input> element. Includes label, description, error, sizes, file input, textarea auto-grow, and focus ring. Zero JavaScript.
Default
Standard text input with label.
<label class="label" for="email">Email</label>
<input class="input" type="email" id="email"
placeholder="you@example.com">
With description
Use .field-description below the input for help text.
Choose a unique username for your account.
<label class="label" for="username">Username</label>
<input class="input" type="text" id="username"
placeholder="codylindley"
aria-describedby="username-desc">
<p class="field-description" id="username-desc">
Choose a unique username for your account.
</p>
Sizes
Small, default, and large via data-size.
<input class="input" data-size="sm" type="text" placeholder="Small">
<input class="input" type="text" placeholder="Default">
<input class="input" data-size="lg" type="text" placeholder="Large">
Disabled
Non-interactive at 50% opacity.
This field is currently disabled.
<input class="input" type="text" disabled placeholder="Disabled">
Invalid
Use aria-invalid="true" with a .field-error message.
Please enter a valid email address.
<label class="label" for="email">Email</label>
<input class="input" type="email" id="email"
aria-invalid="true" value="not-an-email"
aria-describedby="email-err">
<p class="field-error" id="email-err">
Please enter a valid email address.
</p>
Required
Add required attribute and a visual indicator in the label.
This field is required.
<label class="label" for="name">
Name <span class="text-destructive">*</span>
</label>
<input class="input" type="text" id="name"
required placeholder="Jane Doe">
With icon
Wrap in a relative container and position the Lucide icon absolutely.
<div style="position:relative;">
<i data-lucide="search"
class="text-muted-foreground"
style="position:absolute;left:0.75rem;top:50%;transform:translateY(-50%);width:1rem;height:1rem;"></i>
<input class="input" style="padding-left:2.25rem;" type="search"
placeholder="Search...">
</div>
File
Use type="file" — the file selector button is styled automatically.
Select a picture to upload.
<label class="label" for="avatar">Picture</label>
<input class="input" type="file" id="avatar">
<p class="field-description">Select a picture to upload.</p>
Textarea
Use <textarea> with the .input class. Auto-grows with content via field-sizing: content — zero JS.
<label class="label" for="message">Message</label>
<textarea class="input" id="message"
placeholder="Your message..."></textarea>
Readonly
Non-editable value the user can still select and copy.
Your API key is read-only.
<label class="label" for="api-key">API Key</label>
<input class="input" type="text" id="api-key"
readonly value="sk-1234567890abcdef">
Password
Use type="password" for secure text entry.
<label class="label" for="password">Password</label>
<input class="input" type="password" id="password"
placeholder="Enter your password">
With button
Pair an input with a button for inline actions like search.
<div style="display:flex;gap:0.5rem;">
<input class="input" type="search"
placeholder="Search...">
<button class="btn" data-variant="default">Search</button>
</div>
Form composition
Multiple inputs with labels, descriptions, and a submit button.
<form>
<div>
<label class="label" for="name">
Name <span class="text-destructive">*</span>
</label>
<input class="input" type="text" id="name"
required placeholder="Jane Doe">
</div>
<div>
<label class="label" for="email">
Email <span class="text-destructive">*</span>
</label>
<input class="input" type="email" id="email"
required placeholder="jane@example.com">
<p class="field-description">
We'll never share your email with anyone.
</p>
</div>
<div>
<label class="label" for="bio">Bio</label>
<textarea class="input" id="bio"
placeholder="Tell us about yourself..."></textarea>
</div>
<button class="btn" data-variant="default" type="submit">Submit</button>
</form>
CSS view file
/* -- Input component ------------------------------------------- */
@layer components {
.input {
height: 2.5rem;
width: 100%;
border: 1px solid var(--input);
border-radius: var(--radius-md);
background: var(--background);
padding: 0 0.75rem;
font-size: 0.875rem;
font-family: var(--font-sans);
color: var(--foreground);
outline: none;
box-shadow: var(--shadow-xs);
transition: border-color 150ms, box-shadow 150ms;
&:focus {
border-color: var(--ring);
box-shadow: 0 0 0 2px oklch(from var(--ring) l c h / 0.2);
}
&::placeholder { color: var(--muted-foreground); }
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
/* -- Readonly state -------------------------------------- */
&:read-only:not([type="file"]) {
background: var(--muted);
cursor: default;
opacity: 0.7;
&:focus {
border-color: var(--input);
box-shadow: var(--shadow-xs);
}
}
/* -- Invalid state --------------------------------------- */
&:is([aria-invalid="true"], :user-invalid) {
border-color: var(--destructive);
&:focus {
border-color: var(--destructive);
box-shadow: 0 0 0 2px oklch(from var(--destructive) l c h / 0.2);
}
}
/* -- File input ------------------------------------------ */
&[type="file"] {
padding: 0;
font-size: 0.875rem;
&::file-selector-button {
height: 100%;
border: none;
border-right: 1px solid var(--input);
background: var(--muted);
color: var(--foreground);
font-size: 0.875rem;
font-weight: 500;
font-family: var(--font-sans);
padding: 0 0.75rem;
margin-right: 0.75rem;
cursor: pointer;
transition: background 150ms;
&:hover { background: var(--accent); }
}
}
/* -- Sizes ----------------------------------------------- */
&[data-size="sm"] { height: 2rem; padding: 0 0.625rem; font-size: 0.8125rem; }
&[data-size="lg"] { height: 2.75rem; padding: 0 1rem; font-size: 1rem; }
}
/* -- Auto-growing textarea --------------------------------- */
textarea.input {
field-sizing: content;
min-height: 5rem;
padding-top: 0.5rem;
padding-bottom: 0.5rem;
}
/* -- Field description ------------------------------------- */
.field-description {
font-size: 0.8125rem;
color: var(--muted-foreground);
margin: 0.375rem 0 0;
}
/* -- Field error ------------------------------------------- */
.field-error {
font-size: 0.8125rem;
color: var(--destructive);
margin: 0.375rem 0 0;
}
/* -- Accessibility ---------------------------------------- */
@media (prefers-reduced-motion: reduce) {
.input {
transition: none;
}
.input[type="file"]::file-selector-button {
transition: none;
}
}
@media (prefers-contrast: more) {
.input {
border-width: 2px;
}
.input:disabled {
opacity: 0.7;
}
}
@media (forced-colors: active) {
.input {
border-color: ButtonBorder;
color: ButtonText;
background: Field;
}
.input:focus {
outline: 2px solid Highlight;
outline-offset: 1px;
}
.input:disabled {
opacity: 1;
color: GrayText;
border-color: GrayText;
}
.input:is([aria-invalid="true"], :user-invalid) {
border-color: LinkText;
}
.field-error {
color: LinkText;
}
}
}