Input

The form-control family. Input, Textarea, Select, Checkbox and Switch all share one field base, so a form looks like one thing however it is assembled.

import from ~/components/ui/input

Types

type

The native type prop picks the keyboard and the browser validation. It is not a visual choice.

Sizes

inputSize

A closed set of three. Pick one per surface and stay with it: marketing forms sit at default, dense app tables at sm.

States

placeholder

A hint about the format, never a substitute for the label.

disabled

Not available right now. Say why somewhere the user can read it.

aria-invalid

The error state is driven by aria-invalid, so the border and the screen reader agree.

readOnly

Shown, focusable, copyable, not editable. Different from disabled.

Textarea and Select

Textarea

One shared field base. Resizes vertically only.

Select

A native select with the chevron supplied by the component.

Checkbox and Switch

Checkbox

Multi-select lists and explicit consent. Wrap it in a label so the text is a hit target.

Notifications on Notifications off

Switch

A single setting that applies the moment it flips. Both states shown; the specimen is static, so it does not toggle.

In context

contact form

Label above control, one control per row unless two fields are genuinely paired.

Rules

Do

Give every control a visible label, tied to it with FieldLabel htmlFor and a matching id.
Set type so the user gets the right keyboard and the browser gets the right validation.
Mark an errored control with aria-invalid; the destructive border follows from it.
Constrain width with the container (max-w-sm on a wrapper), not on the control.

Don't

Do not use the placeholder as the label. It vanishes as soon as the user types.
Do not use disabled to mean invalid. Disabled means unavailable, not wrong.
Do not pass border, color or rounded classes to fix a state. The state props already cover it.
Do not put a Switch in a form that needs a submit. A Switch applies immediately.