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.
Examples
Editor
Select text and use the toolbar, or move through it with the arrow keys. The pressed state follows the cursor.
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
<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.
<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
| Part | Description |
|---|---|
| Label | A field label for the content. |
| Toolbar | role="toolbar" in groups divided by hairlines, on layer-02. |
| Tool | A square icon button with an accessible name and aria-pressed. |
| Content | A contenteditable region with role="textbox", aria-multiline and flux-prose. |
API
| Name | Kind | Description |
|---|---|---|
flux-editor | class | The field surface holding the toolbar and the content. |
data-flux-editor | attribute | Wires the toolbar, its roving focus and its pressed states. |
flux-editor__toolbar | __group | class | The toolbar, with role="toolbar" and a label, and its groups. |
flux-editor__tool | class | A toolbar button. Give it an aria-label and aria-pressed. |
data-command | attribute | On a tool: bold, italic, heading, quote, bullets or numbers. |
flux-editor__content | class | The contenteditable region. Add flux-prose, role="textbox", aria-multiline="true" and aria-labelledby. |
data-placeholder | attribute | On the content: shown while it is empty. It does not replace the label. |
flux-prose | class | Styles rich text anywhere it is read, held to the reading measure. |
States
| State | Treatment |
|---|---|
| Default | The field border around the toolbar and the content. |
| Hover | The field hover border. |
| Focus | The border turns border-focus while focus is inside, and the content draws an inset focus ring. |
| Tool pressed | surface-selected fill and icon-brand, with aria-pressed="true". |
Keyboard
| Keys | Action |
|---|---|
Tab | Moves to the toolbar, which is one stop, then to the content. |
← → Home End | In the toolbar, moves between tools. |
Enter, Space | On a tool, applies or removes its format. |
⌘B, ⌘I or Ctrl B, Ctrl I | In 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.