Skip to content

Theming

The @domternal/theme package provides ready-made light and dark themes for the editor, toolbar, bubble menu, and all UI components. Every visual property is a CSS custom property, so you can customize anything by overriding a single variable.

Terminal window
pnpm add @domternal/theme

Import the theme in your application entry point:

// JavaScript/TypeScript bundler (Vite, Webpack, esbuild)
import '@domternal/theme';
// SCSS
@use '@domternal/theme';
<!-- HTML link tag -->
<link rel="stylesheet" href="node_modules/@domternal/theme/dist/domternal-theme.css" />

The package exports multiple entry points:

ImportPathDescription
@domternal/themedist/domternal-theme.cssDefault: compiled CSS
@domternal/theme/cssdist/domternal-theme.cssExplicit CSS import
@domternal/theme/scsssrc/index.scssSCSS source (for Sass pipelines)

Light mode is the default. Dark mode is activated by adding a CSS class to the editor or any ancestor element.

ClassBehavior
(none)Light theme (default)
dm-theme-darkDark theme, always
dm-theme-lightLight theme, always (use to force light inside a dark context)
dm-theme-autoFollows the user’s system preference via prefers-color-scheme
<!-- Always dark -->
<div class="dm-theme-dark">
<div class="dm-toolbar">...</div>
<div class="dm-editor">...</div>
</div>
<!-- Follows system preference -->
<div class="dm-theme-auto">
<div class="dm-toolbar">...</div>
<div class="dm-editor">...</div>
</div>
<!-- Force light inside a dark page -->
<div class="dm-theme-light">
<div class="dm-toolbar">...</div>
<div class="dm-editor">...</div>
</div>
function toggleTheme() {
document.body.classList.toggle('dm-theme-dark');
}

Or use dm-theme-auto and let the OS handle it.

Override any CSS custom property on .dm-editor, .dm-toolbar, or any parent element:

/* Custom accent color */
.my-editor {
--dm-accent: #e11d48;
--dm-accent-hover: #be123c;
--dm-accent-surface: rgba(225, 29, 72, 0.1);
}
/* Custom editor appearance */
.my-editor .dm-editor {
--dm-editor-bg: #fefce8;
--dm-editor-border-radius: 0;
--dm-editor-shadow: none;
}
/* Custom toolbar */
.my-editor .dm-toolbar {
--dm-toolbar-bg: #fef9c3;
--dm-button-size: 2.25rem;
}

Since CSS custom properties cascade, you can set them on any ancestor and they flow down to all editor components.

All properties are defined on .dm-editor unless noted otherwise. Light values are the defaults. Dark values are applied when dm-theme-dark or dm-theme-auto (in dark mode) is active.

These are the core design tokens that other properties reference via var().

PropertyLightDarkDescription
--dm-bg#ffffff#1e1e1eBase background
--dm-text#1a1a1a#e0e0e0Base text color
--dm-muted#999999#777777Secondary/muted text
--dm-surface#f8f9fa#2a2a2aElevated surface background
--dm-border-color#e5e7eb#3a3a3aBorder color
--dm-hoverrgba(0,0,0,0.04)rgba(255,255,255,0.08)Hover state background
--dm-activergba(0,0,0,0.1)rgba(255,255,255,0.15)Active state background
--dm-accent#2563eb#60a5faPrimary accent (blue)
--dm-accent-hover#1d4ed8#93c5fdAccent on hover
--dm-accent-surfacergba(37,99,235,0.1)rgba(96,165,250,0.15)Accent tint background
--dm-focus-colorrgba(66,133,244,0.3)rgba(96,165,250,0.3)Focus ring color
--dm-selectionrgba(66,133,244,0.2)rgba(96,165,250,0.25)Text selection color
--dm-code-surface#f0f0f0#2d2d2dCode background surface
--dm-code-colorinheritinheritCode text color
PropertyDefaultDescription
--dm-editor-bgvar(--dm-bg)Editor background
--dm-editor-textvar(--dm-text)Editor text color
--dm-editor-font-familySystem font stackFont family
--dm-editor-font-size1remBase font size
--dm-editor-line-height1.6Line height
--dm-editor-padding1remContent padding
--dm-editor-border1px solid var(--dm-border-color)Border
--dm-editor-border-radius0.75remCorner radius
--dm-editor-focus-ringnoneFocus ring (override for custom, e.g., 0 0 0 2px var(--dm-focus-color))
--dm-editor-shadow0 1px 3px rgba(0,0,0,0.04), 0 1px 2px rgba(0,0,0,0.02)Box shadow
PropertyDefaultDescription
--dm-placeholder-colorvar(--dm-muted)Placeholder text color
PropertyDefaultDescription
--dm-link-colorvar(--dm-accent)Link color
--dm-link-hover-colorvar(--dm-accent-hover)Link hover color
PropertyDefaultDescription
--dm-code-bgvar(--dm-code-surface)Inline code background
--dm-code-textvar(--dm-code-color)Inline code text color
--dm-code-font"SF Mono", "Fira Code", Consolas, "Liberation Mono", Menlo, monospaceMonospace font
--dm-code-border-radius0.25remInline code corner radius
PropertyDefaultDescription
--dm-code-block-bgvar(--dm-code-surface)Code block background
--dm-code-block-textvar(--dm-text)Code block text color

