Skip to content

Text fields

Selection and input

In Material Design 3

A text field lets people enter and edit text. Material Design 3 has two kinds: a filled field, a tinted container with a line under it, and an outlined one, a field with a border. The label is the field's name: it sits in the middle as a hint and moves up, smaller, when the field has focus or text. Supporting text, a character counter, icons before and after, and an error state with its message belong to the field.

In Tesserae

widget: TextField is a view Tesserae ships (TextField_View.yaml) with its look as rules (TextField_Stylesheet.yaml). It is built from the TextInput widget, the bare input, and the nodes around it, so every part has a name a stylesheet can address.

Property Type Meaning
variant filled or outlined the look; filled by default
label text the field's name; it also names the input for a screen reader
text text, two-way what is typed: give it a bare reference and typing writes it
placeholder text shown while the field is empty and has focus (or has no label)
supporting text help under the field
error text an error message: the field takes the error look and the message replaces the supporting text
leading, trailing icon name an icon before and after the text; an error replaces the trailing icon
prefix, suffix text text around the typing, such as $ and USD
max_length whole number the most characters it takes; 0 is no limit
counter true or false shows how many characters are typed, of max_length when there is one
multiline true or false several lines; Enter makes a new one
obscured true or false a password: dots, with a button that shows the text
disabled, read_only true or false dimmed and takes no typing; the text can be selected and copied but not changed
suggestions a list of texts those that contain what is typed show in a menu under the field (up to six); pressing one fills the field in; Escape closes the menu
mask a pattern what is typed is fitted to it (###-####)
on_edit a handler called after each edit, once text has been written

Events: on_key for each key pressed in the field, on_edit after each edit, and on_submit for Enter in a field that is not multiline. A handler written on the field is called as the user types.

The parts a stylesheet can address are box, label, input, prefix, suffix, leading_icon, trailing_icon, error_icon, reveal_button, reveal_icon, indicator (the line under a filled field), supporting and counter. The state of a rule is the state of the whole field: focused is true while anything inside it has the focus, hovered while the pointer is over it, and error and disabled read the field's own properties. The label glides between its two places (its position eases in 150 milliseconds; its size changes at once, as the engine cannot ease a font size). A filled field has 4 pixel top corners and a square bottom, over its line. The input says it is described by the help line, so a screen reader reads that too, and it is invalid while there is an error. Not built: moving into the suggestions with the arrow keys (press one with the pointer), the input method's composition drawn by Tesserae (the engine draws the candidate window at the caret), and an exposed dropdown that is a select.

This component is a view Tesserae ships: use widget: TextField in a view (see The View Language).

Using it

name: signup
widget: Container
style: {flex_direction: vertical, width: 360, height: 420, gap: 16, padding: 16}
children:
  - widget: TextField
    name: email
    label: Email
    leading: search
    text: "{{ email }}"
    error: "{{ email_error }}"
    supporting: We never share it
    handlers: {on_submit: sign_up}
  - widget: TextField
    name: password
    variant: outlined
    label: Password
    obscured: true
    text: "{{ password }}"
  - widget: TextField
    name: bio
    label: About you
    multiline: true
    max_length: 140
    counter: true
    text: "{{ bio }}"

TextInput is the bare input, for when the field around it is your own: text, placeholder, multiline, obscured, max_length, read_only and disabled, drawn as one line that scrolls.

Changing its look

Write rules for TextField in your app's stylesheet; they sit above the shipped ones, and a style: on the node beats every rule for the fields it sets.

styles:
  - widget: TextField
    part: box
    variant: filled
    style: {background: surface_container_high, corner_radius: 12}
  - widget: TextField
    part: indicator
    state: focused
    style: {background: tertiary}

A view named TextField_View.yaml in your project replaces the shipped field, and its shipped look with it. In a view written with kind:, kind: TextField is the bare input (the TextInput widget), with text: {placeholder, multiline, obscured} for those three; tesserae migrate-yaml turns it into widget: TextInput.

See also