Input
Single-line text field for typed user input. Flat border (no shadow) and Yuno focus ring. Height h-9 (36px), rounded-md.
Anatomy
We only use this to send transactional receipts.
- 1Label
Describes the field. Always visible above the input — never rely on placeholder alone.
- 2Input
The typed field itself. Flat border-input, no shadow, focus-visible ring at ring/40.
- 3Placeholder
Hint for the expected format. Muted color. Not a replacement for the Label.
- 4Helper or error text
Optional. Placed below the input. Muted for helper, destructive tone for errors.
Types
Set the correct type attribute so mobile keyboards, browser autofill and validation work out of the box. The kit hides the browser's native number spin buttons globally so type='number' looks clean like any other input.
<Input type="text" placeholder="Full name" />
<Input type="email" placeholder="you@yuno.tools" />
<Input type="password" placeholder="••••••••" />
<Input type="number" placeholder="0.00" />
<Input type="tel" placeholder="+57 300 000 0000" />
<Input type="url" placeholder="https://" />
<Input type="search" placeholder="Search transactions" />
<Input type="file" />With icons
Icons live inside a relative wrapper. Leading icon on the left uses pl-9 on the Input; trailing icon on the right uses pr-9. Always Phosphor weight='light' size-4.
{/* Leading icon */}
<div className="relative w-full max-w-sm">
<MagnifyingGlass weight="light" className="absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" />
<Input type="search" placeholder="Search transactions" className="pl-9" />
</div>
{/* Trailing icon */}
<div className="relative w-full max-w-sm">
<Input type="password" placeholder="Password" className="pr-9" />
<Eye weight="light" className="absolute right-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" />
</div>With prefix or suffix
For static prefixes (currency, protocol) or suffixes (units, TLDs), wrap the Input in a flex container that shares the same border and radius. The Input itself becomes borderless inside.
{/* Prefix addon */}
<div className="flex w-full max-w-sm items-stretch rounded-md border border-input bg-background focus-within:border-ring focus-within:ring-2 focus-within:ring-ring/40">
<span className="flex items-center border-r border-input px-3 text-sm text-muted-foreground">
<CurrencyDollar weight="light" className="size-4" />
</span>
<Input type="number" placeholder="0.00" className="border-0 focus-visible:ring-0" />
</div>
{/* Suffix addon */}
<div className="flex w-full max-w-sm items-stretch rounded-md border border-input bg-background focus-within:border-ring focus-within:ring-2 focus-within:ring-ring/40">
<Input type="text" placeholder="yuno-tools" className="border-0 focus-visible:ring-0" />
<span className="flex items-center border-l border-input px-3 text-sm text-muted-foreground">.com</span>
</div>States
The full state matrix: default, focus, disabled, readonly, error, success. Errors add border-destructive + helper text.
<div className="grid gap-1.5 w-full max-w-sm">
<Label htmlFor="email">Email</Label>
<Input id="email" type="email" placeholder="you@yuno.tools" />
</div><Input placeholder="Acme HQ" disabled /><Input value="ACC-38F91-Q2" readOnly />Amount must be greater than 0.
<Input
aria-invalid
className="border-destructive focus-visible:border-destructive focus-visible:ring-destructive/30"
/>
<p className="text-xs text-destructive">Amount must be greater than 0.</p>Handle is available.
<Input
value="yuno-tools"
className="border-success focus-visible:border-success focus-visible:ring-success/30"
/>
<p className="text-xs text-success">Handle is available.</p>Form field
The canonical pattern in Yuno forms: Label + Input + optional helper/error text stacked in a vertical group.
Visible to everyone with access to the account.
<div className="grid w-full max-w-sm gap-1.5">
<Label htmlFor="workspace">Workspace name</Label>
<Input id="workspace" placeholder="Acme HQ" />
<p className="text-xs text-muted-foreground">Visible to everyone with access to the account.</p>
</div>Required and optional
Yuno marks the odd one out. If most fields in a form are required, mark the few Optional ones with a small muted 'Optional' to the right of the Label. If most are optional, mark the required ones with a discrete 'Required' in the same spot. Never mix asterisks and Optional labels in the same form.
<div className="grid gap-1.5 w-full max-w-sm">
<div className="flex items-center justify-between">
<Label htmlFor="phone">Phone</Label>
<span className="text-xs text-muted-foreground">Optional</span>
</div>
<Input id="phone" type="tel" placeholder="+57 300 000 0000" />
</div><div className="grid gap-1.5 w-full max-w-sm">
<div className="flex items-center justify-between">
<Label htmlFor="email">Email</Label>
<span className="text-xs text-muted-foreground">Required</span>
</div>
<Input id="email" type="email" placeholder="you@yuno.tools" required />
</div>Recipes
Ready-to-copy compositions covering the most common Yuno usages of this atom.
Input with a leading MagnifyingGlass. Use above tables, lists, or as a global search.
<div className="relative w-full max-w-sm">
<MagnifyingGlass weight="light" className="absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" />
<Input type="search" placeholder="Search transactions" className="pl-9" />
</div>type='password' input with a trailing Eye button that toggles between hidden and visible.
function PasswordToggle() {
const [visible, setVisible] = React.useState(false);
return (
<div className="relative">
<Input type={visible ? "text" : "password"} className="pr-9" />
<button type="button" onClick={() => setVisible(v => !v)}
className="absolute right-2 top-1/2 -translate-y-1/2 ...">
{visible ? <EyeSlash weight="light" /> : <Eye weight="light" />}
</button>
</div>
);
}Number input with a static currency addon on the left. Used across payments, refunds, transaction filters.
<div className="flex items-stretch rounded-md border border-input bg-background focus-within:border-ring focus-within:ring-2 focus-within:ring-ring/40">
<span className="flex items-center border-r border-input px-3 text-sm text-muted-foreground">
<CurrencyDollar weight="light" className="size-4" />
</span>
<Input type="number" placeholder="0.00" className="border-0 focus-visible:ring-0" />
</div>Text input with a trailing static domain segment. Used for handles, subdomains, custom URLs.
<div className="flex items-stretch rounded-md border border-input bg-background focus-within:border-ring focus-within:ring-2 focus-within:ring-ring/40">
<Input type="text" placeholder="acme" className="border-0 focus-visible:ring-0" />
<span className="flex items-center border-l border-input px-3 text-sm text-muted-foreground">.yuno.tools</span>
</div>Import
Copy this import line at the top of the file where you compose this atom.
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { MagnifyingGlass } from "@phosphor-icons/react/dist/ssr";Props
Everything else from the underlying HTML or Radix primitive is forwarded via ...props.
| Prop | Type | Default | Description |
|---|---|---|---|
| type | "text" | "email" | "password" | "number" | "tel" | "url" | "search" | "file" | ... | "text" | Native HTML input type. Drives mobile keyboard, autofill and validation. |
| value / defaultValue | string | number | — | Controlled or uncontrolled value. Use React.useState + onChange for controlled. |
| placeholder | string | — | Format hint (e.g. 'you@yuno.tools'). Not a replacement for the Label. |
| disabled | boolean | false | Dims the input and blocks interaction. |
| readOnly | boolean | false | Keeps the value visible and copyable but not editable. |
| required | boolean | false | Native validation. Pair with the 'Required' label pattern in this doc so the user sees it. |
| aria-invalid | boolean | false | Signals the field is in an error state. Combine with border-destructive on className. |
| className | string | — | Merged with the base classes via cn(). Use pl-9 / pr-9 when composing leading / trailing icons. |
When to use
- Short single-line entries: email, name, amount, search term.
- Inside a form, always paired with an explicit Label.
- Search fields — pair with a leading MagnifyingGlass icon.
When not to use
- Long text — use Textarea.
- Choice from a fixed list — use Select or Combobox.
- Toggles or booleans — use Switch or Checkbox.
- One-time codes (2FA, SMS) — use Input OTP.
Usage
- Always pair with a <Label htmlFor='id'> for accessibility.
- Use placeholder as a format hint (you@yuno.tools), never as a label replacement.
- For errors, add border-destructive + a small helper text below.
- Set the correct type: email, tel, url, number, search, so mobile keyboards work.
- For search, add a leading MagnifyingGlass icon (weight='light', size-4).
- Never apply drop shadows on inputs — only border.
- Don't use placeholder as the field label.
- Don't disable an input silently without visual + textual explanation.
- Don't hardcode widths — let the parent form grid control sizing.
Related
Cross-links to atoms and patterns you may reach for next.