Typography

Titles, paragraphs and inline text, with ellipsis, copy, edit and the semantic color types.

Accessibility

Typography renders the structure (the heading tag, the decoration elements, the copy and edit buttons) and leaves two things to you: whether the level is right, and whether color is the only thing carrying a meaning.

Level is structure, not appearance

level picks the tag: level={3} renders <h3>, and a screen reader's heading list, the browser outline and every “next heading” shortcut read that tag. A level picked because it looks right changes the structure for a visual reason, and nothing on screen shows it.
  • level defaults to 1, so a bare <Title> in a card claims to be the page's subject. Pass the level every time.
  • Do not skip levels going down. A reader moving by heading uses them to learn the shape of the page.
  • Title stops at level 5. Anything outside 1 to 5 logs a warning in development and renders an <h1>, so clamp a computed level yourself.
  • When the outline needs a level the design does not want, keep the level and change the class.

The type colors are not a signal

type sets a color and nothing else: no role, no icon, no text. A <Text type="danger"> and a <Text type="success"> are the same element in different colors, and read aloud they sound the same.
  • The words have to carry the meaning. “Failed” in red is fine. A red number that means “overdue” is not, and neither is a green check that is only a color.
  • Where the status matters and the text around it cannot say it, add the icon or the word yourself.
  • disabled only changes the color, to a lower contrast. On text it is decoration. When the text belongs to a control, disable the control.

What an ellipsis does to the text

Truncation works two ways, and they expose different things. The CSS way keeps the whole text in the page and hides the overflow, so a screen reader still gets all of it. The measured way shortens the text itself, and the full text survives only in a label.
  • CSS. A single line, or rows above 1 where the browser supports line clamping. The text is complete for assistive technology and for find-in-page.
  • Measured. An expandable block, a suffix, or a browser without line clamping renders shortened text, hides it and the ellipsis from screen readers, and puts the full text in an aria-label. That only works when the children are a string or a number. With JSX children there is nothing to put in the label, and the cut text is all that is left.
  • ellipsis.tooltip shows the full text, and is also used for that aria-label. It works on hover and focus, and not on touch.
  • expandable renders a real <button> labeled “Expand” and “Collapse” from the locale, so it works from the keyboard. If you change the text with symbol, the aria-label still comes from the locale.

Naming the copy and edit buttons

Both are real <button type="button"> elements, so they are in the tab order and work with Enter and Space. Their names come from the locale, so they are translated but generic.
  • The copy button is labeled “Copy”, and “Copied” after a copy. There is no live region, so only a reader whose focus is already on the button hears the change. It confirms the copy to the person who pressed it.
  • “Copy” alone does not say copy what. Where several sit on one screen, label each with copyable.tooltips, a pair for before and after copying. The tooltip text is also the button's aria-label, so one prop fixes both.
  • Pass tooltips as plain strings. If editable.tooltip is a node, the edit button gets an empty name. On the copy button, a tooltip that is not a string falls back to the locale label.
  • Keep the default copyable.tabIndex and editable.tabIndex. A -1 takes the button off the keyboard and leaves no other way to reach it.

Editing mode and focus

editable swaps the text for a text area in place, which moves focus in and back out. Both are handled for you, as long as the edit button is there.
  • On entry the text area takes focus with the caret at the end of the value.
  • On exit focus goes back to the edit button, so a keyboard reader stays in place.
  • Enter and blur commit, and Escape cancels. Keys pressed while an IME is composing are skipped, so the first Enter in Japanese or Korean input confirms the character rather than the edit.
  • triggerType: ["text"] alone works with a mouse only. Without icon there is no button, and the text takes the click with no role, no tab stop and no key handler. Use ["icon", "text"] to make the text clickable too.
  • The text area takes the aria-label of the text it replaced, and nothing else names it. Where the text around it does not make the field clear, give the Typography element an aria-label.

Contrast of the quiet text

The text colors are see-through, so their contrast depends on what is behind them. On the default backgrounds:
  • secondary is about 7.0:1 in light mode and 8.2:1 in dark, fine for body text.
  • tertiary is about 3.4:1 in light mode and 4.5:1 in dark. It fails 4.5:1 for normal text in light mode. It passes 3:1 for large text (18.66px bold or 24px), so keep it for decorative or large text, and never for the only copy of a fact.
  • disabled is about 1.8:1 in light mode and 2.2:1 in dark, below every threshold. Disabled text must never be the only place something is said.
  • mark has a fixed yellow background and keeps the text color, so in dark mode it puts light text on a light highlight. Check it in both modes.
  • A custom theme changes all of these. The numbers above are for the default theme, so measure again with your own colorTextBase or a tinted surface.

Motion

The only motion here is the copy icon swap, and it only plays with motion-safe:, so a reader who asked for reduced motion sees the icons change without movement. Icons you pass through copyable.icon change without animation.
  • Put anything you add, like a highlight on copy or a slide on expand, behind motion-safe: too.
  • To turn off every transition in the project, set theme={{ token: { motion: false } }}. That applies to everyone, while motion-safe: follows each reader's setting.