A stylesheet editor contains border-color: #176b52. A swatch beside the six-digit value lets the author inspect that color while retaining the exact source for copying and saving. This is a source preview: it does not calculate the final color after CSS variables, inheritance, or theme rules.
Describe a swatch without editing source
This JavaScript belongs in an existing bundled CodeMirror 6 application with compatible packages:
import {
Decoration, EditorView, MatchDecorator,
ViewPlugin, WidgetType
} from '@codemirror/view';
class ColorSwatch extends WidgetType {
constructor(color) { super(); this.color = color; }
eq(other) { return this.color === other.color; }
toDOM() {
const node = document.createElement('span');
node.className = 'cm-source-swatch';
node.style.backgroundColor = this.color;
node.setAttribute('aria-hidden', 'true');
return node;
}
}
const swatchTheme = EditorView.baseTheme({
'.cm-source-swatch': {
display: 'inline-block',
width: '0.8em',
height: '0.8em',
marginLeft: '0.25em',
border: '1px solid #777777',
verticalAlign: 'baseline'
}
});
eq() allows CodeMirror to reuse the DOM for an equivalent widget. The swatch has no button behavior and is hidden from assistive technology because the adjacent source already supplies its value. Keep that text present and readable; the color square is supplementary.
Place a zero-length widget after each matching literal
Use decorate to place the widget at the end of a match rather than wrapping the matched text in an element:
const matcher = new MatchDecorator({
regexp: /#[0-9a-fA-F]{6}\b/g,
decorate(add, from, to, match) {
add(to, to, Decoration.widget({
widget: new ColorSwatch(match[0]),
side: 1
}));
}
});
const swatches = ViewPlugin.fromClass(class {
constructor(view) {
this.decorations = matcher.createDeco(view);
}
update(update) {
this.decorations = matcher.updateDeco(
update, this.decorations
);
}
}, {
decorations: plugin => plugin.decorations
});
export const colorSwatchExtension = [swatches, swatchTheme];
Add colorSwatchExtension to the editor's initial extensions. The matcher maintains decorations through view updates, including changes to visible content. The MatchDecorator reference describes its viewport-based matching and the decorate callback.
The widget occupies one position rather than a text range. side: 1 places it after the position it is attached to. The literal remains in the document, so this is neither a replacement decoration nor an atomic replacement that the cursor must skip.
Decide how much the matcher should understand
For this original fixture:
.ticket { color: #176b52; background: #fff4d6; }
/* Also remember #abcdef for the next draft. */
The lexical matcher finds three six-digit values, including the one in the comment. It deliberately ignores #fff, eight-digit colors, rgb(), and CSS variables. It can also match six hexadecimal characters preceded by # in text that is not a CSS color. A syntax-aware product should inspect the language's syntax tree and restrict decorations to supported color nodes rather than describing this regex as a CSS parser.
For very long lines or folded content, MatchDecorator does not promise a full-document inventory. Do not use the number of visible swatches as an audit count of all colors in the file. If the application needs that inventory, calculate it separately from the viewport presentation.
Check editing and saving in the integrated application
Type the fixture, edit #176b52 to #0044aa, then scroll away and return. Confirm that the swatch follows the visible literal and that deleting the literal removes its cue. Copy a source line and read view.state.doc.toString() before saving: both should contain the source characters without a generated swatch element.
Do not append spans directly into CodeMirror's managed content DOM. The editor may rebuild that DOM on an update. Decorations give it the presentation information it needs to maintain the view.
For changing the editor's own appearance, use theme configuration. A swatch beside a literal answers a different question: what color the written value names, while preserving the stylesheet the author will deliver.