highlight-round
v1.0.0
Published
Inline text highlights with true rounded corners — including concave radii at line-width transitions. Pure vanilla JS + SVG, no dependencies.
Downloads
97
Maintainers
Readme
highlight-round
Inline text highlights with true rounded corners — including concave (inverted) radii where line-widths step in or out. Pure vanilla JS + SVG. No dependencies. No build step.

Install
npm install highlight-roundOr via CDN (no build step):
<script src="https://unpkg.com/highlight-round/highlight-round.js"></script>Quick start
<!-- 1. Load the library -->
<script src="node_modules/highlight-round/highlight-round.js"></script>
<!-- 2. Mark any inline element -->
<p>
This sentence has a
<span data-highlight>highlighted phrase that can wrap across lines</span>
and then continues normally.
</p>
<!-- 3. Initialise -->
<script>
document.addEventListener('DOMContentLoaded', () => {
new HighlightRound({ color: '#3B3BFF' }).init('[data-highlight]');
});
</script>Per-element colour
<span data-highlight data-highlight-color="#e8394f">red highlight</span>
<span data-highlight data-highlight-color="#1aaa6e">green highlight</span>
<span data-highlight data-highlight-color="#1a1a1a" data-highlight-text-color="#ffffff">dark highlight</span>
<span data-highlight data-highlight-padding-x="20" data-highlight-padding-y="12">extra roomy</span>Nesting
Highlights can be nested. Edges on the same visual line are automatically aligned flush — no micro-overlap or pixel gaps.
<span data-highlight data-highlight-color="#3B3BFF">
Outer highlight with
<span data-highlight data-highlight-color="#e8394f">an inner one</span>
continuing after.
</span>Options
All options with their defaults — copy, tweak, go:
const rh = new HighlightRound({
radius: 14, // corner radius, convex & concave
paddingX: 10, // horizontal padding per line
paddingY: 6, // vertical padding per line
color: '#3B3BFF', // default fill colour
textColor: '#ffffff', // default text colour
snapThreshold: 10, // edges within this px range snap flush
bevel: 0, // diagonal slant at step corners (0 = sharp)
forceColor: false, // ignore per-element data-highlight-color
});
rh.init('[data-highlight]');Any option can also be overridden per element via data-highlight-* attributes:
data-highlight-color="…" <!-- fill colour -->
data-highlight-text-color="…" <!-- text colour -->
data-highlight-padding-x="…" <!-- horizontal padding -->
data-highlight-padding-y="…" <!-- vertical padding -->API
const rh = new HighlightRound({ radius: 16, color: '#3B3BFF' });
// Highlight all matching elements and watch for layout changes
rh.init('[data-highlight]');
// Highlight a single element (e.g. after a DOM update)
rh.highlight(someElement);After a significant DOM change affecting multiple elements, trigger a full redraw:
document.getElementById('rh-overlay').remove();
new HighlightRound({ radius: 16 }).init('[data-highlight]');CSS note
The library is entirely self-contained. The only CSS that helps in edge cases with stacking contexts is:
[data-highlight] {
position: relative;
z-index: 1;
}Add this to your stylesheet if highlighted text ever appears behind other elements.
How it works
- Measure — a
TreeWalkercollectsgetClientRects()from each non-whitespace text node inside the element, giving one rect per visual line fragment. - Merge — rects on the same line are unioned into one rect per line.
- Cross-snap — before drawing, all elements' rects are compared. Rects on the same visual line across different elements are snapped to the same Y/height and to matching X edges within
snapThreshold. This keeps nested highlights flush. - Polygon — line rects are traced clockwise into a polygon, with step vertices inserted wherever lines change width.
- Classify corners — the 2D cross product of consecutive edge vectors determines whether each corner is convex (
sweep=1) or concave (sweep=0). - Draw — each corner becomes an SVG arc. The result is appended to a fixed-position SVG overlay behind the text.
License
MIT
