Markdown editor
<gui-markdown> is a Markdown editor. It renders a native <textarea> under a toolbar of
formatting buttons, with a preview of the rendered text. Its value is the Markdown source.
import { GuiMarkdown } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function MarkdownBasic() { return <GuiMarkdown label="Release notes" />;}import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import '@golemui/gui-components/markdown';
@Component({ selector: 'app-markdown-basic', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: `<gui-markdown label="Release notes"></gui-markdown>`,})export class MarkdownBasicComponent {}<script setup lang="ts">import '@golemui/gui-components/markdown';import '@golemui/gui-components/index.css';</script>
<template> <gui-markdown label="Release notes"></gui-markdown></template>import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import '@golemui/gui-components/markdown';import '@golemui/gui-components/index.css';
@customElement('markdown-basic')export class MarkdownBasic extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <gui-markdown label="Release notes"></gui-markdown> `; }}<gui-markdown label="Release notes"></gui-markdown>The element’s entry point is @golemui/gui-components/markdown, which registers <gui-markdown>
and <gui-markdown-text>, the element that draws its preview. To install the package and load
the stylesheet, see Installation.
The @golemui/gui-components/react components have typed props and an on* prop for each event. React 18 or later.
import { useState } from 'react';import { GuiMarkdown } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function NotesField() { const [notes, setNotes] = useState('');
return ( <GuiMarkdown label="Release notes" name="notes" value={notes} onGuiChange={(event) => setNotes(event.detail.value ?? '')} /> );}Add CUSTOM_ELEMENTS_SCHEMA to the component that uses the tags, and the stylesheet to styles in angular.json.
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import type { GuiEventMap, GuiMarkdownEvents } from '@golemui/gui-components';import '@golemui/gui-components/markdown';
type ChangeEvent = GuiEventMap<typeof GuiMarkdownEvents>['gui-change'];
@Component({ selector: 'app-notes-field', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <gui-markdown label="Release notes" name="notes" [value]="notes" (gui-change)="onChange($event)" ></gui-markdown> `,})export class NotesFieldComponent { notes = '';
onChange(event: Event) { this.notes = (event as ChangeEvent).detail.value ?? ''; }}"styles": ["node_modules/@golemui/gui-components/lib/styles/index.css"]Tell Vue's compiler that gui-* tags are custom elements, and add the typings once for typed props and @gui-* handlers.
<script setup lang="ts">import { ref } from 'vue';import '@golemui/gui-components/markdown';import '@golemui/gui-components/index.css';
const notes = ref('');</script>
<template> <gui-markdown label="Release notes" name="notes" :value="notes" @gui-change="notes = $event.detail.value ?? ''" /></template>vue({ template: { compilerOptions: { isCustomElement: (tag) => tag.startsWith('gui-') } },});import type {} from '@golemui/gui-components/vue';The page's stylesheet styles the gui-* elements, so it only reaches them outside a shadow root. Render the host component into the light DOM too, as below, or load the stylesheet into its shadow root.
import { LitElement, html } from 'lit';import { customElement, state } from 'lit/decorators.js';import '@golemui/gui-components/markdown';import '@golemui/gui-components/index.css';
@customElement('notes-field')export class NotesField extends LitElement { @state() private notes = '';
protected override createRenderRoot() { return this; }
override render() { return html` <gui-markdown label="Release notes" name="notes" .value=${this.notes} @gui-change=${(event: CustomEvent) => (this.notes = event.detail.value)} ></gui-markdown> `; }}With any bundler, such as Vite. Import the element once, anywhere, and use the tag in your markup.
<gui-markdown label="Release notes" name="notes"></gui-markdown>
<script type="module" src="./main.js"></script>import '@golemui/gui-components/markdown';import '@golemui/gui-components/index.css';
const element = document.querySelector('gui-markdown');
element.addEventListener('gui-change', (event) => { console.log(event.detail.value);});Properties
| Property | Type | Default | Description |
|---|---|---|---|
localeId
attribute locale-id | string | 'en' | BCP 47 locale for formatting and parsing, such as en-US or es. |
value | string | – | The Markdown text. |
tools | string[] | – | Toolbar buttons, in order: H heading, B bold, I italic, S strikethrough, Q quote, L link, OL and UL lists, and | for a separator. All by default. |
placeholder | string | – | Text shown while the control is empty. |
autocomplete | string | – | The autocomplete hint passed to the inner native control. |
counterMode
attribute counter-mode | 'remaining' | 'current' | – | With maxLength, whether the counter shows the characters left (remaining) or used (current). |
minimumHeight
attribute minimum-height | number | – | Minimum height of the field, in pixels. |
autoGrow
attribute auto-grow | boolean | false | Grows the field with its content instead of scrolling. |
defaultOpenPreview
attribute default-open-preview | boolean | – | Opens with the preview shown. |
maxLength
attribute maxlength | number | – | The number of characters the counter counts against. It only drives the counter: longer text is not blocked, and the counter marks it as over the limit. |
headingTitle
attribute heading-title | string | – | Tooltip and accessible name of the heading button. An empty value keeps the default. |
boldTitle
attribute bold-title | string | – | Tooltip and accessible name of the bold button. An empty value keeps the default. |
italicTitle
attribute italic-title | string | – | Tooltip and accessible name of the italic button. An empty value keeps the default. |
strikethroughTitle
attribute strikethrough-title | string | – | Tooltip and accessible name of the strikethrough button. An empty value keeps the default. |
quoteTitle
attribute quote-title | string | – | Tooltip and accessible name of the quote button. An empty value keeps the default. |
linkTitle
attribute link-title | string | – | Tooltip and accessible name of the link button. An empty value keeps the default. |
orderedListTitle
attribute ordered-list-title | string | – | Tooltip and accessible name of the numbered list button. An empty value keeps the default. |
unorderedListTitle
attribute unordered-list-title | string | – | Tooltip and accessible name of the bulleted list button. An empty value keeps the default. |
splitViewTitle
attribute split-view-title | string | – | Tooltip and accessible name of the preview button. An empty value keeps the default. |
toolbarAriaLabel
attribute toolbar-aria-label | string | – | Accessible name of the toolbar. An empty value removes it. |
dependencies | { markdown?: MarkdownParser } | – | Provides the markdown parser used by the preview. The preview inserts its HTML without sanitizing it, see MarkdownParser. |
uid | string | – | The id of the element's control. The ids of its label, hint and errors derive from it. Optional: when it is not set, the element generates one from its tag name and a page-wide counter, e.g. gui-textinput-3. Generated ids depend on creation order, so a server-rendered element must set uid or its ids will not match after hydration. |
Form control properties
Every field has these, from the base class of the form controls.
| Property | Type | Default | Description |
|---|---|---|---|
name | string | – | The name the value is submitted under. Without it the element stays out of its form. Reflected, like a native control's: the form reads a single value's name from the attribute. |
label | string | – | The visible label, which also names the control for assistive technology. Without one the control has no accessible name; the date, time, range, tags and file fields warn about it in development builds. |
hint | string | – | Help text shown under the label and announced as the control's description. |
errors | string[] | [] | Error messages to show, typically from your own validation. They show as soon as they are set, unless touched is false. |
touched | boolean | – | Whether the user has interacted with the control. Leave it unset unless you validate on interaction: false holds errors back until it becomes true. |
required | boolean | false | The control needs a value: it is marked as required and, with a name, blocks submission. |
readOnly
attribute readonly | boolean | – | The value can be read and focused but not changed. |
disabled | boolean | – | Disabled by its own attribute or by a disabled <fieldset> around it. Setting the property sets the attribute, like a native control's. |
form read-only, no attribute | HTMLFormElement | null | – | The <form> the element belongs to, if any. |
validity read-only, no attribute | ValidityState | – | The element's validity, like a native control's. |
validationMessage read-only, no attribute | string | – | The message the browser shows for an invalid value. |
Methods
| Method | Description |
|---|---|
checkValidity(): boolean | – |
reportValidity(): boolean | – |
Events
Every event bubbles and is composed, so you can listen on any ancestor.
| Event | Description |
|---|---|
gui-input | The user changed the value. detail.value is the new value. |
gui-change | The user committed the text, on blur or with a toolbar command. detail.value is the Markdown. |
gui-blur | Focus left the control. |
CSS custom properties
| Property | Default | Description |
|---|---|---|
--gui-md-text-color | – | Text color. |
--gui-md-line-height | – | Line height. |
--gui-md-heading-color | – | Heading color. |
--gui-md-heading-weight | – | Heading font weight. |
--gui-md-link-color | – | Link color. |
--gui-md-link-hover-color | – | Link color on hover. |
--gui-md-blockquote-bg | – | Quote background. |
--gui-md-blockquote-border-color | – | Quote border color. |
--gui-md-blockquote-color | – | Quote text color. |
--gui-md-code-bg | – | Code background. |
--gui-md-code-color | – | Code text color. |
--gui-md-code-radius | – | Code corner radius. |
--gui-md-hr-color | – | Horizontal rule color. |
Examples
Section titled “Examples”The toolbar
Section titled “The toolbar”Each button writes Markdown into the text, at the caret or around the selection:
| Button | Writes |
|---|---|
| Heading | # at the start of each selected line. |
| Bold | ** around the selection. |
| Italic | _ around the selection. |
| Strikethrough | ~~ around the selection. |
| Quote | > at the start of each selected line. |
| Link | [ and ](url) around the selection. |
| Ordered List | 1. , 2. … at the start of each selected line. |
| Unordered List | - at the start of each selected line. |
A button shows as pressed while the caret is inside its format, and pressing it again removes the format. The last button, Split View, shows and hides the preview.
tools picks the buttons and their order, as a JSON array of H (heading), B (bold), I
(italic), S (strikethrough), Q (quote), L (link), OL and UL (the lists), and | for a
separator. The default is ["H", "B", "I", "S", "Q", "L", "|", "OL", "UL"]. The Split View button
is always there, at the end.
import { GuiMarkdown } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
const tools = ['B', 'I', 'L', '|', 'UL'];
export function MarkdownTools() { return <GuiMarkdown label="Comment" tools={tools} />;}import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import '@golemui/gui-components/markdown';
@Component({ selector: 'app-markdown-tools', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: `<gui-markdown label="Comment" [tools]="tools"></gui-markdown>`,})export class MarkdownToolsComponent { tools = ['B', 'I', 'L', '|', 'UL'];}<script setup lang="ts">import '@golemui/gui-components/markdown';import '@golemui/gui-components/index.css';
const tools = ['B', 'I', 'L', '|', 'UL'];</script>
<template> <gui-markdown label="Comment" :tools.prop="tools"></gui-markdown></template>import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import '@golemui/gui-components/markdown';import '@golemui/gui-components/index.css';
const tools = ['B', 'I', 'L', '|', 'UL'];
@customElement('markdown-tools')export class MarkdownTools extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <gui-markdown label="Comment" .tools=${tools}></gui-markdown> `; }}<gui-markdown label="Comment" tools='["B", "I", "L", "|", "UL"]'></gui-markdown>Preview
Section titled “Preview”The preview renders the Markdown as HTML. The element ships no Markdown parser: give it yours in
the dependencies property, as { markdown: { parse } }, where parse takes Markdown and returns
HTML. In TypeScript, parse returns SanitizedHtml: see
The parser. Without one the preview stays
empty. default-open-preview opens the editor with the
preview shown.
import type { SanitizedHtml } from '@golemui/gui-components';import snarkdown from 'snarkdown';import { GuiMarkdown } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
const dependencies = { markdown: { parse: (md: string) => snarkdown(md) as SanitizedHtml },};
const recipe = `## Pastel de nata
Bake at **250 °C** for _12 minutes_.
- Puff pastry- Egg yolks- Cinnamon`;
export function MarkdownPreview() { return ( <GuiMarkdown id="recipe" label="Recipe" defaultOpenPreview value={recipe} dependencies={dependencies} /> );}import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import type { SanitizedHtml } from '@golemui/gui-components';import snarkdown from 'snarkdown';import '@golemui/gui-components/markdown';
@Component({ selector: 'app-markdown-preview', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <gui-markdown id="recipe" label="Recipe" default-open-preview value="## Pastel de nata Bake at **250 °C** for _12 minutes_. - Puff pastry - Egg yolks - Cinnamon" [dependencies]="dependencies" ></gui-markdown> `,})export class MarkdownPreviewComponent { dependencies = { markdown: { parse: (md: string) => snarkdown(md) as SanitizedHtml }, };}<script setup lang="ts">import type { SanitizedHtml } from '@golemui/gui-components';import snarkdown from 'snarkdown';import '@golemui/gui-components/markdown';import '@golemui/gui-components/index.css';
const dependencies = { markdown: { parse: (md: string) => snarkdown(md) as SanitizedHtml },};
const recipe = `## Pastel de nata
Bake at **250 °C** for _12 minutes_.
- Puff pastry- Egg yolks- Cinnamon`;</script>
<template> <gui-markdown id="recipe" label="Recipe" :defaultOpenPreview="true" :value="recipe" :dependencies.prop="dependencies" ></gui-markdown></template>import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import type { SanitizedHtml } from '@golemui/gui-components';import snarkdown from 'snarkdown';import '@golemui/gui-components/markdown';import '@golemui/gui-components/index.css';
const dependencies = { markdown: { parse: (md: string) => snarkdown(md) as SanitizedHtml },};
@customElement('markdown-preview')export class MarkdownPreview extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <gui-markdown id="recipe" label="Recipe" default-open-preview value="## Pastel de nata Bake at **250 °C** for _12 minutes_. - Puff pastry - Egg yolks - Cinnamon" .dependencies=${dependencies} ></gui-markdown> `; }}<gui-markdown id="recipe" label="Recipe" default-open-preview value="## Pastel de nata Bake at **250 °C** for _12 minutes_. - Puff pastry - Egg yolks - Cinnamon"></gui-markdown>
<script type="module"> // In your app: import snarkdown from 'snarkdown'; import snarkdown from 'https://cdn.jsdelivr.net/npm/snarkdown@2/+esm';
document.querySelector('#recipe').dependencies = { markdown: { parse: snarkdown } };</script>- Narrow fields. While the element is under 768 pixels wide, the preview takes the place of the text instead of sitting next to it. Press Split View again to go back to the text.
- Styles. The preview is a
<gui-markdown-text>, with its text styles and--gui-md-*tokens. See Markdown text. - Untrusted text. The preview inserts the parser’s HTML as it is. When the text comes from other people, use a parser that escapes HTML, or sanitize its output. See Untrusted Markdown.
Counter and height
Section titled “Counter and height”maxlength, counter-mode, minimum-height and auto-grow work as on the
textarea: the counter counts the Markdown source,
and the limit doesn’t stop the user from typing past it.
import { GuiMarkdown } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
const tools = ['B', 'I', 'L', '|', 'UL'];
export function MarkdownCounter() { return ( <GuiMarkdown label="Changelog entry" maxLength={200} minimumHeight={80} autoGrow tools={tools} /> );}import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import '@golemui/gui-components/markdown';
@Component({ selector: 'app-markdown-counter', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <gui-markdown label="Changelog entry" maxlength="200" minimum-height="80" auto-grow [tools]="tools" ></gui-markdown> `,})export class MarkdownCounterComponent { tools = ['B', 'I', 'L', '|', 'UL'];}<script setup lang="ts">import '@golemui/gui-components/markdown';import '@golemui/gui-components/index.css';
const tools = ['B', 'I', 'L', '|', 'UL'];</script>
<template> <gui-markdown label="Changelog entry" :maxLength="200" :minimumHeight="80" :autoGrow="true" :tools.prop="tools" ></gui-markdown></template>import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import '@golemui/gui-components/markdown';import '@golemui/gui-components/index.css';
const tools = ['B', 'I', 'L', '|', 'UL'];
@customElement('markdown-counter')export class MarkdownCounter extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <gui-markdown label="Changelog entry" maxlength="200" minimum-height="80" auto-grow .tools=${tools} ></gui-markdown> `; }}<gui-markdown label="Changelog entry" maxlength="200" minimum-height="80" auto-grow tools='["B", "I", "L", "|", "UL"]'></gui-markdown>disabled disables the field and every button. readonly disables the formatting buttons but
keeps Split View, so the user can read the preview.
In a form
Section titled “In a form”With a name, <gui-markdown> submits the Markdown source, as text, under that name, and
required blocks an empty submit. It never submits the rendered HTML.
import { useState, type SubmitEvent } from 'react';import { GuiButton, GuiMarkdown } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function MarkdownNativeForm() { const [result, setResult] = useState('');
const onSubmit = (event: SubmitEvent<HTMLFormElement>) => { event.preventDefault(); const data = Object.fromEntries(new FormData(event.currentTarget)); setResult(`Submitted ${JSON.stringify(data)}`); };
return ( <form id="announcement" onSubmit={onSubmit}> <GuiMarkdown label="Announcement" name="body" required /> <GuiButton type="submit" label="Publish" /> <p id="announcement-result" role="status"> {result} </p> </form> );}import { Component, CUSTOM_ELEMENTS_SCHEMA, signal } from '@angular/core';import '@golemui/gui-components/button';import '@golemui/gui-components/markdown';
@Component({ selector: 'app-markdown-native-form', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <form id="announcement" #form (submit)="onSubmit($event, form)"> <gui-markdown label="Announcement" name="body" required></gui-markdown> <gui-button type="submit" label="Publish"></gui-button> <p id="announcement-result" role="status">{{ result() }}</p> </form> `,})export class MarkdownNativeFormComponent { result = signal('');
onSubmit(event: SubmitEvent, form: HTMLFormElement) { event.preventDefault(); const data = Object.fromEntries(new FormData(form)); this.result.set(`Submitted ${JSON.stringify(data)}`); }}<script setup lang="ts">import { ref } from 'vue';import '@golemui/gui-components/button';import '@golemui/gui-components/markdown';import '@golemui/gui-components/index.css';
const result = ref('');
function onSubmit(event: SubmitEvent) { if (!(event.currentTarget instanceof HTMLFormElement)) return; const data = Object.fromEntries(new FormData(event.currentTarget)); result.value = `Submitted ${JSON.stringify(data)}`;}</script>
<template> <form id="announcement" @submit.prevent="onSubmit"> <gui-markdown label="Announcement" name="body" :required="true" ></gui-markdown> <gui-button type="submit" label="Publish"></gui-button> <p id="announcement-result" role="status">{{ result }}</p> </form></template>import { LitElement, html } from 'lit';import { customElement, query, state } from 'lit/decorators.js';import '@golemui/gui-components/button';import '@golemui/gui-components/markdown';import '@golemui/gui-components/index.css';
@customElement('markdown-native-form')export class MarkdownNativeForm extends LitElement { @query('#announcement') private form!: HTMLFormElement; @state() private result = '';
protected override createRenderRoot() { return this; }
private onSubmit(event: SubmitEvent) { event.preventDefault(); const data = Object.fromEntries(new FormData(this.form)); this.result = `Submitted ${JSON.stringify(data)}`; }
override render() { return html` <form id="announcement" @submit=${this.onSubmit}> <gui-markdown label="Announcement" name="body" required></gui-markdown> <gui-button type="submit" label="Publish"></gui-button> <p id="announcement-result" role="status">${this.result}</p> </form> `; }}<form id="announcement"> <gui-markdown label="Announcement" name="body" required></gui-markdown> <gui-button type="submit" label="Publish"></gui-button> <p id="announcement-result" role="status"></p></form>
<script type="module"> const form = document.querySelector('#announcement');
form.addEventListener('submit', (event) => { event.preventDefault(); const data = Object.fromEntries(new FormData(form)); document.querySelector('#announcement-result').textContent = `Submitted ${JSON.stringify(data)}`; });</script>How the elements submit, validate and reset is in Native forms.
Styling
Section titled “Styling”Anatomy
Section titled “Anatomy”<gui-markdown> renders into the light DOM, so you can style every part with plain CSS. These
are the parts and their public classes:
<gui-markdown class="gui-field"> <label class="gui-label" for="notes" id="notes_label"> <span class="gui-label__text">Release notes</span> </label> <div class="gui-widget gui-markdown--with-preview"> <div class="gui-markdown__toolbar" role="toolbar" aria-label="Text formatting" > <ul role="presentation"> <li role="presentation"> <button type="button" class="gui-markdown__toolbar-button gui-markdown__toolbar-button--active" aria-label="Heading" aria-pressed="true" title="Heading" > <svg aria-hidden="true">…</svg> </button> </li> <!-- One item per tool. A separator: --> <li role="presentation"> <span class="gui-markdown__toolbar-separator" ><svg aria-hidden="true">…</svg></span > </li> <li role="presentation"> <button type="button" class="gui-markdown__toolbar-button gui-markdown__toolbar-button--active" aria-label="Split View" aria-pressed="true" title="Split View" > <svg aria-hidden="true">…</svg> </button> </li> </ul> </div> <div class="gui-markdown__container"> <textarea id="notes" class="gui-widget-input" style="min-height: 120px;" ></textarea> <section class="gui-markdown__preview" style="min-height: 120px;"> <gui-markdown-text ><h2>Pastel de nata</h2> …</gui-markdown-text > </section> </div> </div> <div class="gui-markdown--validation"> <div> <div class="gui-validator gui-validator--empty" id="notes_errors" role="alert" ></div> </div> <div class="gui-markdown--counter"> <span>200</span> <span> / 200</span> </div> </div></gui-markdown>| Class | Part |
|---|---|
gui-field | The element itself. Lines its parts up with the fields next to it in a gui-grid row. |
gui-label | The label, with the text and the hint. |
gui-label__text | The label text and the required marker. |
gui-widget-hint | The hint. |
gui-widget | The box around the toolbar, the text and the preview. |
gui-markdown--with-preview | On the box while the preview shows. |
gui-markdown__toolbar | The toolbar. |
gui-markdown__toolbar-button | A toolbar button. |
gui-markdown__toolbar-button--active | On a button while its format is under the caret, and on Split View while it is on. |
gui-markdown__toolbar-button--disabled | On the formatting buttons while the element is disabled or read-only. |
gui-markdown__toolbar-separator | A separator, from | in tools. |
gui-markdown__container | The row with the text and the preview. |
gui-widget-input | The <textarea>. |
gui-markdown__preview | The preview, while it shows. |
gui-markdown--validation | The row under the field, with the errors and the counter. |
gui-validator | The errors container. It is always there, so the alert role announces new errors. |
gui-validator--empty | On the errors container while there are no errors. |
gui-validator__error | One error message. |
gui-markdown--counter | The counter, with maxlength only. |
gui-markdown--counter__error | On the counter while the text is over the limit. |
The ids derive from uid, or from a generated id such as gui-markdown-3 when you don’t set one.
Select by class or by tag, not by id.
Tokens
Section titled “Tokens”The editor reads these design tokens. Set them on :root to change every editor, or on any
element to change the editors inside it:
| Token | What it styles |
|---|---|
--gui-radius-md | The corner radius of the box. |
--gui-border-default | The border of the box, under the toolbar and beside the preview. |
--gui-bg-surface | The background of the toolbar and the text. |
--gui-text-default | The text, the label and the toolbar icons. |
--gui-font-family | The font. |
--gui-font-sm | The text size of the label, the text and the counter. |
--gui-font-xs | The text size of the hint and the errors. |
--gui-text-secondary | The hint. |
--gui-space-1 | The toolbar’s top and bottom padding, the buttons’ padding and the gaps between them. |
--gui-space-2 | The side padding of the toolbar, the text and the preview, and the space over the counter. |
--gui-space-3 | The top and bottom padding of the text and the preview. |
--gui-space-7 | The height of the toolbar buttons. |
--gui-radius-sm | The corner radius of the toolbar buttons. |
--gui-bg-surface-hover | A toolbar button under the pointer. |
--gui-intent-primary | The icon of a button under the pointer, and the background of a pressed button. |
--gui-intent-primary-text | The icon of a pressed button. |
--gui-intent-primary-hover | A pressed button under the pointer. |
--gui-border-focus | The border while the text is focused. |
--gui-shadow-focus | The focus ring of the box and of the toolbar buttons. |
--gui-intent-error | The border, the messages and the counter over the limit. |
--gui-shadow-focus-error | The focus ring when there are errors. |
--gui-bg-disabled | The background of the text when disabled. |
--gui-text-disabled | The text when disabled. |
The preview also reads the --gui-md-* tokens: see
Markdown text.
To restyle beyond the tokens, see Overriding styles; for dark mode and the Clay theme, see Themes and dark mode.
Accessibility
Section titled “Accessibility”- Name. The
labelnames the text field. Without one the field has no accessible name. - Description and errors. The
hintis the field’s description, througharia-describedby. While errors show, the textarea hasaria-invalid="true"and points at them witharia-errormessage, and screen readers announce new errors as they appear. - Toolbar. The toolbar has the
toolbarrole and the name “Text formatting”.toolbar-aria-labelchanges it; an empty value removes it. - Buttons. Each button has a name and a tooltip, such as “Bold”, and
aria-pressedsays whether its format is under the caret, or whether the preview shows. The*-titleattributes, such asbold-titleandsplit-view-title, change them on one editor; an empty value keeps the default. To translate them for the whole app, use the keystextFormatting,heading,bold,italic,strikethrough,quote,link,orderedList,unorderedListandsplitView, see i18n. - Focus. A formatting button puts focus back in the text after it writes its format.
- Counter. The counter is visual only: screen readers don’t announce it.
| Key | Action |
|---|---|
| Tab | Moves focus through the toolbar buttons, one at a time, then to the text. |
| Enter or Space on a button | Applies or removes its format, or shows or hides the preview. |
| Enter in the text | Adds a new line. Leaving the text commits the value and fires gui-change. |
| Every editing key | Works as in any native textarea. There are no formatting shortcuts. |
What every element shares, such as translating the browser messages, is in Accessibility.