Components · Forms & selection
Input
Single-line text input for free-form text entry. Split from zetta-form-v1_4.md (removed in v1.5) in v1.4.
Preview
We'll never share your email.
Please enter a valid email address.
Build with the skill
No package to install — hand this to your AI to generate or migrate the component in your stack.
Apply the Zetta "Input" component to this project using the zetta-design-md skill.
WHEN TO USE
- Migrating or fixing existing code (the common case): audit the current implementation against the `input` spec and CORRECT every deviation — replace off-spec values, don't only add new ones.
- New component: build it to the `input` spec in this project's stack.
AUDIT & FIX (when the component already exists)
Compare it to the skill's spec and fix, don't just append: hardcoded colors/hex, off-scale radius, wrong fonts, dark/navy fills, shadows used in place of borders (or missing borders), brand lime used as a CTA or interaction (must be primary indigo), and any missing or wrong hover / focus / active / disabled / invalid state or missing 2px focus ring. Replace each with the correct Zetta token.
ALWAYS
- Pull the authoritative spec and tokens from the skill — never guess or hardcode values.
- Use this project's own component conventions and framework idioms.
- Honor the Zetta guardrails (brand = identity only, primary = interaction, shadows for overlays) and verify in Light, Dark, and Accessibility. Anatomy & rules
┌─────────────────────────────────────────┐
│ [prefix icon] Placeholder text [✕] │ 36px height
└─────────────────────────────────────────┘
↑ input-text-prefix ↑ input-text-suffix
| State | Border | Fill | Text |
|---|---|---|---|
| Default | {colors.border-strong} |
{colors.surface-card} |
{colors.ink} |
| Placeholder | {colors.border-strong} |
{colors.surface-card} |
{colors.placeholder} |
| Hover | {colors.muted} |
{colors.surface-card} |
{colors.ink} |
| Focus | {colors.primary} + glow |
{colors.surface-card} |
{colors.ink} |
| Error | {colors.danger} + glow |
{colors.surface-card} |
{colors.ink} |
| Disabled | {colors.disabled-border} |
{colors.disabled-bg} |
{colors.disabled-text} |
| Read-only | {colors.hairline} |
{colors.surface-muted} |
{colors.ink} |
When to use
- Single-line free-form text entry: names, email addresses, search queries, URLs.
- Any field where the user types a value rather than selecting from a list.
When not to use
- Multiple lines of text — use Text Area.
- Selecting from known options — use Select.
- Numeric entry with increment controls — use Number Input.
Do
- Always pair with a visible
<label>viafor/id. - Show a prefix icon to indicate field purpose (e.g.
searchfor search fields,mailfor email). - Reveal a suffix clear (
✕) button when the field has a value.
Don't
- Don't use placeholder as a substitute for a label.
- Don't validate on every keystroke — use blur or submit timing.
- Labels: always visible — never rely on placeholder as the only label.
- Placeholder: supplementary hint only. "e.g. john@company.com" not "Email address".
- Error messages: specific and actionable. "Email must include '@'." not "Invalid email."
- Character count: show when there's a meaningful limit. Position below-right of field.
- Password fields: always include show/hide toggle.
Keyboard
| Key | Behavior |
|---|---|
Tab |
Move focus to input |
Shift+Tab |
Move focus away |
| Standard text editing keys | All standard browser text input behavior |
Accessibility
- Always pair with a
<label>element linked viafor/id. - Error state: add
aria-invalid="true"andaria-describedby="error-message-id". - Read-only: add
aria-readonly="true". - Contrast:
{colors.ink}on{colors.surface-card}is 21:1 (AAA).
Spec
Values and token references resolved straight from the Zetta spec.
input-text
- backgroundColor
- {colors.surface-card}
- textColor
- {colors.ink}
- borderColor
- {colors.border-strong}
- borderWidth
- 1px
- borderRadius
- {rounded.base}
- fontFamily
- Geist
- fontSize
- 14px
- fontWeight
- 400
- lineHeight
- 1.5
- height
- 36px
- paddingX
- {spacing.sm}
- paddingY
- 0
- width
- 100%
- transition
- border-color {motion.duration-fast}, box-shadow {motion.duration-fast}
input-text-placeholder
- textColor
- {colors.placeholder}
input-text-hover
- borderColor
- {colors.muted}
input-text-focus
- borderColor
- {colors.primary}
- outline
- none
- boxShadow
- 0 0 0 2px {colors.primary-bg}
input-text-disabled
- backgroundColor
- {colors.disabled-bg}
- textColor
- {colors.disabled-text}
- borderColor
- {colors.disabled-border}
- cursor
- not-allowed
input-text-error
- borderColor
- {colors.danger}
- boxShadow
- 0 0 0 2px {colors.danger-bg}
input-text-read-only
- backgroundColor
- {colors.surface-muted}
- textColor
- {colors.ink}
- borderColor
- {colors.hairline}
- cursor
- default
input-text-prefix
- paddingLeft
- {spacing.xl}
input-text-suffix
- paddingRight
- {spacing.xl}