Notion Color Picker
NotionColorPicker adds a bubble-menu trigger (“A” glyph with a slash) that drives named-token inline colors on the textStyle mark - the same 9-color Notion palette mapped to CSS custom properties. Hex/token mutual exclusion means setting a token clears any existing hex, and setting a hex clears any existing token (last action wins).
Not included in StarterKit. Add it separately for Notion-style color UX.
Quickstart
Section titled “Quickstart”import { Editor, StarterKit, TextStyle, TextColor, Highlight, NotionColorPicker, BubbleMenu,} from '@domternal/core';import '@domternal/theme';
const editor = new Editor({ element: document.getElementById('editor')!, extensions: [ StarterKit, TextStyle, TextColor, Highlight, // textStyle mark + color attribute providers NotionColorPicker, // adds bubble-menu trigger + colorToken/backgroundColorToken attrs BubbleMenu.configure({ element: bubbleEl }), ],});The “A” trigger appears in the bubble menu when text is selected. Framework wrappers render the picker panel; see the per-framework guide for examples.
Dependencies
Section titled “Dependencies”TextStylemark - required (extension declaresdependencies: ['textStyle'])TextColor+Highlight- recommended (they own thecolorToken/backgroundColorTokenattribute schemas ontextStyle)BubbleMenu- required to surface the “A” trigger (the toolbar item hastoolbar: falseand is bubble-menu-only)
Options
Section titled “Options”NotionColorPicker.configure({ palette: DEFAULT_NOTION_COLOR_PALETTE,})| Option | Type | Default | Description |
|---|---|---|---|
palette | readonly string[] | DEFAULT_NOTION_COLOR_PALETTE | Named tokens shown in the picker. Each must have matching --dm-block-text-<token> and --dm-block-bg-<token> CSS variables in the active theme. Tokens with no theme support render as transparent swatches. |
Default palette
Section titled “Default palette”export const DEFAULT_NOTION_COLOR_PALETTE: readonly string[] = Object.freeze([ 'gray', 'brown', 'orange', 'yellow', 'green', 'blue', 'purple', 'pink', 'red',]);These tokens align with BlockColor’s palette, so inline + block tints share the same color set.
Storage
Section titled “Storage”interface NotionColorPickerStorage { isOpen: boolean;}The UI flips editor.storage.notionColorPicker.isOpen as the picker opens and closes. Read this to gate other overlays or hide the bubble menu while the picker is showing.
Toolbar item
Section titled “Toolbar item”The extension registers one toolbar item:
{ name: 'notionColor', type: 'button', icon: 'textAUnderline', group: 'textStyle', priority: 250, toolbar: false, // bubble-menu only, hidden from main toolbar emitEvent: 'notionColorOpen',}Because toolbar: false, the item appears only in the bubble menu, not the main toolbar. Clicking it emits the notionColorOpen custom event on the editor, which framework wrappers listen for.
Events
Section titled “Events”| Event | Direction | Payload | Purpose |
|---|---|---|---|
notionColorOpen | editor.emit | { anchorElement?: HTMLElement } | Trigger emits when clicked; picker components listen |
Listen via editor.on('notionColorOpen', ...) or by wiring the relevant component (DomternalNotionColorPicker in each framework).
Hex/token mutual exclusion
Section titled “Hex/token mutual exclusion”The textStyle mark can carry EITHER a hex color (color: '#ff0000') OR a named token (colorToken: 'red'), not both. Setting one clears the other inside the affected range:
| Action | Effect |
|---|---|
Set colorToken: 'blue' | Clears color hex on the range |
Set color: '#0000ff' | Clears colorToken on the range |
Set backgroundColorToken: 'yellow' | Clears backgroundColor hex on the range |
Set backgroundColor: '#ffff00' | Clears backgroundColorToken on the range |
This is implemented inside TextColor and Highlight when they handle the new attrs.
Last-action-wins for block conflicts
Section titled “Last-action-wins for block conflicts”When BlockColor sets a block-level color and stripInlineColorConflicts() runs, it removes inline textStyle marks of the same kind (text or bg) inside the affected range so the block tint isn’t visually masked by older inline overrides.
The reverse also applies: applying an inline color token via NotionColorPicker on text that has a block-level color does NOT remove the block color; both layer (inline on top of block background, semantic priority).
Cross-framework component
Section titled “Cross-framework component”All four wrappers ship a DomternalNotionColorPicker component that listens for notionColorOpen and renders the panel:
import { DomternalNotionColorPicker } from '@domternal/vanilla';
const picker = new DomternalNotionColorPicker({ editor: dm.editor });// .destroy() when finished<domternal-notion-color-picker [editor]="editor()!" /><DomternalNotionColorPicker editor={editor} /><DomternalNotionColorPicker :editor="editor" />The components handle positioning, outside-click, keyboard navigation, and applying the token via editor.chain().focus().setColorToken('blue').run()-style commands.
CSS theme tokens
Section titled “CSS theme tokens”The theme stylesheet defines color values for each token:
| Variable | Purpose |
|---|---|
--dm-block-text-gray | Text color for the gray token |
--dm-block-text-brown | Text color for the brown token |
--dm-block-text-orange | etc. |
--dm-block-bg-gray | Background color for the gray token |
--dm-block-bg-brown | Background color for the brown token |
| (9 text + 9 bg) | All 9 palette names |
These are defined for both light and dark themes in @domternal/theme. Override them in your app’s CSS to customize colors.
Picker UI CSS classes
Section titled “Picker UI CSS classes”.dm-ncp-trigger- the “A” trigger button rendered byBubbleMenu(class-based identity, since the trigger button is destroyed and recreated on every transaction).dm-notion-color-picker- root panel container.dm-notion-color-swatch- individual swatch button
Exports
Section titled “Exports”import { NotionColorPicker, DEFAULT_NOTION_COLOR_PALETTE,} from '@domternal/core';import type { NotionColorPickerOptions, NotionColorPickerStorage,} from '@domternal/core';Source
Section titled “Source”@domternal/core - NotionColorPicker.ts