Skip to content

Text input

TextInput is the workhorse field for single-line strings — titles, slugs, emails, and numeric values. Orbit wraps native inputs with consistent label, hint, helper, prefix/suffix affixes, and validation rules that dehydrate with the form. Variants below show type modifiers, affix chrome, and interaction states you can combine on one field.

Each variation below includes a short explanation, the fluent API to paste into your schema, and a screenshot of the rendered control.

Label, placeholder, and helper text — the default starting point.

TextInput.make('title')
.label('Title')
.placeholder('Enter a title…')
.helper_text('Shown on the public page.')

Orbit Basic text input (light)

Orbit Basic text input (dark)

Sets input type to email and adds an email validation rule.

TextInput.make('email')
.email()
.label('Email')
.placeholder('you@acme.test')

Orbit Email (light)

Orbit Email (dark)

Password type with an optional reveal toggle for accessibility.

TextInput.make('password')
.password()
.revealable()
.label('Password')

Orbit Password (revealable) (light)

Orbit Password (revealable) (dark)

URL input type with built-in url validation.

TextInput.make('website')
.url()
.label('Website')
.placeholder('https://')

Orbit URL (light)

Orbit URL (dark)

Tel input type for phone numbers.

TextInput.make('phone')
.tel()
.label('Phone')

Orbit Telephone (light)

Orbit Telephone (dark)

Number input with optional min/max bounds.

TextInput.make('quantity')
.numeric()
.label('Quantity')
.min_value(0)
.max_value(99)

Orbit Numeric (light)

Orbit Numeric (dark)

Static text before the control — common for currency symbols.

TextInput.make('price')
.label('Price')
.prefix('$')
.numeric()

Orbit Prefix text (light)

Orbit Prefix text (dark)

Static text after the control — units, domains, etc.

TextInput.make('weight')
.label('Weight')
.suffix('kg')
.numeric()

Orbit Suffix text (light)

Orbit Suffix text (dark)

Heroicon rendered inside the affix rail.

TextInput.make('search')
.label('Search')
.prefix_icon('heroicon-o-magnifying-glass')

Orbit Prefix icon (light)

Orbit Prefix icon (dark)

Trailing icon affix for links, locks, or status.

TextInput.make('slug')
.label('Slug')
.suffix_icon('heroicon-o-link')

Orbit Suffix icon (light)

Orbit Suffix icon (dark)

Shows the required asterisk and injects a required rule.

TextInput.make('name')
.label('Name')
.required()

Orbit Required (light)

Orbit Required (dark)

Non-interactive state for read-only contexts.

TextInput.make('locked')
.label('Locked field')
.disabled()

Orbit Disabled (light)

Orbit Disabled (dark)

Value visible but not editable — good for generated IDs.

TextInput.make('id')
.label('Record ID')
.readonly()

Orbit Readonly (light)

Orbit Readonly (dark)

Adds a one-click copy button beside the input.

TextInput.make('token')
.label('API token')
.copyable()
.readonly()

Orbit Copyable (light)

Orbit Copyable (dark)

Client-side mask pattern for structured values like card numbers.

TextInput.make('card')
.label('Card number')
.mask('9999 9999 9999 9999')

Orbit Input mask (light)

Orbit Input mask (dark)

Native datalist autocomplete from a string list.

TextInput.make('city')
.label('City')
.datalist(['Nairobi', 'London', 'Berlin'])

Orbit Datalist suggestions (light)

Orbit Datalist suggestions (dark)

Inline hint text and optional hint icon above the control.

TextInput.make('slug')
.label('Slug')
.hint('Used in the public URL.')
.hint_icon('heroicon-o-information-circle')

Orbit With hint (light)

Orbit With hint (dark)

.trim() marks the field so dehydrate strips leading and trailing whitespace before any custom .dehydrate_state_using() callback runs. It does not change the live input type or strip characters mid-typing — only the value that leaves the form. Use it on titles, slugs, and free-text fields where accidental spaces would break uniqueness or URLs.

app/orbit/resources/post_resource.py
TextInput.make('title')
.label('Title')
.trim()

Orbit Trim (light)

Orbit Trim (dark)

