Skip to main content

Theming

Stylesheets

Load two entry points once, in your app layout:

@import "@zuilib/tokens/styles.css"; /* or tokens.css without Tailwind */
@source "../node_modules/@zuilib/text-editor/dist"; /* Tailwind v4 hosts */
@import "@zuilib/text-editor/styles.css";
EntryRole
@zuilib/tokens/styles.css (or tokens.css)The design tokens the editor's chrome reads: --primary, --popover, --border, --muted, --radius, --shadow-medium, … Optional: without them the editor falls back to its own defaults
@zuilib/text-editor/styles.cssEditor chrome, tables, rails, canvas, code colours, layout. Required

Document content (headings, lists, inline code, quotes) is styled with Tailwind utility classes such as text-4xl, font-bold, ml-8, bg-muted. A Tailwind v4 host must @source the package so those classes are generated; a host without Tailwind can style the block classes below directly.

Dark mode

The editor follows the ZUI convention: a dark class on <html> (or a common ancestor). The chrome reads the tokens, which switch under .dark; the parts the editor owns itself (code colours, the drawing surface) have their own .dark rules.

document.documentElement.classList.toggle('dark', isDark)

The drawing area inverts Excalidraw-style under .dark so stored shape colours stay theme-agnostic; --zui-drawing-dark-filter: none opts out.

Block classes

Every top-level block carries a stable class next to its utility classes, for styling and for the layout bleed variable:

ClassBlock
.zui-paragraphParagraph
.zui-heading, .zui-heading-1.zui-heading-6Headings
.zui-list, .zui-list-ordered, .zui-list-unordered, .zui-checklistLists
.zui-quoteBlockquote
.zui-code, .zui-token-<type>Code block and its tokens
.zui-frontmatterFrontmatter block
.zui-table, .zui-table-cell, .zui-table-cell-header, .zui-table-railTable, cells, rails
.zui-drawing, .zui-drawing-canvas (+ .is-ink, .is-text-width)Drawing block and canvas

Structural wrappers: .zui-text-editor (root, height: 100%, scrolls), .zui-text-editor-main (the content pane; a CSS container), .zui-text-editor-content (the editable surface), .zui-text-editor-textarea (the edit-raw textarea).

Custom properties

Layout

PropertySet onMeaning
--zui-text-editor-measure.zui-text-editor or an ancestorMax width of the text column (the measure prop writes this)
--zui-text-editor-gutterderived on .zui-text-editor-mainPane padding: 2rem without a measure
--zui-text-editor-bleedderived on .zui-text-editor-mainHow far a full-width block extends past the column
--zui-text-editor-block-bleedany block classPer-block override of the bleed

Code colours

--zui-code-<type> on .zui-code, one per token type, with light and .dark defaults; see Code blocks.

Drawing canvas

The canvas chrome (header, property row, popover, selection handles) is styled entirely through --zui-drawing-* custom properties. They default to the ZUI tokens, so the canvas follows the host theme in both modes. Override any of them on .zui-drawing-canvas or an ancestor:

.my-app .zui-drawing-canvas {
--zui-drawing-accent: #0f766e; /* selection, active tool, handles */
--zui-drawing-surface: #fbfbf7; /* drawing area (pre dark-mode filter) */
--zui-drawing-grid: rgba(0, 0, 0, 0.12);
--zui-drawing-grid-size: 16px;
--zui-drawing-font: 'Inter', sans-serif;
}
VariableDefaultUsed for
--zui-drawing-accentvar(--primary)Selection frame and handles, active tool, focus ring, bind highlight
--zui-drawing-foregroundvar(--foreground)Header text and icons
--zui-drawing-muted-foregroundvar(--muted-foreground)Swatch labels, empty-state hint
--zui-drawing-bordervar(--border)Block border, header divider, popover border
--zui-drawing-chromevar(--background)Header and property row background
--zui-drawing-popover / -popover-foregroundvar(--popover) / var(--popover-foreground)"More shapes" menu
--zui-drawing-danger / --zui-drawing-successvar(--destructive) / var(--success)Delete button, "copied" state
--zui-drawing-radius / --zui-drawing-control-radiusvar(--radius-lg) / var(--radius-sm)Block corners / buttons and menus
--zui-drawing-shadowvar(--shadow-medium)Popover
--zui-drawing-fontsystem sansShape text and the inline editor (Recursive under .is-ink)
--zui-drawing-surface / --zui-drawing-grid / --zui-drawing-grid-sizewhite / 9% black / 20pxDrawing area and its dot grid, before the dark filter
--zui-drawing-dark-filterinvert(93%) hue-rotate(180deg)Applied to the drawing area under .dark; none opts out
--zui-drawing-hover / --zui-drawing-activecurrentColor at 8% / 12%Button hover and pressed backgrounds

The table rails and the floating table toolbar use the same tokens (--primary, --popover, --border, --shadow-medium, --destructive for the delete tint); their motion respects prefers-reduced-motion.

Example: a subtree retheme

The wrapper around this editor sets --primary: #0f766e (teal), the drawing accent and grid, and a squarer --zui-drawing-radius. Only the chrome that reads the custom properties directly changes; Tailwind utilities such as text-primary resolve at :root, so a subtree override does not reach them.

.example-teal {
--primary: #0f766e;
--zui-drawing-accent: #0f766e;
--zui-drawing-surface: #fbfbf7;
--zui-drawing-grid: rgba(15, 118, 110, 0.18);
--zui-drawing-grid-size: 16px;
--zui-drawing-radius: 0.25rem;
}

.dark .example-teal {
--primary: #2dd4bf;
--zui-drawing-accent: #2dd4bf;
}
Loading editor