Vanilla Breeze

data-highlights

Medium-style text highlighting and annotation with color swatches, private notes, and localStorage persistence.

Overview

The data-highlights attribute enables Medium-style text selection highlighting on any element. Users can select text, choose a highlight color, add private notes, and all annotations persist in localStorage.

Uses the CSS Custom Highlight API for non-destructive rendering, with a <mark> wrapping fallback for older browsers.

Attributes

AttributeTypeDefaultDescription
data-highlights string (optional) pathname Enables highlighting. Optional value becomes the localStorage key suffix.
data-highlights-colors comma-separated yellow,green,blue,pink Available highlight colors. Maps to --highlight-{color} CSS tokens.
data-highlights-readonly boolean Renders stored highlights but disables creating new ones.

Basic Usage

Add data-highlights to any content container. Users can select text to see a color-swatch toolbar.

Custom Colors

Override the default palette with data-highlights-colors.

Define the actual color values with CSS custom properties:

Custom Storage Key

By default, highlights are stored per page pathname. Provide a value to use a custom key.

Read-Only Mode

Render previously stored highlights without allowing new ones.

Private Notes

Click an existing highlight to open the toolbar, then click “Add Note” to attach a private note. Notes appear as Medium-style margin annotations on wide screens and as a fixed bottom panel on narrow screens.

Notes are stored alongside highlights in localStorage. The note panel supports Ctrl+Enter to save and Esc to cancel.

How It Works

  1. On page load, highlights-init.js finds all [data-highlights] elements and creates a HighlightController for each.
  2. When text is selected (via pointerup), a color-swatch toolbar appears as a popover above the selection.
  3. Clicking a color creates a highlight stored as text offsets in localStorage, with a content hash for drift detection.
  4. The CSS Custom Highlight API renders highlights non-destructively (no DOM mutation). On unsupported browsers, <mark> elements are inserted as a fallback.
  5. A MutationObserver watches for dynamically added [data-highlights] elements.

JavaScript API

Access the controller programmatically:

Events

EventDetailDescription
highlights:added { id, text, color, note } Fired when a new highlight is created
highlights:removed { id } Fired when a highlight is deleted
highlights:clicked { id } Fired when a highlight is clicked
highlights:note-changed { id, note } Fired when a note is saved

Accessibility

  • Toolbar uses role="toolbar" with arrow-key navigation between color swatches
  • Screen reader announcements via aria-live="polite" region
  • Keyboard: Escape closes toolbar, Ctrl+Enter saves notes
  • Color swatches use aria-label with color names and aria-pressed for active state
  • Note panel uses role="dialog" with aria-label

CSS Tokens

TokenDefaultDescription
--highlight-yellow#fef08aYellow highlight color
--highlight-green#bbf7d0Green highlight color
--highlight-blue#bfdbfeBlue highlight color
--highlight-pink#fbcfe8Pink highlight color

Related

  • article — Common container for highlights
  • mark — Native highlight element (used as fallback)
  • text-reader — Text-to-speech with word highlighting