.strip_characters() removes every listed character from the dehydrated string. Pass a string (each char is stripped) or a sequence of strings that are joined into the removal set. Strip runs before trim inside apply_dehydrate_transforms, so you can clear dashes/spaces and then trim leftovers. Ideal for SKUs, IBAN paste clean-up, and phone numbers that should store digits only.

app/orbit/resources/product_resource.py
TextInput.make('sku')
.label('SKU')
.strip_characters(['-', ' '])
.trim()

Orbit Strip characters (light)

Orbit Strip characters (dark)

.length(n) records an exact character length and appends a size:n validation rule. Pair it with .mask() or OTP-style inputs when the UI already constrains width. Prefer .min_length() / .max_length() (mapped to enforced min: / max: rules) when you need a range — Form.validate currently enforces min/max/between/regex, while size: is registered by the fluent helper so hosts and future rule coverage can pick it up.

app/orbit/resources/auth_resource.py
TextInput.make('pin')
.label('PIN')
.length(4)
.numeric()

Orbit Exact length (light)

Orbit Exact length (dark)

.tel() only sets type="tel" for the mobile keyboard. .tel_regex(pattern) stores the pattern and adds a regex:… rule that Form.validate enforces. Combine both when you want native tel chrome plus a project-specific E.164 or national format.

app/orbit/resources/contact_resource.py
TextInput.make('phone')
.label('Phone')
.tel()
.tel_regex(r'^\+?[0-9\s\-]{7,20}$')

Orbit Telephone regex (light)

Orbit Telephone regex (dark)

.autocapitalize(value) emits the HTML autocapitalize attribute on the input (for example sentences, words, characters, or none). Browsers on mobile honor it for soft-keyboard behavior; desktop may ignore it. It does not rewrite state on dehydrate — pair with .trim() if you also want whitespace normalized.

app/orbit/resources/person_resource.py
TextInput.make('full_name')
.label('Full name')
.autocapitalize('words')

Orbit Autocapitalize (light)

Orbit Autocapitalize (dark)

.mark_as_required() controls the required asterisk independently of the required validation rule. Use it when the field is visually mandatory (asterisk) but validation is conditional via .required(callable) or sibling rules, or to hide the asterisk while still validating with .required(). shows_required_asterisk() prefers _mark_as_required when set; otherwise it mirrors is_required().

app/orbit/resources/profile_resource.py
TextInput.make('nickname')
.label('Nickname')
.mark_as_required()
.helper_text('Shown as required, but only validated when publishing.')

Orbit Mark as required (light)

Orbit Mark as required (dark)

.prefix_icon_color() / .suffix_icon_color() add an or-color-{color} class on the affix icon span when a matching .prefix_icon() / .suffix_icon() is present. Colors follow Orbit’s semantic tokens (primary, success, warning, danger, gray, …). Text affixes (.prefix() / .suffix()) are unchanged — these helpers only tint icon chrome.

app/orbit/resources/invoice_resource.py
TextInput.make('amount')
.label('Amount')
.numeric()
.prefix_icon('heroicon-o-currency-dollar')
.prefix_icon_color('success')
.suffix_icon('heroicon-o-check-circle')
.suffix_icon_color('primary')

Orbit Prefix and suffix icon colors (light)

Orbit Prefix and suffix icon colors (dark)

Every Field can inject HTML around the label, control, and error region via nine slots: .above_label(), .below_label(), .before_label(), .after_label(), .above_content(), .below_content(), .before_content(), .after_content(), and .below_error(). Slot bodies accept strings or callables and are not HTML-escaped (unlike labels), so you can drop muted captions, badges, or small links. Prefer slots over wrapping the field in a custom layout when you only need adjacent chrome.

app/orbit/resources/author_resource.py
TextInput.make('bio')
.label('Bio')
.above_label('<span class="or-badge">Public</span>')
.below_label('Shown on the author page.')
.above_content('Keep it under two sentences.')
.below_error('Fix validation before saving.')

Orbit Content slots (light)

Orbit Content slots (dark)

Closures work on .label(), .helper_text(), .placeholder(), content slots, .visible(), .disabled(), and .required() where applicable — see Form closures.