Zetta v1.6

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.

Build with the skill

No package to install — hand this to your AI to generate or migrate the component in your stack.

Prompt for your AI
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> via for/id.
  • Show a prefix icon to indicate field purpose (e.g. search for search fields, mail for 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 via for/id.
  • Error state: add aria-invalid="true" and aria-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}