Used by Code Block Lowlight for highlight.js token colors.

PropertyLightDarkDescription
--dm-syntax-keyword#c72031#ff7b72Keywords, types
--dm-syntax-entity#6f42c1#d2a8ffFunctions, class names
--dm-syntax-constant#005cc5#79c0ffConstants, numbers
--dm-syntax-string#032f62#a5d6ffStrings, regex
--dm-syntax-variable#d35400#ffa657Variables, built-ins
--dm-syntax-comment#57606a#8b949eComments
--dm-syntax-tag#22863a#7ee787HTML/XML tags
--dm-syntax-addition#22863a#aff5b4Diff additions
--dm-syntax-addition-bg#f0fff4#033a16Diff addition background
--dm-syntax-deletion#b31d28#ffdcd7Diff deletions
--dm-syntax-deletion-bg#ffeef0#67060cDiff deletion background
PropertyLightDarkDescription
--dm-blockquote-border3px solid #6a6a6a3px solid #555555Left border
--dm-blockquote-color#6a6a6a#a0a0a0Text color
PropertyDefaultDescription
--dm-hr-colorvar(--dm-border-color)Rule color
PropertyDefaultDescription
--dm-table-border1px solid var(--dm-border-color)Cell border
--dm-table-header-bgvar(--dm-surface)Header cell background
--dm-table-selected-bgrgba(66, 133, 244, 0.15)Selected cell background
PropertyLightDarkDescription
--dm-highlight-bg#fff3cdrgba(255, 243, 205, 0.2)Default highlight background (used by ==text== input rule)
PropertyDefaultDescription
--dm-mention-bgvar(--dm-accent-surface)Mention background
--dm-mention-colorvar(--dm-accent)Mention text color
--dm-mention-border-radius0.25remMention corner radius
PropertyDefaultDescription
--dm-details-border1px solid var(--dm-border-color)Details border
--dm-details-bgvar(--dm-surface)Summary background
--dm-details-summary-font-weight600Summary text weight

Defined on .dm-toolbar:

