Theming
The editor is themed with CSS custom properties. Set them in your own
stylesheet on .stesura (the class on the editor root) or on any ancestor of
the editor.
/* your-theme.css, loaded after @stesura/core/styles */
.stesura {
--stesura-canvas-solid: #fffef5;
--stesura-page-border-color: rgba(0, 0, 0, 0.2);
--editor-accent-color: oklch(0.65 0.19 55);
}import "./your-theme.css";Every variable is optional. Without them the editor uses its built-in light and dark defaults.
The UI tokens (--primary, --border, --radius, …) and the icon variables
are declared on .stesura itself, so set those on .stesura: a value on an
ancestor or on :root doesn’t get past it. To theme one editor, scope the
selector (.my-wrapper .stesura). Popups (menus, dialogs, tooltips) render
outside the editor but carry .stesura too, so they follow the same
overrides. To restyle a component, use .stesura .your-selector or something
more specific.
Scoping
@stesura/ui/styles.css only styles elements inside a .stesura element. It
follows that:
- Our components outside the editor need
.stesura. If you use@stesura/ui/components/*in your own UI, wrap them in an element with thestesuraclass, or they render unstyled. Their popups carry the class themselves. - Anything you render outside the editor root needs it too. A node view,
toolbar extension or menu that portals into
<body>(createPortal, a Base UIPortal) must putstesuraon the portaled root. - Your rules still win from two classes up. Inside the editor, the
editor’s rules beat your element and single-class rules. They lose to rules
with two or more classes (
.prose .lead p) and to!important, including Tailwind’simportant: true. - Your content inside the editor gets our utilities. In a toolbar slot or a
custom node view, a Tailwind class you share with us (
p-2,rounded-md) resolves to the editor’s value. Pin your own with.stesura .your-selector. - Popups take dark mode from
<html>or<body>..darkthere darkens the editor and its popups..darkon the editor’s wrapper alone darkens the editor but not its popups, which render outside it.
Canvas background
The canvas is the surface the pages sit on. It spans the toolbar’s tab strip
and the margins around each page. It is either a solid color or a
two-stop linear gradient. Light and dark mode are configured
independently: every variable has a -dark counterpart, so theming one mode
never changes the other.
Canvas colors must be opaque. The gaps between pages must be opaque (see
What the gradient covers), so the editor rejects
translucent values such as rgba(…, 0.5) or #fff8: it falls back to the next
entry in the resolution order and warns in development.
Solid
.stesura {
--stesura-canvas-solid: #fffef5;
--stesura-canvas-solid-dark: #0b1220;
}Gradient
Opt in by setting both stops for that mode. direction is optional and
defaults to to bottom right.
.stesura {
--stesura-canvas-gradient-from: #ffffff;
--stesura-canvas-gradient-to: #ffd6d6;
--stesura-canvas-gradient-direction: to bottom right;
--stesura-canvas-gradient-from-dark: #1a1416;
--stesura-canvas-gradient-to-dark: #4a1f24;
--stesura-canvas-gradient-direction-dark: to bottom right;
}direction must be one of the CSS to … keywords: to top, to bottom,
to left, to right, to top left, to top right, to bottom left,
to bottom right. Angles are not supported. Any other value disables the
gradient for that mode.
Colors can use any CSS color syntax (hex, rgb(), named colors, oklch(),
color-mix()). The editor normalizes them to sRGB and interpolates in sRGB.
Resolution order
For each mode, the editor uses the first of these that applies:
- the gradient, if both stops are valid opaque colors and
directionis valid or unset - that mode’s solid color
- the built-in default
So a gradient in light mode with a solid color in dark mode (or the reverse) works.
What the gradient covers
The gradient is screen-space: it is anchored to the editor’s box, not to the document, so it does not scroll with the pages. It is painted behind the toolbar’s tab strip and the margins around the pages. The raised toolbar ribbon keeps its own opaque background.
The gaps between pages look like windows onto the gradient, but they are
opaque: content flows behind mid-table page breaks and would otherwise show
through. Each gap paints the slice of the gradient at its on-screen position.
That is why the gradient is declared as from/to/direction rather than a free
CSS background-image: the slice has to be computable.
The note and header/footer sub-editors paint the gradient across their own panel instead of continuing the canvas behind them.
Changing the theme at runtime
The editor reads the canvas variables on mount and again whenever class
changes on <html> or on the editor root, or the OS color scheme changes. To
apply other runtime changes (a swapped stylesheet), toggle one of those classes
or remount the editor.
Pages
.stesura {
--stesura-page-border-color: rgba(0, 0, 0, 0.2);
--stesura-page-shadow-color: rgba(0, 0, 0, 0.08);
}Both default to transparent: pages have no border or shadow until a theme
sets them. The border is a 1px solid line; the shadow is a 6px blur.
The page background is not themeable. It comes from the document’s docBgColor
attribute (white by default), so it round-trips through DOCX and is edited in
the UI.
Side panels and toolbar
By default the side panels float as rounded cards with a 4px gap, matching the
toolbar ribbon. Set the margin and radius to 0 for panels that fill their
column edge to edge:
.stesura {
--stesura-panel-margin: 0;
--stesura-panel-radius: 0;
--stesura-panel-shadow: none;
--stesura-panel-divider-color: var(--border);
}| Variable | Default | |
|---|---|---|
--stesura-panel-margin | 4px | Gap above, below and on the outer side. The side facing the editor never gets one. |
--stesura-panel-radius | var(--radius-md) | Corner radius. |
--stesura-panel-border | none | A border shorthand. |
--stesura-panel-shadow | the toolbar’s shadow | A box-shadow. |
--stesura-panel-background | var(--card) | The panel’s fill, painted while its content loads. |
--stesura-panel-divider-color | transparent | The resize handle’s line. |
--stesura-panel-transition-duration | 200ms | Open/close slide. 0s turns it off, as does prefers-reduced-motion. |
The toolbar ribbon has its own set, defaulting to its current look:
| Variable | Default | |
|---|---|---|
--stesura-toolbar-margin | 0px (0.25rem sideways on wide screens) | Gap above and on both sides. |
--stesura-toolbar-radius | var(--radius-md) | Corner radius. |
--stesura-toolbar-border | none | A border shorthand. |
--stesura-toolbar-shadow | a small shadow in var(--secondary) | A box-shadow. |
On compact viewports panels open as bottom drawers and ignore the panel variables.
The header/footer and footnote editing panels slide over the canvas with their
own fill, --stesura-hf-panel-surface (#f3f4f6, #1f2937 in dark mode):
.stesura {
--stesura-hf-panel-surface: #eef2f7;
}Accent and icons
.stesura {
--editor-accent-color: oklch(0.65 0.19 55);
}Set the accent on .stesura, in a stylesheet loaded after
@stesura/core/styles. The accent-derived icon variables
(--editor-icon-outline-secondary, --editor-icon-accent-*) are re-declared on
.stesura, so override those there too.
Each path in an icon names a semantic layer. --editor-icon-outline-* layers
paint strokes, the rest paint fills:
| Variable | Default | Purpose |
|---|---|---|
--editor-icon-outline-primary | currentColor | Glyph strokes. Icons follow button text color, including hover and disabled states. |
--editor-icon-outline-secondary | accent | Stroked accents: indent arrows, line-height marks. |
--editor-icon-accent-primary/-secondary/-tertiary | accent | Filled emphasis marks such as list numerals and bullets. |
--editor-icon-fill-primary | currentColor | Solid glyph surfaces. |
--editor-icon-fill-secondary/-tertiary | transparent | Optional interior surfaces. |
--editor-icon-constructive / --editor-icon-destructive | green / red | Table actions: insert vs delete. |
The PDF and Word brand marks use --editor-icon-brand-pdf and
--editor-icon-brand-word. Leave them alone: they are logo colors, and the
accent deliberately doesn’t touch them.
Icon set
The iconSet prop picks the icon geometry. Microsoft Fluent is the default;
the classic set (the original Stesura icons, completed with lucide) is its own
subpath, so only apps that import it ship it. Both use the layer variables above.
import { classicIcons } from "@stesura/icons/classic";
<StesuraEditor iconSet={classicIcons} /* … */ />Comments
.stesura {
--stesura-comment-bg: oklch(71.4% 0.203 305.504 / 20%);
--stesura-comment-boundary-color: oklch(71.4% 0.203 305.504 / 40%);
--stesura-comment-pending-comment-bg: oklch(71.4% 0.203 305.504 / 80%);
--stesura-comment-selected-comment-bg: oklch(71.4% 0.203 305.504);
}The values shown are the defaults: commented text, the range boundary markers, a comment being drafted, and the selected thread.
Callouts
Each built-in callout kind (note, tip, warning, danger, quote) reads a
fill, a left-rule and a title colour:
.stesura {
--stesura-callout-note-bg: #eff6ff;
--stesura-callout-note-border: #2563eb;
--stesura-callout-note-title: #1d4ed8;
}The values shown are the note defaults; the others follow the same pattern
(--stesura-callout-tip-bg, …). They print as set, and have no dark variant:
the page stays a sheet of paper. Custom kinds are styled by data-kind, see
Callouts → Custom kinds.
Dark mode
The editor is in dark mode when:
- an ancestor of the editor, or the editor root, has the
.darkclass (thenext-themesconvention withattribute="class"), or - no
.darkclass is present,<html>doesn’t have the.lightclass, and the OS prefers dark.
data-theme is not read. With a class-based theme switcher, use
attribute="class".
In dark mode the editor reads the -dark canvas variables itself, so you don’t
need a .dark { … } block for them. Other variables (pages, icons, comments)
have a single value: override them inside .dark if dark mode needs different
ones.
PDF export
@stesura/pdf-export-client forces a flat white canvas and hides the page
border and shadow during export, whatever the theme. Browser printing
(Ctrl+P) is not affected.
Variables the editor writes
The editor writes these on the editor root. Read them in your own UI; don’t set them in a theme, they are overwritten.
| Variable | Value |
|---|---|
--stesura-canvas-surface | The canvas’s flat color: the solid color, or the gradient’s from stop. Use it where a plain background should match the canvas. |
--stesura-canvas-gradient | The active gradient as a CSS linear-gradient(…), or none for a solid canvas. |