Flux 2.0

Component

Rich text editor

A field with a formatting toolbar for short structured text, and the prose styles it produces.

Plain-text source, for agents and clipboard use: /components/source/rich-text-editor.txt

When to use it

For text that needs a little structure: itinerary notes, supplier instructions, a description shown to travellers. The toolbar covers bold, italic, a heading, a quote and two kinds of list, as one tab stop with arrow-key movement, and each button shows whether its format applies at the cursor. The content is styled by flux-prose, the same class that renders the saved text elsewhere, so what is written is what is shown.

Not this
Not for plain text; use a Textarea. Not for long documents with tables, images and comments; that needs a dedicated editor engine. Not for code.

Examples

Editor

Select text and use the toolbar, or move through it with the arrow keys. The pressed state follows the cursor.

Itinerary notes

Day 2: Golden Circle

Pick-up from the hotel at 08:30. Bring a waterproof layer for Gullfoss.

  • Þingvellir National Park
  • Geysir geothermal area
  • Gullfoss waterfall
html
<div class="flux-field">
  <span class="flux-field__label" id="notes-label">Itinerary notes</span>
  <div class="flux-editor" data-flux-editor>
    <div class="flux-editor__toolbar" role="toolbar" aria-label="Formatting" aria-controls="notes">
      <div class="flux-editor__group">
        <button class="flux-editor__tool" type="button" data-command="bold" aria-pressed="false" aria-label="Bold"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4.5 3h4a2.5 2.5 0 0 1 0 5h-4zM4.5 8h4.75a2.5 2.5 0 0 1 0 5H4.5z" /></svg></button>
        <button class="flux-editor__tool" type="button" data-command="italic" aria-pressed="false" aria-label="Italic"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10.5 3H7M9 13H5.5M9.25 3l-2.5 10" /></svg></button>
      </div>
      <div class="flux-editor__group">
        <button class="flux-editor__tool" type="button" data-command="heading" aria-pressed="false" aria-label="Heading"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.5 3.5v9M7.5 3.5v9M2.5 8h5M10 7a1.75 1.75 0 1 1 3 1.25L10 12.5h3.5" /></svg></button>
        <button class="flux-editor__tool" type="button" data-command="quote" aria-pressed="false" aria-label="Quote"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 3.5v9M6.5 5h6.5M6.5 8h6.5M6.5 11h4" /></svg></button>
      </div>
      <div class="flux-editor__group">
        <button class="flux-editor__tool" type="button" data-command="bullets" aria-pressed="false" aria-label="Bulleted list"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6.5 4.5h7M6.5 8h7M6.5 11.5h7M3 4.5v.01M3 8v.01M3 11.5v.01" /></svg></button>
        <button class="flux-editor__tool" type="button" data-command="numbers" aria-pressed="false" aria-label="Numbered list"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M7 4.5h6.5M7 8h6.5M7 11.5h6.5M2.5 3.5l1-.5v3M2.5 9.5h2l-2 2.5h2" /></svg></button>
      </div>
    </div>
    <div class="flux-editor__content flux-prose" id="notes" contenteditable="true" role="textbox" aria-multiline="true" aria-labelledby="notes-label" data-placeholder="Add notes for the guide and the travellers"><h2>Day 2: Golden Circle</h2><p>Pick-up from the hotel at <strong>08:30</strong>. Bring a waterproof layer for Gullfoss.</p><ul><li>Þingvellir National Park</li><li>Geysir geothermal area</li><li>Gullfoss waterfall</li></ul></div>
  </div>
</div>

Prose

flux-prose renders the saved text anywhere it is read: a booking summary, a traveller document. Headings, lists, quotes, links and code take Flux sizes and roles.

Day 2: Golden Circle

Pick-up from the hotel at 08:30. Bring a waterproof layer for Gullfoss; the spray carries well beyond the viewing platform.

  • Þingvellir National Park
  • Geysir geothermal area
  • Gullfoss waterfall
Lunch is included at Friðheimar. Tell the guide about allergies at pick-up.

Booking reference KB-104931. Full terms are in the booking conditions.

html
<div class="flux-prose">
  <h2>Day 2: Golden Circle</h2>
  <p>Pick-up from the hotel at <strong>08:30</strong>. Bring a waterproof layer for Gullfoss; the spray carries well beyond the viewing platform.</p>
  <ul>
    <li>Þingvellir National Park</li>
    <li>Geysir geothermal area</li>
    <li>Gullfoss waterfall</li>
  </ul>
  <blockquote>Lunch is included at Friðheimar. Tell the guide about allergies at pick-up.</blockquote>
  <p>Booking reference <code>KB-104931</code>. Full terms are in the <a href="#">booking conditions</a>.</p>
</div>

Anatomy

PartDescription
LabelA field label for the content.
Toolbarrole="toolbar" in groups divided by hairlines, on layer-02.
ToolA square icon button with an accessible name and aria-pressed.
ContentA contenteditable region with role="textbox", aria-multiline and flux-prose.

API

NameKindDescription
flux-editorclassThe field surface holding the toolbar and the content.
data-flux-editorattributeWires the toolbar, its roving focus and its pressed states.
flux-editor__toolbar | __groupclassThe toolbar, with role="toolbar" and a label, and its groups.
flux-editor__toolclassA toolbar button. Give it an aria-label and aria-pressed.
data-commandattributeOn a tool: bold, italic, heading, quote, bullets or numbers.
flux-editor__contentclassThe contenteditable region. Add flux-prose, role="textbox", aria-multiline="true" and aria-labelledby.
data-placeholderattributeOn the content: shown while it is empty. It does not replace the label.
flux-proseclassStyles rich text anywhere it is read, held to the reading measure.

States

StateTreatment
DefaultThe field border around the toolbar and the content.
HoverThe field hover border.
FocusThe border turns border-focus while focus is inside, and the content draws an inset focus ring.
Tool pressedsurface-selected fill and icon-brand, with aria-pressed="true".

Keyboard

KeysAction
TabMoves to the toolbar, which is one stop, then to the content.
← → Home EndIn the toolbar, moves between tools.
Enter, SpaceOn a tool, applies or removes its format.
⌘B, ⌘I or Ctrl B, Ctrl IIn the content, bold and italic.

Accessibility

  • The toolbar has a label and roving tabindex, so it is one tab stop.
  • Each tool reports aria-pressed, so the state is not conveyed by fill alone.
  • The content has role="textbox" and aria-multiline, and is labelled by the visible label.
  • Formatting uses the browser’s editing commands so the component works without an engine; a product with its own editor engine binds the same toolbar markup to it.

Tokens

The tier-3 and tier-2 tokens this component binds to. Change the token, not the component.

  • --flux-editor-bg | -border | -border-hover
  • --flux-editor-toolbar-bg | -toolbar-padding
  • --flux-editor-tool-size | -tool-icon | -tool-hover | -tool-pressed-bg | -tool-pressed-icon
  • --flux-editor-content-padding | -min-height
  • --flux-prose-text | -gap | -heading-2 | -heading-3 | -quote-border | -quote-text | -link

Do and don’t

Do

  • Offer only the formats the text needs.
  • Render saved text with flux-prose so it matches the editor.
  • Sanitise the HTML on the server before it is stored or shown.

Don’t

  • Do not offer colour, font or size controls. Flux sets those.
  • Do not use the editor where a Textarea would do.