A controlled single-line input for machine-shaped values. The monospace face and the disabled spell-check / autocomplete / autocapitalize / autocorrect attributes are baked in, not opt-in: a slug or token must round-trip exactly as typed. The caller owns the value signal and receives keystrokes via `on_input`; no internal state, disposal-safe.
Readiness
complete
Preview
live
Props
5
Examples
2
Code
implementation mapped
When to use
Element slugs, resource identifiers, branch names
API keys, tokens, hashes, hex colour values
Any field where browser auto-correction would corrupt the value
When not to use
Human prose or names — use Input
Multi-line code or config — use TextArea
Multi-value tag entry — use TokenInput
Accessibility
Role: textbox
Tab — Focus to / from the input
Screen reader: A standard text input; announced as an edit field. Callers should provide an `aria-label` or wrap with `field` so the expected format is communicated. `aria-invalid=true` is set in the error state.
Notes: Disabling autocorrect/autocapitalize also helps screen-reader users on mobile who would otherwise hear auto-substituted text.
A controlled colour field. A native `<input type=color>` swatch opens the OS picker; a monospace hex readout sits beside it so the value is visible and copyable. The value is the native `#rrggbb` string. The caller owns the value signal; changes arrive via `on_change`. No internal state, disposal-safe.
Readiness
complete
Preview
live
Props
3
Examples
2
Code
implementation mapped
When to use
Choosing a single solid colour (brand accent, tag colour, label)
Where the OS-native colour picker is the right affordance
Colour fields that must round-trip a stable hex value
When not to use
Gradients or multi-stop palettes — out of scope
A small fixed swatch set — use RadioCardGroup of swatches
Alpha / HSL editing — needs a dedicated colour-picker pattern
Accessibility
Role: button
Enter — Open the native colour picker
Space — Open the native colour picker
Screen reader: The swatch carries an explicit `aria-label` ('Choose colour'); the hex readout is plain text adjacent to it. Callers should add a `field` wrapper naming what the colour applies to.
Notes: The hex value beside the swatch means the choice is not conveyed by colour alone — colourblind users can read and copy the exact value.
A controlled date field. The value is the native `<input type=date>` contract — an ISO-8601 `YYYY-MM-DD` string — so it serialises cleanly and round-trips through APIs without locale ambiguity. Optional `min` / `max` bound the selectable range. The caller owns the value signal; changes arrive via `on_change`. No internal state, disposal-safe.
Readiness
complete
Preview
live
Props
5
Examples
2
Code
implementation mapped
When to use
A single calendar date (due date, start date, birthday)
Date fields where the OS-native picker is the right affordance
Bounded date selection via min / max
When not to use
Date + time together — pair DateInput with TimeInput
Free-form relative phrases ('next Tuesday') — use a parsed Input
Date ranges — compose two DateInputs in a form row
Accessibility
Role: textbox
ArrowDown — Open the native picker popup
ArrowUp — Step the focused date segment
Screen reader: The native date control is announced segment-by-segment by the platform. Callers should still provide an `aria-label` (or wrap with `field`) naming the date being entered.
Notes: `color-scheme: dark` is set so the native picker chrome matches the dark theme; the picker indicator is keyboard reachable.
An email field that specialises `<Input>` with built-in email-format validation surfaced as an inline helper/error message, so login/signup/settings forms stop reimplementing the same regex + error wiring. It lives portal-side today (`portal/src/components/form.rs`) with this catalog entry as the stable contract.
Readiness
complete
Preview
live
Props
4
Examples
2
Code
implementation mapped
When to use
Any email field — login, signup, invite, contact, settings.
Where inline format feedback (not just browser-native) improves the flow.
Forms that should share one consistent email-validation UX.
When not to use
A generic text field — use `<Input>`.
Server-side-only validation with no inline feedback — a plain `<Input type=email>` suffices.
Multi-recipient / tag-style entry — that needs a tokenised input, not this.
Accessibility
Role: textbox
Tab — Focus to/from the input.
Screen reader: The validation message must be associated via `aria-describedby` and the field marked `aria-invalid` when invalid, so SR users hear the error tied to the field — not just a visually-adjacent red line.
Notes: `portal::components::form::EmailInput` is the current home. Inline validation complements, never replaces, server-side validation.
Props
Name
Type
Default
Description
label
string
Email
Field label.
placeholder
string
you@example.com
Input placeholder.
required
boolean
true
Whether the field is required (drives required-validation).
show_validation
boolean
true
Whether inline format-validation feedback renders. Off → relies on native/server validation only.
Examples
Login email
Login email field — labelled, required, inline validation on.
The reusable substrate of IdentityPicker. A visually-hidden `<input type=file>` keeps the native file dialog and form semantics; a styled trigger button proxies its click so the affordance matches the design system; a live `aria-live` list previews selected file names. The selection-name signal is component-scoped (created at the component owner, never inside a `<Show>`/`<For>`) and the only DOM handle is a `NodeRef` Leptos cleans up — disposal-safe. Selected names are emitted via `on_files`.
Readiness
complete
Preview
live
Props
5
Examples
2
Code
implementation mapped
When to use
Any file upload where the native button styling is off-brand
Single or multiple file selection with a name preview
Building blocks for richer upload patterns (drag-drop, avatars)
When not to use
Drag-and-drop dropzones — compose on top of FilePicker
Direct-to-storage resumable uploads — needs an upload pattern
Non-file data entry — use the matching input primitive
Accessibility
Role: button
Enter — Open the system file picker (trigger button)
Space — Open the system file picker (trigger button)
Tab — Focus the trigger button
Screen reader: The real `<input type=file>` is visually hidden but remains in the accessibility tree and focus order; the trigger is a real button. The preview list is an `aria-live=polite` region so a new selection is announced. Callers should label the trigger for context.
Notes: Hiding the input uses the clip pattern (not display:none) so it stays keyboard- and screen-reader-reachable.
Props
Name
Type
Default
Description
on_files
string
—
label
string
Choose file…
multiple
boolean
false
accept
string
disabled
boolean
false
Examples
Attach file
Single-file attachment picker.
YAML
type: file-picker
props:
label: Attach a file
on_files: ${actions.set_attachment}
InlineEdit lets a label become editable in place. It is best for short, low-risk values such as names, titles, and labels where opening a full form would slow the workflow.
Readiness
complete
Preview
live
Props
3
Examples
1
Code
implementation mapped
When to use
Renaming elements, boards, files, or saved views
Short text values where the display state and edit state occupy the same space
Low-risk edits with immediate save or local draft handling
When not to use
Long-form content, code, markdown, or multiline text
Fields that require validation explanation before editing
Destructive or audited changes that need explicit confirmation
Accessibility
Role: textbox
Enter — Saves the current value
Escape — Cancels editing
Tab — Moves focus out of the inline editor
Screen reader: Display state should be introduced by surrounding label or row context.
Notes: Use clear row context so the editable value is not announced in isolation.
Props
Name
Type
Default
Description
value
string
—
Current display value.
placeholder
string
Click to edit
Text shown when value is empty.
on_save
string
—
Examples
Element name edit
Rename an element without leaving the current row.
Daily contact sync
YAML
type: inline-edit
props:
value: Daily contact sync
placeholder: Name this element
Single-line text input with consistent focus, disabled, and error visual treatment. Use `input_type: password` for secrets. Wrap with `field` for labelled forms.
Readiness
complete
Preview
live
Props
9
Examples
3
Code
implementation mapped
When to use
Single-line text entry (name, username, search, etc.)
Inline editable values (use with InlineEdit pattern)
When not to use
Multi-line text — use TextArea
Choosing from a fixed list — use Select or NativeSelect
Date/time entry — use DateInput / TimeInput (when they land)
Color picking — use ColorInput (when it lands)
Range / numeric drag — use Slider (when it lands)
Single labelled field — wrap with `field` so label + helper + error are consistent
Accessibility
Role: textbox
Tab — Focus to/from the input
Enter — Submits the wrapping form (if input_type != textarea)
Escape — Blurs the input (browser default)
Screen reader: `placeholder` is NOT a label — pair with `field` for proper association. `error: true` adds `aria-invalid=true`. `disabled` removes from focus order. `required: true` adds `aria-required=true`.
Notes: `auto_focus: true` should be used on at most one input per modal — the audit fails if multiple auto_focus inputs are mounted simultaneously.
Props
Name
Type
Default
Description
value
string
—
Current text value. v1 binding language: pass `${state.username}` to bind; the engine wires read + write paths to a shared signal.
on_input
string
—
Action reference fired on every keystroke with the new value. v1 binding language: `${actions.set_username}`.
input_type
enum: text | password | email | number | search | url | tel
text
Native HTML input type. Drives keyboard / autofill.
placeholder
string
—
disabled
boolean
false
required
boolean
false
error
boolean
false
Toggles the error visual (red border, red focus ring). The error message itself is rendered by the wrapping `field` component.
name
string
—
HTML name attribute — used by form-submit semantics.
auto_focus
boolean
false
Focuses the input on first render. Use sparingly — only one input per modal should auto-focus.
A controlled numeric input with explicit decrement / increment buttons. The caller owns the value signal; the buttons compute the next clamped value from the current signal and emit it via `on_change`, so the component holds no internal state and is disposal-safe. Values are clamped to `[min, max]` on every change, whether typed or stepped.
Readiness
complete
Preview
live
Props
6
Examples
2
Code
implementation mapped
When to use
Small integer quantities (cart count, retry limit, replica count)
Discrete numeric entry where ± buttons aid touch / discoverability
Bounded numeric fields where typed input must still be clamped
When not to use
Bounded continuous values — use Slider
Free-form text or identifiers — use Input or CodeInput
A small fixed option set — use SegmentedControl
Accessibility
Role: spinbutton
ArrowUp — Increment value by step (native field)
ArrowDown — Decrement value by step (native field)
Tab — Move between the − button, field, and + button
Screen reader: The wrapper exposes `role=spinbutton` with `aria-valuenow` mirrored from the current value; the ± buttons carry explicit `aria-label`s. Callers should add an `aria-label` describing what is being counted.
Notes: Disabled state dims the control and blocks both the buttons and the field.
A password field that specialises `<Input>` with a show/hide eye toggle, so login/signup/change-password forms stop reimplementing the masked-state UX (and the a11y of the toggle) every time. It lives portal-side today (`portal/src/components/form.rs`) with this catalog entry as the stable contract.
Readiness
complete
Preview
live
Props
4
Examples
2
Code
implementation mapped
When to use
Any password entry — login, signup, change-password, re-auth.
Where a reveal toggle reduces typo-driven failed logins.
Forms that should share one consistent masked-input + toggle UX.
When not to use
Non-secret text — use `<Input>`.
A masked config/secret display (not entry) — use `<KeyValue masked>`.
One-time codes / OTP — use a segmented code input, not a password field.
Accessibility
Role: textbox
Tab — Focus to/from the input, then to the show/hide toggle.
Enter — Submits the form (does not toggle visibility).
Screen reader: The eye toggle MUST announce its state — "Show password" / "Hide password" — and be a real focusable button, not a click-only icon. Toggling visibility must not move focus out of the field.
Notes: `portal::components::form::PasswordInput` is the current home. Reveal is a convenience, not a security downgrade — never auto-reveal on focus.
Props
Name
Type
Default
Description
placeholder
string
Input placeholder.
required
boolean
true
Whether the field is required.
show_label
boolean
true
Whether the field label renders (off for compact inline forms).
revealable
boolean
true
Whether the show/hide eye toggle renders. Off → permanently masked (e.g. high-security contexts).
Examples
Login password (masked)
Login password field — masked, with reveal toggle.
A text input specialised for search queries. The magnifier icon prefix and clear-affordance (× appears once the user has typed) are structural, not opt-in props. Pair with debouncing in the caller via `${actions.x}` or use `use_debounced` from the hooks module.
Readiness
complete
Preview
live
Props
5
Examples
2
Code
implementation mapped
When to use
Filter / search query box atop a list (table, file tree, results)
Type-to-narrow inputs in popovers or palettes
RailFilter-like usage in panels
Anywhere the user is typing a query rather than a structured value
When not to use
Free-form text input — use Input (no magnifier, no clear)
Combobox with picker — use Combobox when it lands
Compound search with filters — use a Toolbar + SearchInput composition
Accessibility
Role: searchbox
Tab — Focus to/from the input
Enter — Submits the query
Escape — Clears the input (when value is non-empty)
Screen reader: Sets `role=searchbox`. Caller should provide an aria-label or wrap with `field` so SR users know what is being searched.
Notes: Clear affordance is announced as a button — pair with the Clear icon glyph to avoid screen-reader confusion.
A controlled bounded-numeric drag input. The caller owns the value signal and receives every change via `on_change`; the component holds no internal state, so it is disposal-safe inside showcase `<Show>`/`<For>` scopes. Use Slider when the value is continuous and both bounds are known (e.g. 0–100 opacity). The optional live readout shows the current value without the caller wiring a separate label.
Readiness
complete
Preview
live
Props
7
Examples
2
Code
implementation mapped
When to use
Bounded continuous values where approximate selection is fine (opacity, volume, zoom)
Threshold tuning where the user benefits from seeing the value move
Compact numeric control in a toolbar or properties panel
When not to use
Unbounded or precision numeric entry — use NumberStepper
A small discrete option set — use SegmentedControl or RadioGroup
Boolean on/off — use Switch
Accessibility
Role: slider
ArrowLeft — Decrement value by step
ArrowRight — Increment value by step
Home — Set to minimum
End — Set to maximum
Screen reader: Backed by a native `<input type=range>` with `role=slider` and `aria-valuemin` / `aria-valuemax` / `aria-valuenow` mirrored from the bounds and current value. Callers should provide an `aria-label` (or wrap with `field`) so the value's meaning is announced.
Notes: The thumb hover/scale animation is suppressed under `prefers-reduced-motion: reduce`.
Multi-line text entry with consistent focus, disabled, and error treatment. Use for multi-paragraph content (descriptions, comments, prompts). Pair with `field` for labelled forms; rely on `placeholder` only for ephemeral hint text. Auto-grow via the `rows` prop is the fixed canonical mechanism for variable height.
Readiness
complete
Preview
live
Props
7
Examples
3
Code
implementation mapped
When to use
Multi-line text content (descriptions, comments, prompts)
Code with mono font + no spell-check — use CodeInput when it lands
Markdown editors with preview — wrap with a higher-level Markdown editor primitive
Accessibility
Role: textbox
Tab — Focus to/from the textarea (does NOT insert tab character)
Enter — Inserts a newline
Escape — Blurs (browser default)
Screen reader: `placeholder` is NOT a label — pair with `field` for proper association. `error: true` adds `aria-invalid=true`. `disabled` removes from focus order. `aria-multiline=true` is implicit.
Notes: `auto_focus: true` should be used on at most one textarea per modal. Set `rows` >= 3 for usable content height.
Props
Name
Type
Default
Description
value
string
—
Current text value. Bind `${state.x}` for reactive content.
on_input
string
—
Action reference fired on keystroke. `${actions.set_x}`.
placeholder
string
—
rows
integer
3
Visible rows. Caller controls vertical size.
disabled
boolean
false
error
boolean
false
auto_focus
boolean
false
Examples
Description
Description field with helper text.
YAML
type: field
props:
label: Description
field_id: desc
slots:
default:
- type: textarea
props:
value: ${state.description}
on_input: ${actions.set_description}
placeholder: Tell us about this circle...
rows: 5
A controlled time field. The value is the native `<input type=time>` contract — an `HH:MM` 24-hour string — so it serialises cleanly and is locale-stable. Optional `step` (in seconds) controls granularity, e.g. `60` for minute steps or `1` to expose seconds. The caller owns the value signal; changes arrive via `on_change`. No internal state, disposal-safe.
Readiness
complete
Preview
live
Props
4
Examples
2
Code
implementation mapped
When to use
A time-of-day (reminder time, daily schedule slot)
Time fields where the OS-native picker is the right affordance
Granularity control via the step prop (minutes vs seconds)
When not to use
Date + time together — pair with DateInput
Durations rather than clock times — use NumberStepper or a Duration field
Free-form relative phrases — use a parsed Input
Accessibility
Role: textbox
ArrowDown — Open the native picker popup
ArrowUp — Step the focused time segment
Screen reader: The native time control is announced segment-by-segment by the platform. Callers should provide an `aria-label` (or wrap with `field`) naming the time being entered.
Notes: `color-scheme: dark` keeps the native picker chrome on-theme.
A controlled multi-value field. Committed tokens render as removable chips driven by the caller-owned `tokens` signal; Enter commits the trimmed draft (de-duplicated), Backspace on an empty field removes the last chip, and each chip has a × remove button. The in-progress draft is a component-scoped signal (created at the component owner, never inside a `<Show>`/`<For>`), so it is disposal-safe. The full list is emitted via `on_change` on every mutation.
Readiness
complete
Preview
live
Props
4
Examples
2
Code
implementation mapped
When to use
Free-form tags, labels, or keywords
Email-recipient-style entry where each value becomes a chip
Any open-vocabulary multi-value field
When not to use
A fixed option set — use a multi-select / SelectMulti
Single value — use Input or CodeInput
Read-only display of values — use a row of Badges
Accessibility
Role: combobox
Enter — Commit the current text as a chip
Backspace — Remove the last chip when the field is empty
Tab — Focus to / from the draft field
Screen reader: The wrapper is a labelled group; each chip's remove control has an `aria-label`. Callers should wrap with `field` (or provide an `aria-label`) so the field's purpose is announced; committed tokens are read as part of the group.
Notes: Chip add/remove is reflected immediately in the DOM so assistive tech tracks the current set; the remove-button hover transition is suppressed under `prefers-reduced-motion: reduce`.