Input
Single-line text field built on the native <input>; supports addons, clearable, and password-toggle affordances.
Renders a native <input>; aria-invalid toggled on invalid state; focus ring follows design tokens.
Renders as a segmented control: the field and any addon slots sit as
raised tiles inside a tinted, padded track — the same container NumberInput
and the date/time pickers use. The focus ring lights up the track.
Preview
tsx
import { Input } from '@arshad-shah/cynosure-react';
export default function Example() {
return <Input placeholder="Type something…" />;
}
Three sizes are available: sm, md (default), and lg.
Preview
tsx
import { Input } from '@arshad-shah/cynosure-react';
export default function Example() {
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem', width: '320px' }}>
<Input size="sm" placeholder="Small" />
<Input size="md" placeholder="Medium (default)" />
<Input size="lg" placeholder="Large" />
</div>
);
}
Invalid state
Section titled “Invalid state”Set invalid to signal a validation error visually.
Preview
tsx
import { Input } from '@arshad-shah/cynosure-react';
import { useState } from 'react';
export default function Example() {
const [value, setValue] = useState('bad-email');
const invalid = value.length > 0 && !value.includes('@');
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem', width: '320px' }}>
<Input
type="email"
value={value}
onChange={setValue}
invalid={invalid}
placeholder="you@example.com"
/>
{invalid && (
<p style={{ fontSize: '0.875rem', color: 'var(--color-danger-600, #dc2626)', margin: 0 }}>
Enter a valid email address.
</p>
)}
</div>
);
}
Disabled state
Section titled “Disabled state” Preview
tsx
import { Input } from '@arshad-shah/cynosure-react';
export default function Example() {
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem', width: '320px' }}>
<Input placeholder="Disabled" disabled />
<Input defaultValue="Read-only content" readOnly />
</div>
);
}
With addons
Section titled “With addons”Use leadingSlot and trailingSlot to attach icons or text prefixes/suffixes.
Preview
tsx
import { Input } from '@arshad-shah/cynosure-react';
const SearchIcon = () => (
<svg
aria-hidden="true"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<circle cx="11" cy="11" r="8" />
<line x1="21" y1="21" x2="16.65" y2="16.65" />
</svg>
);
export default function Example() {
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem', width: '360px' }}>
<Input leadingSlot={<SearchIcon />} placeholder="Search…" />
<Input leadingSlot="https://" trailingSlot=".com" placeholder="example" />
<Input leadingSlot="$" trailingSlot="USD" placeholder="0.00" />
</div>
);
}
With helper text
Section titled “With helper text”Pair with a visible description below the field.
Preview
tsx
import { Input } from '@arshad-shah/cynosure-react';
import { useState } from 'react';
export default function Example() {
const [value, setValue] = useState('');
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.375rem', width: '320px' }}>
<label htmlFor="username" style={{ fontSize: '0.875rem', fontWeight: 500 }}>
Username
</label>
<Input id="username" value={value} onChange={setValue} placeholder="arshad_shah" />
<p style={{ fontSize: '0.75rem', color: 'var(--color-fg-muted, #6b7280)', margin: 0 }}>
Only letters, numbers, and underscores. 3–20 characters.
</p>
</div>
);
}
PropTypeDefaultDescription
type
"number"|"text"|"email"|"password"|"tel"|"url"|"search"
"text"
Native `<input type>` value. `password` automatically adds a show/hide
toggle; `search`/`email`/`tel`/`url`/`number` keep their native
keyboards on mobile.
variant
"outline"|"filled"|"ghost"|"flat"
"outline"
Visual treatment. `outline` is the default bordered surface; `filled`
uses a tinted background; `ghost` removes the surface entirely; `flat`
is the legacy single-well layout (no multi-well slots).
leadingSlot
ReactNode|ReactNode[]
—
Single node or array. Strings/icons render as inert wells; buttons/onClick render as action wells.
trailingSlot
ReactNode|ReactNode[]
—
Single node or array. Same rules as leadingSlot.
clearable
boolean
—
When true and value is non-empty, appends a clear × as a trailing action well.
className
string
—
style
CSSProperties
—
placeholder
string
—
size
"sm"|"md"|"lg"
"md"
Control size — affects height, padding, and font size.
value
string
—
Controlled value. Pair with `onChange`.
defaultValue
string
—
Uncontrolled initial value.
onChange
((value: string) => void)
—
Called with the next value whenever the user changes the field.
disabled
boolean
—
Disables interaction and dims the field.
name
string
—
Submitted form field name.
readOnly
boolean
—
Renders the field as read-only — value is visible and selectable but not editable.
required
boolean
—
Marks the field as required for form submission.
autoFocus
boolean
—
Focuses the control on mount.
id
string
—
Element id — auto-generated via `useId` when omitted.
invalid
boolean
—
Renders the invalid state (red border, error styling) and sets `aria-invalid`.
Accessibility
Section titled “Accessibility”- Renders a native
<input>— inherits all built-in ARIA semantics. invalidsetsaria-invalid="true"on the underlying input element.- Focus ring is token-driven and never removed.
Recipes
Section titled “Recipes”- Use
type="password"to automatically get a show/hide toggle affordance. - Use
clearableto append an accessible clear button when the field has a value.