Integration
Rich text × Actual CSS
@lekoala/rich-text owns editing, sanitization, toolbar
behaviour, mentions and suggestions; Actual owns the visual language.
Switch themes and watch the surface, toolbar, focus, mentions and
suggestions follow the tokens — the editing engine never changes.
Squire remains the engine underneath the package.
Loading the editor…
Team note
The native <textarea> stays the baseline and the value
owner. Without the integration script it remains a plain textarea holding
raw HTML — honest, submittable, sanitized server-side either way.
Type @ to mention, / for snippets.
(nothing submitted yet)
Rich composer
The Actual composer chrome around a rich editor: attachments and
actions belong to the composer, never to the serialized HTML.
Enter stays a paragraph; sending goes through
Ctrl/Cmd+Enter or the send button. The Snippet and
Mention buttons reuse the same suggestion seam as
/ and @.
Bridge contract
Actual maps tokens, it does not restyle the editor's internals.
The bridge CSS stays in docs/demo — it is a recipe, not package API,
so no actual-richtext.css ships with the framework and no
widget version is pinned by it.
.actual-rich-text .rt-shell {
--rt-bg: var(--surface);
--rt-fg: var(--text);
--rt-border: var(--form-invalid-border, var(--control-border, var(--border)));
--rt-divider: var(--border);
--rt-muted: var(--text-muted);
--rt-focus: var(--form-invalid-border, var(--focus));
--rt-focus-width: var(--focus-ring-width);
--rt-radius: var(--radius);
--rt-font-size: var(--control-font-size);
--rt-min-height: 10rem;
--rt-toolbar-bg: var(--surface-subtle);
--rt-button-hover: var(--hover-overlay);
--rt-button-pressed-bg: var(--state-selected);
--rt-button-pressed-fg: var(--state-selected-fg);
--rt-disabled-bg: var(--surface-subtle);
--rt-disabled-fg: var(--state-disabled);
--rt-link: var(--primary);
--rt-mention-bg: color-mix(in oklab, var(--surface) var(--soft-bg-mix), var(--primary));
--rt-mention-fg: color-mix(in oklab, var(--primary) var(--soft-fg-mix), var(--text));
--rt-suggestion-bg: var(--surface-raised);
--rt-suggestion-active: var(--hover-overlay);
--rt-suggestion-shadow: var(--shadow-popout);
}
/* The hidden textarea owns validity: the wrapper re-owns the hook. */
.actual-rich-text:has(textarea[aria-invalid="true"]),
.needs-validation.was-validated .actual-rich-text:has(textarea:invalid) {
--form-invalid-border: var(--danger);
}
/* Rich composer: lift the editor and toolbar into the composer grid. */
.rt-composer rich-text,
.rt-composer .rt-shell {
display: contents;
}
.rt-composer .rt-editor {
grid-area: editor;
}
.rt-composer .rt-toolbar {
grid-area: toolbar;
}