Component
Input
A single-line text field on a sunken fill, in four sizes, with an optional decorative icon before or after the text. Text is 16px on touch screens so iOS Safari doesn't zoom in on focus.
Xem trước
import { Input } from "@/components/ui/input"
<div className="flex w-full max-w-sm flex-col gap-xs">
<label htmlFor="preview-input" className="text-sm text-text-primary">Email</label>
<Input id="preview-input" placeholder="you@example.com" />
</div>Cách dùng
Khi nào dùng
- Single-line text the user types: a name, an email address, a search term, a number.
- Values the user can copy but not edit, such as an API key, with readOnly.
- A field whose icon says what goes in, such as a magnifying glass for search, with leadingIcon.
Khi nào không dùng
- Text over several lines. Use Textarea.
- Picking one value from a known list. Use Select.
- A disabled field for a value the user should still read or copy. Use readOnly instead, so the field stays focusable and the value can be copied.
Biến thể
size
Height from size-element-*: sm 28px, md 32px (the default), lg 36px, xl 44px with a mouse. On touch, md grows to 36px, lg to 44px and xl to 48px; sm does not, so don't use sm on touch-first screens.
import { Input } from "@/components/ui/input"
<div className="flex w-full max-w-sm flex-col gap-xs">
<label htmlFor="preview-input-size-sm" className="text-sm text-text-primary">Email</label>
<Input id="preview-input-size-sm" size="sm" placeholder="you@example.com" />
</div>
<div className="flex w-full max-w-sm flex-col gap-xs">
<label htmlFor="preview-input-size-md" className="text-sm text-text-primary">Email</label>
<Input id="preview-input-size-md" placeholder="you@example.com" />
</div>
<div className="flex w-full max-w-sm flex-col gap-xs">
<label htmlFor="preview-input-size-lg" className="text-sm text-text-primary">Email</label>
<Input id="preview-input-size-lg" size="lg" placeholder="you@example.com" />
</div>
<div className="flex w-full max-w-sm flex-col gap-xs">
<label htmlFor="preview-input-size-xl" className="text-sm text-text-primary">Email</label>
<Input id="preview-input-size-xl" size="xl" placeholder="you@example.com" />
</div>Ví dụ
Default
import { Input } from "@/components/ui/input"
<div className="flex w-full max-w-sm flex-col gap-xs">
<label htmlFor="email" className="text-sm text-text-primary">Email</label>
<Input id="email" type="email" placeholder="you@example.com" />
</div>With an icon
The icon is decorative; the visible label names the field.
import { IconSearch } from "@tabler/icons-react"
import { Input } from "@/components/ui/input"
<div className="flex w-full max-w-sm flex-col gap-xs">
<label htmlFor="search-files" className="text-sm text-text-primary">Search files</label>
<Input id="search-files" type="search" placeholder="Name or extension" leadingIcon={<IconSearch size={16} />} />
</div>Disabled
import { Input } from "@/components/ui/input"
<div className="flex w-full max-w-sm flex-col gap-xs">
<label htmlFor="workspace" className="text-sm text-text-primary">Workspace</label>
<Input id="workspace" disabled defaultValue="Personal" />
</div>Readonly
import { IconLock } from "@tabler/icons-react"
import { Input } from "@/components/ui/input"
<div className="flex w-full max-w-sm flex-col gap-xs">
<label htmlFor="api-key" className="text-sm text-text-primary">API key</label>
<Input id="api-key" readOnly defaultValue="wolf_sk_4f9a..." trailingIcon={<IconLock size={16} />} />
</div>Invalid
import { Input } from "@/components/ui/input"
<div className="flex w-full max-w-sm flex-col gap-xs">
<label htmlFor="signup-email" className="text-sm text-text-primary">Email</label>
<Input id="signup-email" type="email" aria-invalid="true" aria-describedby="signup-email-error" defaultValue="not-an-email" />
<p id="signup-email-error" className="text-sm text-text-danger">Enter an email like you@example.com.</p>
</div>Nên và không nên
Nên
- Always pair the input with a visible label (htmlFor / id).
- Match the input size to the button next to it: md in forms, sm only in dense mouse-first UI (table filters, toolbars), lg or xl for the single main field of a screen (search hero, sign-in on mobile).
- Invalid: aria-invalid="true", the danger border, plus error text in color-text-danger linked with aria-describedby. Never color alone.
- Readonly: native readOnly, same look as default, still focusable with the focus ring. Use it for values the user can copy but not edit (API key, generated URL).
- Keep the danger border while focused; the focus ring is drawn outside it.
- Use leadingIcon for an icon that says what kind of value goes in: a magnifying glass for search, an envelope for email.
- Pass the icon with size and that match the input size: 16 in sm, md and lg, 20 in xl.
Không nên
- Don't use placeholder as the label or for required info; it disappears on typing.
- Don't use size sm on touch-first screens. On touch md is already 36px, lg 44px and xl 48px.
- Don't use disabled for values the user should still read or copy; use readOnly.
- Don't add a hover border change. border-control already marks the field.
- Don't drop below font-size-input or restyle colors through className.
- Don't let an icon carry meaning alone (a check mark for valid, a warning for invalid). Say it in text linked with aria-describedby.
- Don't put buttons or links in leadingIcon or trailingIcon. The icons are decorative and hidden from screen readers.
Props
| Tên | Kiểu | Giá trị | Mặc định | Mô tả |
|---|---|---|---|---|
| size | enum | sm | md | lg | xl | md | Height from size-element-*: sm 28px, md 32px (the default), lg 36px, xl 44px with a mouse. On touch, md grows to 36px, lg to 44px and xl to 48px; sm does not, so don't use sm on touch-first screens. |
| disabled | boolean | - | false | Not interactive. Neutral disabled look. |
| readOnly | boolean | - | false | The value can be read, selected and copied but not edited. Same look as default; keeps the focus ring. |
| type | enum | text | email | password | search | tel | url | number | text | Native input type for single-line text-like fields. Chỉ trên web. |
| leadingIcon | node | - | - | Decorative Tabler icon before the text, such as a magnifying glass in a search field. Pass size and: 16 in sm, md and lg, 20 in xl. |
| trailingIcon | node | - | - | Decorative Tabler icon after the text, such as a lock on a readonly value. Same size rule as leadingIcon. Not for buttons: a clear or show-password button is planned as its own prop. |
Slot
| Tên | Bắt buộc | Mô tả |
|---|---|---|
| value | không | Controlled or uncontrolled text value. |
Cấu tạo
| Phần | Mô tả |
|---|---|
| root | The input element. Alone, it is the field box. With an icon it fills the group and draws no box of its own. |
| placeholder | Hint text shown when the value is empty. Colored with text-secondary, so it reaches 4.5:1 on the sunken fill. |
| group | The field box when leadingIcon or trailingIcon is set: a wrapper with the same fill, border, radius and height as the input alone. It shows the input's focus ring, disabled look and danger border through :has(). className lands here. |
| groupIcon | The leading or trailing icon inside the group. 16px in sm, md and lg, 20px in xl (DESIGN.md 9.1). Decorative. |
Khả năng truy cập
Role: textbox
| Phím | Hành động |
|---|---|
| Tab | Moves focus to the input and shows the focus ring. |
| Any printable key | Inserts text at the caret. |
- Pair with a visible label (htmlFor / id). Placeholder alone is not a label.
- Disabled uses the native disabled attribute, so the input leaves the tab order.
- Readonly uses the native readOnly attribute: still focusable and in the tab order, the value can be selected and copied.
- Invalid uses aria-invalid="true" plus visible error text in color-text-danger linked with aria-describedby.
- font-size-input is 14px on mouse and 16px on touch so iOS Safari does not zoom the page on focus.
- leadingIcon and trailingIcon are decorative (aria-hidden). The label and the text carry the meaning, so a screen reader hears the same field with or without them.
- A press on an icon or the padding of the group focuses the input, like a press on the field itself.
Cài đặt
npx shadcn add https://werewolf-ui.vercel.app/r/input.jsonLệnh này thêm luôn styles/werewolf.css, bộ token mà mọi component dùng, và các component khác mà nó cần. Import file đó một lần trong CSS gốc, ngay sau @import "tailwindcss", với đường dẫn tính từ file CSS đó (từ app/globals.css là @import "../styles/werewolf.css").