Skip to content

Theming

notectl ships with a complete theming system built on CSS custom properties. Two built-in themes (light & dark), automatic system-preference detection, and full custom-theme support are included out of the box.

import { createEditor, ThemePreset } from '@notectl/core';
const editor = await createEditor({
theme: ThemePreset.Dark,
});

Available presets:

PresetValueDescription
ThemePreset.Light'light'Default light theme
ThemePreset.Dark'dark'Dark theme (Catppuccin-inspired)
ThemePreset.System'system'Follows OS prefers-color-scheme
<notectl-editor theme="dark"></notectl-editor>
const editor = await createEditor({
theme: ThemePreset.System,
});

The editor listens to prefers-color-scheme changes and switches automatically.

// Switch to dark
editor.setTheme(ThemePreset.Dark);
// Read current theme
const current = editor.getTheme(); // 'light' | 'dark' | 'system' | Theme object

Toggle example:

const toggle = document.getElementById('theme-toggle');
toggle.addEventListener('click', () => {
const next = editor.getTheme() === ThemePreset.Dark
? ThemePreset.Light
: ThemePreset.Dark;
editor.setTheme(next);
});

Create a custom theme by extending a built-in base theme with partial overrides:

import { createTheme, LIGHT_THEME, createEditor } from '@notectl/core';
import type { Theme } from '@notectl/core';
const corporate: Theme = createTheme(LIGHT_THEME, {
name: 'corporate',
primitives: {
primary: '#6B21A8',
primaryForeground: '#6B21A8',
primaryMuted: 'rgba(107, 33, 168, 0.15)',
borderFocus: '#6B21A8',
focusRing: 'rgba(107, 33, 168, 0.2)',
},
});
const editor = await createEditor({ theme: corporate });

Only specify the values you want to change — everything else falls back to the base theme.

Component-level tokens (toolbar, code block, tooltip) can be overridden independently:

const myTheme: Theme = createTheme(DARK_THEME, {
name: 'custom-dark',
codeBlock: {
background: '#0d1117',
foreground: '#c9d1d9',
},
tooltip: {
background: '#21262d',
foreground: '#f0f6fc',
},
});

Themes are plain objects — export them from a package:

my-theme-package/index.ts
import { createTheme, DARK_THEME } from '@notectl/core';
import type { Theme } from '@notectl/core';
export const OCEAN_THEME: Theme = createTheme(DARK_THEME, {
name: 'ocean',
primitives: {
primary: '#06b6d4',
background: '#0c1222',
surfaceRaised: '#1a2332',
surfaceOverlay: '#1a2332',
},
});

The theme engine sets all properties on :host inside the Shadow DOM. Every color in the editor references these variables.

These are the core tokens that all components derive their colors from.

CSS PropertyTokenDescription
--notectl-bgbackgroundEditor and input background
--notectl-fgforegroundMain text color
--notectl-fg-mutedmutedForegroundSecondary text (placeholders, labels, arrows)
--notectl-borderborderDefault borders (editor, toolbar, inputs, separators)
--notectl-border-focusborderFocusFocus state border
--notectl-primaryprimaryAccent color (selection outlines, insert lines, active states)
--notectl-primary-fgprimaryForegroundText on primary-tinted backgrounds
--notectl-primary-mutedprimaryMutedSubtle primary background (active toolbar button, selected cells)
--notectl-surface-raisedsurfaceRaisedElevated surfaces (toolbar background)
--notectl-surface-overlaysurfaceOverlayOverlay surfaces (popups, context menus, dropdowns)
--notectl-hover-bghoverBackgroundHover state background
--notectl-active-bgactiveBackgroundActive/pressed state background
--notectl-dangerdangerDelete and error color
--notectl-danger-muteddangerMutedSubtle danger background
--notectl-successsuccessChecked/success color (checklist checkmarks)
--notectl-shadowshadowBox-shadow color
--notectl-focus-ringfocusRingFocus ring shadow (typically semi-transparent)
CSS PropertyTokenFallback
--notectl-toolbar-bgtoolbar.backgroundvar(--notectl-surface-raised)
--notectl-toolbar-bordertoolbar.borderColorvar(--notectl-border)
CSS PropertyTokenFallback
--notectl-code-block-bgcodeBlock.backgroundvar(--notectl-surface-raised)
--notectl-code-block-colorcodeBlock.foregroundvar(--notectl-fg)
--notectl-code-block-header-bgcodeBlock.headerBackgroundvar(--notectl-surface-raised)
--notectl-code-block-header-colorcodeBlock.headerForegroundvar(--notectl-fg-muted)
--notectl-code-block-header-bordercodeBlock.headerBordervar(--notectl-border)
CSS PropertyTokenFallback
--notectl-tooltip-bgtooltip.backgroundvar(--notectl-fg)
--notectl-tooltip-fgtooltip.foregroundvar(--notectl-bg)
CSS PropertyDescription
--notectl-content-min-heightMinimum height of the content area (default: 400px)

When a syntax highlighter is configured on the CodeBlockPlugin, token classes are applied to code content. Style them to match your theme:

/* Light theme tokens */
notectl-editor .notectl-token--keyword { color: #d73a49; }
notectl-editor .notectl-token--string { color: #032f62; }
notectl-editor .notectl-token--number { color: #005cc5; }
notectl-editor .notectl-token--comment { color: #6a737d; font-style: italic; }
/* Dark theme tokens */
@media (prefers-color-scheme: dark) {
notectl-editor .notectl-token--keyword { color: #c678dd; }
notectl-editor .notectl-token--string { color: #98c379; }
notectl-editor .notectl-token--number { color: #d19a66; }
notectl-editor .notectl-token--comment { color: #5c6370; font-style: italic; }
}
interface ThemePrimitives {
readonly background: string;
readonly foreground: string;
readonly mutedForeground: string;
readonly border: string;
readonly borderFocus: string;
readonly primary: string;
readonly primaryForeground: string;
readonly primaryMuted: string;
readonly surfaceRaised: string;
readonly surfaceOverlay: string;
readonly hoverBackground: string;
readonly activeBackground: string;
readonly danger: string;
readonly dangerMuted: string;
readonly success: string;
readonly shadow: string;
readonly focusRing: string;
}
interface Theme {
readonly name: string;
readonly primitives: ThemePrimitives;
readonly toolbar?: Partial<ThemeToolbar>;
readonly codeBlock?: Partial<ThemeCodeBlock>;
readonly tooltip?: Partial<ThemeTooltip>;
}

All theme-related exports from @notectl/core:

ExportKindDescription
ThemePresetEnum objectLight, Dark, System
LIGHT_THEMEConstantBuilt-in light theme
DARK_THEMEConstantBuilt-in dark theme
createTheme(base, overrides)FunctionCreate custom theme from a base
resolveTheme(preset | theme)FunctionResolve a preset to a full Theme
generateThemeCSS(theme)FunctionGenerate CSS string from a Theme
createThemeStyleSheet(theme)FunctionCreate a CSSStyleSheet from a Theme
ThemeTypeFull theme definition
PartialThemeTypePartial overrides for createTheme()
ThemePrimitivesTypePrimitive color palette

Plugins that create UI elements (popups, dialogs, overlays) should reference theme variables instead of hardcoding colors:

// In your plugin's DOM creation
const popup = document.createElement('div');
popup.style.cssText = `
background: var(--notectl-surface-overlay);
border: 1px solid var(--notectl-border);
color: var(--notectl-fg);
box-shadow: 0 4px 12px var(--notectl-shadow);
`;

This ensures your plugin adapts automatically when the user switches themes.