PropertyDefaultDescription
--dm-toolbar-bgvar(--dm-bg, #ffffff)Toolbar background
--dm-toolbar-bordernoneToolbar border
--dm-toolbar-padding0.375rem 0.5remToolbar padding
--dm-toolbar-gap0.125remGap between items
--dm-toolbar-border-radius0.75rem 0.75rem 0 0Toolbar corner radius

Defined on .dm-toolbar:

PropertyDefaultDescription
--dm-button-size2remButton width and height
--dm-button-border-radius0.375remButton corner radius
--dm-button-colorvar(--dm-text, #374151)Button icon/text color
--dm-button-hover-bgvar(--dm-hover)Button hover background
--dm-button-active-bgvar(--dm-accent-surface)Active button background
--dm-button-active-colorvar(--dm-accent)Active button icon color
--dm-button-disabled-opacity0.35Disabled button opacity

Defined on .dm-toolbar:

PropertyDefaultDescription
--dm-separator-colorvar(--dm-border-color)Separator color
--dm-separator-margin0.375remSeparator vertical margin

The theme styles these CSS classes. Use them when building custom UI or vanilla JS editors.

ClassDescription
.dm-editorMain editor wrapper. Defines all design tokens. Must have position: relative (set by theme).
.dm-editor .ProseMirrorThe contenteditable area. Inherits editor tokens.
.dm-toolbarToolbar container. Flex layout with grouped buttons.
.dm-toolbar-groupGroup of related toolbar buttons.
.dm-toolbar-buttonIndividual toolbar button.
.dm-toolbar-separatorVertical separator between button groups.
.dm-toolbar-dropdown-panelDropdown panel (headings, font family, colors).
.dm-toolbar-dropdown-itemItem inside a dropdown panel.
ClassDescription
.dm-bubble-menuInline formatting toolbar (appears on text selection). Compact button sizing.
.dm-floating-menuBlock-level menu (appears on empty lines).
.dm-link-popoverURL input popover for links.
.dm-image-popoverURL input popover for images.
ClassDescription
.mentionInline mention node.
.dm-mention-suggestionMention autocomplete dropdown.
.dm-emoji-pickerEmoji picker panel.
.dm-emoji-suggestionEmoji autocomplete dropdown.
.dm-color-paletteColor grid for text color and highlight pickers.
.dm-image-resizableResizable image wrapper with handles.
.invisible-charInvisible character indicator (paragraph marks, spaces).
ClassDescription
.dm-table-containerWrapper for table handles and content.
.dm-table-col-handleColumn selection handle.
.dm-table-row-handleRow selection handle.
.dm-table-cell-handleCell corner handle (opens cell toolbar).
.dm-table-cell-toolbarFloating toolbar above selected cells.
.dm-table-controls-dropdownDropdown for table operations.
ClassDescription
.ProseMirror-selectednodeOutline on selected atom nodes (images, horizontal rules). Uses --dm-accent.
.ProseMirror-gapcursorAnimated blinking cursor in gap positions.
.selectedCellBackground overlay on selected table cells. Uses --dm-table-selected-bg.
.is-emptyAdded to empty nodes for placeholder display.
.is-editor-emptyAdded to the editor when the document is empty.
.has-focusAdded by the Focus extension to the focused node.
.is-openAdded to open details/accordion elements.

You don’t need @domternal/theme to use Domternal. The editor works without any styles. You can build everything from scratch.

At minimum, ProseMirror requires these styles to function correctly:

/* ProseMirror content area */
.dm-editor .ProseMirror {
position: relative;
outline: none;
word-wrap: break-word;
white-space: pre-wrap;
white-space: break-spaces;
}
/* Gapcursor (if using Gapcursor extension) */
.dm-editor .ProseMirror-gapcursor {
display: none;
pointer-events: none;
position: absolute;
}
.dm-editor .ProseMirror-gapcursor::after {
content: '';
display: block;
position: absolute;
top: -2px;
width: 20px;
border-top: 1px solid currentColor;
animation: ProseMirror-cursor-blink 1.1s steps(2, start) infinite;
}
@keyframes ProseMirror-cursor-blink {
to { visibility: hidden; }
}
.dm-editor .ProseMirror-focused .ProseMirror-gapcursor {
display: block;
}
/* Hide selection on drag */
.ProseMirror-hideselection *::selection {
background: transparent;
}
/* Table column resize handle (if using tables) */
.dm-editor .column-resize-handle {
position: absolute;
right: -1.5px;
top: 0;
bottom: 0;
width: 2px;
background-color: #2563eb;
pointer-events: none;
}

The fastest way to customize is to import the default theme and override specific variables:

@import '@domternal/theme';
.my-app .dm-editor {
--dm-accent: #e11d48;
--dm-editor-border-radius: 0;
--dm-editor-font-family: 'Inter', sans-serif;
}
.my-app .dm-toolbar {
--dm-toolbar-bg: #f1f5f9;
--dm-toolbar-border-radius: 0;
}

Import the SCSS source and use the theme’s structure:

// Import just the variables and base styles you need
@use '@domternal/theme/scss';

If building a custom dark theme, override the same semantic tokens:

.my-dark-theme .dm-editor {
--dm-bg: #0d1117;
--dm-text: #c9d1d9;
--dm-surface: #161b22;
--dm-border-color: #30363d;
--dm-accent: #58a6ff;
--dm-accent-hover: #79c0ff;
--dm-accent-surface: rgba(88, 166, 255, 0.15);
--dm-hover: rgba(255, 255, 255, 0.06);
--dm-active: rgba(255, 255, 255, 0.12);
--dm-code-surface: #161b22;
}
/* Apply the same tokens to floating UI that escapes the editor */
.my-dark-theme .dm-toolbar,
.my-dark-theme .dm-bubble-menu,
.my-dark-theme .dm-emoji-picker,
.my-dark-theme .dm-mention-suggestion,
.my-dark-theme .dm-table-controls-dropdown {
--dm-bg: #0d1117;
--dm-text: #c9d1d9;
--dm-surface: #161b22;
--dm-border-color: #30363d;
--dm-accent: #58a6ff;
--dm-hover: rgba(255, 255, 255, 0.06);
}

The theme is composed of SCSS partials:

FileDescription
_variables.scssAll CSS custom property definitions
_base.scss.dm-editor wrapper styles, fade-in animation
_content.scssContent typography (headings, lists, links, code, images)
_prosemirror.scssProseMirror required styles (gapcursor, selected nodes, tables)
_toolbar.scssToolbar, buttons, dropdowns, separators
_bubble-menu.scssCompact bubble menu
_floating-menu.scssFloating menu
_link-popover.scssLink URL input popover
_color-palette.scssColor grid for text color and highlight pickers
_mention.scssMention nodes and suggestion dropdown
_image.scssResizable images, handles, image popover
_details.scssAccordion/details with CSS grid toggle
_placeholder.scssPlaceholder text
_task-list.scssTask list with checkboxes
_invisible-chars.scssInvisible character indicators
_syntax.scssSyntax highlighting for code blocks
_emoji-picker.scssEmoji picker and suggestions
_table-controls.scssTable handles, cell toolbar, dropdowns
themes/_light.scssLight theme class and mixin
themes/_dark.scssDark theme class, auto mode, and mixin