Skip to the page
Tactile UI

Kit parts

Rich editor

A plain rich-text field with no dependency. A toolbar of keys sits over an editing surface in the field's well: two heading levels, bold, italic, code, bulleted and numbered lists, a quote, a code block and a link. The keys stay down while the text under the caret has that format. What comes out is always the same small set of clean HTML (p, h2, h3, strong, em, code, pre, blockquote, ul, ol, li and a with an address), whatever was pasted in, because everything that goes in, by typing, pasting, dropping or setting the value, is cleaned by a strict allow-list. It is a real form field: it submits, resets, validates, counts and goes off like any other.

On this page
  1. Anatomy
  2. Variants
  3. States
  4. Props and options
  5. Usage notes
  6. Accessibility
  7. Related parts

Seats open in 11 days. Want one email that morning?

Anatomy

The field's well with the kit's toolbar and toggle keys above it. The command layer uses the browser's own editing commands for inline marks, lists and blocks, so undo, redo and the spell checker keep working; everything the editor reports, submits or copies goes through the sanitiser, which has its own small tokenizer and an allow-list and never gives an untrusted string to innerHTML. The sanitiser is the same code in the browser and in Node, so it is tested without a DOM.

Variants

  • Default · empty, with its placeholder
  • With help text and a count · maxlength
  • Required · empty is not valid
  • Filled · every kind of block
  • Error · aria-invalid with its own message
  • Over the limit
  • Disabled
  • Read only · the text can be read and selected
  • The cleaner · paste junk, see what is kept

States

  • Empty
  • Filled
  • Focused

Props and options

NameKindDescription
required · minlength · maxlengthattributeRequired is met when there is text. The lengths count characters of text, not tags. The limit is soft: the count turns to a warning, the form will not submit, and typing is never cut off.
disabled · readonlyattributeBoth turn the toolbar off. Disabled leaves the tab order; read only keeps the text focusable and selectable but not editable.
data-placeholder · placeholderattributeShown while the editor is empty. It is drawn with CSS, so it is never part of the value.
aria-describedbyattributeMarks and announces the error state. The page writes the message and points aria-describedby at it, as for any field. The field also draws its own validity message after a first blur or a submit.
valuepropertyThe clean HTML. Setting it cleans what is given. An editor with no text reads as "".
plainText · lengthpropertyThe text, and the number of characters counted against the limits.
input · changeeventinput on every edit; change when focus leaves the editor and the value is not what it was.
sanitizeHTML() · htmlToText() · plainToHTML() · richSummary() · pasteHTML()functionClean any HTML to the allow-list, read it as text, turn plain text into paragraphs, and everything in one pass. They run in Node with no DOM.
safeHref() · linkHref() · lineStartShortcut() · shortcutAction() · formatState() · richSays() · countText() · countState() · richValidity()functionWhich addresses are kept (http, https, mailto, tel), what the link row makes of what is typed, the line-start and key shortcuts, which keys are down for a caret, what is said aloud, and the count and its validity.

Usage notes

Select a few words and press B: the key stays down and the words are bold. Move the caret into them and the key is down; move out and it comes up. Type "- " at the start of a line for a bullet, "## " for a heading. Press Ctrl+K (Command+K on a Mac) for a link. Paste something from a web page or a document: it comes in clean. The value under the editor is what the form would send.

Accessibility

  • Every toggle key keeps its name (Bold, Italic) and says whether it is down with aria-pressed, which follows the caret and the selection. Escape on the toolbar puts the caret back in the text.
  • A polite status line says what a command did: "Bold on", "Bold off", "Heading 2", "Bulleted list on", "Link added".
  • The shortcuts are listed on each key as aria-keyshortcuts (Control or Meta by platform) and printed on its tooltip: Mod+B bold, Mod+I italic, Mod+E code, Mod+K link, Mod+Shift+8 bulleted list, Mod+Shift+7 numbered list, Mod+Shift+9 quote, Mod+Alt+2 and 3 headings, Mod+Alt+E code block. Tab is never trapped, and undo and redo are the browser's own.
  • The link row is a small group with a labelled address field, Apply and Remove. Enter applies, Escape closes it and returns the focus to the key (or to the text, if it was opened by Ctrl+K). A bad address is marked aria-invalid and said aloud, not silently dropped.

Field

Text entry in a recess, the label printed above it, and a hint that says how to fix an error.

Toolbar

A rack of keys in one slot cut into the plate, under one tab stop. Keys, toggle keys, a copy key and a menu key sit side by side, parted into clusters by engraved lines, and the arrow keys move along it so a keyboard user passes the whole rack with one Tab.

Toggle key

One key that latches below the plate and stays there. It is the kit’s own key with one state to keep, down or up, so it is the way to say mute, pin, bold or armed without the weight of a switch. A lamp in the cap may light while it is down.

Composer

The prompt box. One deep recess holds the text and the keys sit on its floor: attach at one end; at the other a small LCD that counts tokens, amber near the limit and red over it, and the send key, which becomes a stop key while a reply runs. It is a real form with a labelled text area. Enter sends and Shift and Enter make a new line, and the send key is disabled while there is nothing to send. The text grows as you type, up to a height, then scrolls.

Log pad

A writing pad that is still a real textarea. A sheet of ruled paper is set into a machined frame, and the rules move with the words. A small LCD in the foot counts characters or words: it lights amber near the limit and red over it, and the limit is said once, in words, when it is crossed. The pad grows with the text up to a height you choose.