Deck
Browse the catalog

Build / Molecules

Input

  • molecule
  • play-fn

A single-line text field.

The label is a required prop rather than something you remember to add — an unlabelled input is the most common form accessibility failure.

import { Input } from '@meetcard/deck'

Example

<Input label="Work email" type="email" placeholder="ada@meetcard.com" /> <Input label="Company" error="Company is required" />

Stories

Every preview below is the real component, rendered in this page from the same story Storybook runs. The width control resizes each preview's container; CSS media queries still follow the browser window, so breakpoint behaviour is worth checking in Storybook.

Default
Source
const Default = () => <div style={{ maxWidth: 320 }}>
    <Input label="Work email" placeholder="ada@meetcard.com" onChange={fn()} />
</div>;

Descriptions are wired up with aria-describedby automatically.

With Description
Source
const WithDescription = () => <div style={{ maxWidth: 320 }}>
    <Input
        label="Work email"
        placeholder="ada@meetcard.com"
        onChange={fn()}
        description="We only use this to match you with existing connections." />
</div>;

An error sets aria-invalid and is announced via role="alert", so the failure reaches screen reader users, not just sighted ones.

With Error
Source
const WithError = () => <div style={{ maxWidth: 320 }}>
    <Input
        label="Work email"
        placeholder="ada@meetcard.com"
        onChange={fn()}
        error="Enter a valid work email address"
        defaultValue="ada@" />
</div>;

Required. The field says so in text, not only with an asterisk.

Required
Source
const Required = () => <div style={{ maxWidth: 320 }}>
    <Input
        label="Work email"
        placeholder="ada@meetcard.com"
        onChange={fn()}
        required />
</div>;

With a leading icon, for a field whose purpose is clearer as a glyph — search, mostly.

With Icon
Source
const WithIcon = () => <div style={{ maxWidth: 320 }}>
    <Input
        label="Search your deck"
        placeholder="Search"
        onChange={fn()}
        iconStart={<SearchIcon />} />
</div>;

Disabled, with a value that cannot be changed here.

Disabled
Source
const Disabled = () => <div style={{ maxWidth: 320 }}>
    <Input
        label="Work email"
        placeholder="ada@meetcard.com"
        onChange={fn()}
        disabled
        defaultValue="ada@meetcard.com" />
</div>;

Visually hidden label — still fully announced. Use sparingly.

Hidden Label
Source
const HiddenLabel = () => <div style={{ maxWidth: 320 }}>
    <Input label="Search your deck" placeholder="Search" onChange={fn()} hideLabel />
</div>;

Every size, aligned with Button so a field and its action sit in one row.

Sizes
Source
const Sizes = () => <Stack gap={12} style={{ maxWidth: 320 }}>
    <Input placeholder="ada@meetcard.com" onChange={fn()} size="sm" label="Small" />
    <Input placeholder="ada@meetcard.com" onChange={fn()} size="md" label="Medium" />
    <Input placeholder="ada@meetcard.com" onChange={fn()} size="lg" label="Large" />
</Stack>;

Props

PropTypeDefaultDescription
labelrequiredReactNode—Accessible name. Use hideLabel if the design has no visible label.
descriptionReactNode——
errorReactNode—Message shown below the field; also sets aria-invalid.
fieldClassNamestring—Class applied to the wrapping field rather than the <input>.
hideLabelbooleanfalse—
iconStartReactNode—Decorative leading adornment, e.g. a search or currency glyph.
size'sm' | 'md' | 'lg''md'—

Also accepts everything from Omit, so id, data-* and event handlers pass straight through.

Open in Storybookfor controls, the a11y panel and the interaction runner.