* refactor(skills): consolidate 15 skills into 3 for better trigger reliability Merge 9 GSAP skills (core, timeline, scrolltrigger, plugins, utils, react, frameworks, performance, effects) and 6 HyperFrames skills (compose, captions, tts, audio-reactive, marker-highlight, cli) into 3 consolidated skills: - `gsap` — core API + timelines + performance in SKILL.md; scrolltrigger, plugins, utils, react, frameworks, effects in references/ - `hyperframes` — composition authoring rules in SKILL.md; captions, tts, audio-reactive, marker-highlight in references/ - `hyperframes-cli` — CLI commands (init, lint, preview, render, etc.) Why: With 15 separate skills, agents must correctly trigger the right subset for any task. "Create an animated video with captions" needed 6+ skills to fire — each with ~90% trigger accuracy means ~53% chance of getting all of them. With 3 skills, that same task needs just `hyperframes` + `gsap` (~90% both fire). Progressive disclosure still works via references/ files loaded on demand. Also fixes: CLAUDE.md referenced `window.__GSAP_TIMELINE` (incorrect) — corrected to `window.__timelines`. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(cli): add --skip-skills flag to init command Allow skipping the AI coding skills installation prompt during `hyperframes init` with `--skip-skills`. Useful when skills are already installed or when the user wants to scaffold without them. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(skills): address code review feedback on consolidation Restore content lost during over-compression: - captions: fix overflow to `visible` (not hidden — clips glow effects), add container pattern warning, scale headroom formula, and self-lint placement guidance - audio-reactive: restore sampling frequency pattern (per-frame tl.call loop vs single tween) and textShadow-on-container gotcha - effects/typewriter: restore word rotation, appending words, spacing with static text, and multi-line cursor handoff patterns - effects/audio-visualizer: restore spatial mapping conventions, fetch vs inline loading, WebGL/DOM rendering approaches, and canvas layering - hyperframes-cli: restore --strict-all flag in render flags table Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(cli): update build:copy and template for consolidated skill names - build:copy: reference skills/hyperframes, skills/hyperframes-cli, skills/gsap instead of the old 15 skill directory names - _shared/CLAUDE.md template: update skill table to consolidated names Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5.2 KiB
Marker Highlight
Animated canvas-based text highlighting using MarkerHighlight.js. Wraps text in <mark> tags and renders effects (marker pen, circle, burst, scribble, sketchout) on a canvas overlay without modifying text DOM.
The library runs its own requestAnimationFrame loop — not GSAP-driven. Use tl.call() to trigger at specific timeline points.
Required Script
Download and convert to global script:
curl -sL "https://cdn.jsdelivr.net/gh/Robincodes-Sandbox/marker-highlight@main/dist/marker-highlight.min.js" \
| sed 's/export{[^}]*};$/window.MarkerHighlighter=W;/' > marker-highlight.global.js
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<script src="marker-highlight.global.js"></script>
Color Setup
Set via data-color, copy to data-original-bgcolor before constructing. Never set background-color in CSS.
mark {
color: inherit;
background-color: transparent;
}
<mark id="m1" data-color="rgba(255, 220, 50, 0.5)">highlighted</mark>
document
.querySelectorAll("mark[data-color]")
.forEach((m) => m.setAttribute("data-original-bgcolor", m.getAttribute("data-color")));
GSAP Integration Pattern
ONE MarkerHighlighter per container with animate: false, hide all canvases, then clear+show+reanimate per mark at trigger time.
var hl = new MarkerHighlighter(document.getElementById("text-container"), {
animate: false,
animationSpeed: 800,
padding: 0.3,
highlight: { amplitude: 0.3, wavelength: 5 },
});
setTimeout(function () {
document.querySelectorAll(".highlight").forEach((div) => (div.style.opacity = "0"));
}, 100);
function addHighlight(highlighter, markId, time) {
tl.to(
{},
{
duration: 0.001,
onStart: function () {
var mark = document.getElementById(markId);
var ref = mark.getAttribute("data-mark-ref");
var divs = mark.parentElement.querySelectorAll('.highlight[data-mark-id="' + ref + '"]');
divs.forEach(function (div) {
var canvas = div.querySelector("canvas");
if (canvas) canvas.getContext("2d").clearRect(0, 0, canvas.width, canvas.height);
div.style.opacity = "1";
});
highlighter.reanimateMark(mark);
},
onReverseComplete: function () {
var mark = document.getElementById(markId);
var ref = mark.getAttribute("data-mark-ref");
mark.parentElement
.querySelectorAll('.highlight[data-mark-id="' + ref + '"]')
.forEach((div) => (div.style.opacity = "0"));
},
},
time,
);
}
addHighlight(hl, "m1", 1.0);
Drawing Modes
| Mode | Effect | Best for |
|---|---|---|
highlight |
Wavy marker stroke (default) | Phrases, key terms |
circle |
Hand-drawn ellipse | Single words, annotations |
burst |
Radiating lines/curves/puffs | Excitement, energy |
scribble |
Chaotic scribble | Crossing out, messy energy |
sketchout |
Rough rectangle outline | Boxed callouts, blueprint |
<mark data-drawing-mode="circle" data-color="rgba(229, 57, 53, 0.6)">critical</mark>
<mark
data-drawing-mode="burst"
data-burst='{"style":"cloud","count":20}'
data-color="rgba(255, 220, 50, 0.5)"
>amazing</mark
>
Configuration
Global (constructor)
| Option | Default | Description |
|---|---|---|
animate |
true |
false to defer for GSAP |
animationSpeed |
5000 |
Duration in ms |
drawingMode |
"highlight" |
Default mode |
height |
1 |
Relative to line height |
offset |
0 |
Vertical shift |
padding |
0 |
Horizontal padding |
Per-Mode
highlight: amplitude (0.25), wavelength (1), roughEnds (5), jitter (0.1)
circle: curve (0.5), wobble (0.3), loops (3), thickness (5)
burst: style ("lines"/"curve"/"cloud"), count (10), power (1), randomness (0.5)
Named Styles
MarkerHighlighter.defineStyle("underline", {
animationSpeed: 400,
height: 0.15,
offset: 0.8,
padding: 0,
highlight: { amplitude: 0.2, wavelength: 5, roughEnds: 0 },
});
Mode-to-Caption Energy Mapping
| Energy | Mode | Use for |
|---|---|---|
| High | burst + highlight |
Launches, hype |
| Medium-high | circle |
Key stats, terms |
| Medium | highlight |
Standard emphasis |
| Medium-low | scribble |
Subtle, tutorials |
| Low | sketchout |
Contrast, blueprint |
Notes
- One highlighter per container (clears all
.highlightdivs on init) - Canvas pre-draw + clear pattern for clean reveals
- rAF-based — not seekable mid-stroke
- Use
onReverseCompletefor rewind support
For CSS+GSAP fallback (no library, fully seekable), see css-patterns.md. For full examples, see examples.md.