ObsiViewer/docs/ui-theming.md

3.5 KiB
Raw Blame History

UI Theming and CM6 Highlight

Theme Variables

  • Global tokens (in src/styles.css):
    • --color-accent main accent color (per-theme override via [data-theme]).
    • --cm-hl-bg computed background for inline highlights (theme dependent).
    • --cm-hl-br border radius for highlights (default 3px).
  • Button tokens:
    • --btn-radius, --btn-padding-y, --btn-padding-x, --btn-font, --btn-shadow, --btn-ring, --btn-speed.
    • --btn-bg, --btn-fg, --btn-bg-hover, --btn-bg-active, --btn-outline.

Defaults

:root {
  --color-accent: #3b82f6;
  --cm-hl-bg: color-mix(in srgb, var(--color-accent) 28%, transparent);
  --cm-hl-br: 3px;
}
[data-theme="dark"], .dark {
  --cm-hl-bg: color-mix(in srgb, var(--color-accent) 22%, transparent);
}
[data-theme="nimbus"] { --color-accent: #7c3aed; }
[data-theme="emerald"] { --color-accent: #10b981; }

CodeMirror 6 Highlight Extensions

Files in src/app/shared/editor/extensions/:

  • highlight-occurrences.extension.ts inline dynamic occurrences via regex.
  • ranged-highlights.extension.ts ranged highlights via StateEffect/StateField.
  • markdown-theme-highlight.extension.ts composes ranged + Markdown HighlightStyle and facet-driven CSS variable.

CSS class

  • .cm-md-highlight { background: var(--cm-hl-bg); border-radius: var(--cm-hl-br, 3px); transition: background var(--btn-speed) ease; }

Angular Integration

Service: EditorHighlightService (src/app/shared/editor/editor-highlight.service.ts)

  • extensions: Extension[] base markdown theme highlight extensions to add at editor creation.
  • occurrencesExtension(pattern) build occurrences extension.
  • applyOccurrences(view, compartment, pattern) reconfigure a Compartment with occurrences.
  • setRanges(view, ranges) / clearRanges(view) ranged highlights via effects.
  • facet() access the facet to optionally override --cm-hl-bg per-view.

Example inside editor component:

// compartments
occurrencesCompartment = new Compartment();
highlightFacetCompartment = new Compartment();

// in EditorState.create extensions
...highlightService.extensions,
occurrencesCompartment.of([]),
highlightFacetCompartment.of([]),

// API usage
highlightOccurrences("TODO");
setHighlights([{ from: 10, to: 24 }]);
clearHighlights();
// optional color override
view.dispatch({ effects: highlightFacetCompartment.reconfigure([ highlightService.facet().of('color-mix(in srgb, var(--color-accent) 40%, transparent)') ])});

Live theme changes are applied by reconfiguring the theme compartment and calling EditorView.requestMeasure(...) to refresh decorations that rely on CSS variables.

Button Utilities

Classes in src/styles.css:

  • Base: btn
  • Variants: btn-solid, btn-outline, btn-ghost
  • Sizes: btn-sm, btn-md, btn-lg

Example:

<button class="btn btn-solid btn-sm">Save</button>
<button class="btn btn-outline btn-sm">Cancel</button>
<button class="btn btn-ghost btn-sm" aria-label="Close"></button>

Accessibility

  • Buttons use focus ring via --btn-outline.
  • Ensure icon-only buttons have aria-label.

Performance Notes

  • Occurrence highlights compute decorations only over visible ranges; cost is O(visibleText) per viewport update.
  • Ranged highlights use a StateField and tr.changes.mapPos to keep ranges aligned after edits; updates are incremental.
  • For large docs (≥5k lines), prefer ranged highlights for bulk operations; occurrences are fine for quick regex patterns like TODO.