skills/ah-fix-dom-flash/SKILL.md
Detect and debug DOM flash/flicker bugs via the agent-browser CLI with the 'ah' prefix. Use for 'ah fix dom flash', or when elements briefly appear in wrong positions, visual artifacts flash after interactions (drag-drop, transitions, animations), framework DOM cleanup races with React/Vue re-renders to leave ghost elements, opacity/transform jumps on mount/unmount, portal content outlives its positioning context, or any single-frame glitch after a state change.
npx skillsauth add arinhubcom/arinhub ah-fix-dom-flashInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
This skill uses the agent-browser skill for all browser interactions.
Load the agent-browser skill (agent-browser skills get core) for help with command syntax or flags.
Diagnostic scripts are in scripts/. The flash detector is configurable via
window.__flashDetectorConfig -- set it before injecting; no manual script
editing needed.
position: fixed/absolute while still having contentagent-browser tab
If the browser does not connect (it auto-starts on first use):
agent-browser doctor
agent-browser open "http://localhost:6006/iframe.html?id=..."
agent-browser snapshot -i
agent-browser screenshot /tmp/before.png
The a11y snapshot returns elements with refs like @e1 button "Apple". These
refs are used in subsequent click/hover/drag commands and go stale after a page
change -- re-snapshot when that happens. Prefer snapshot -i
over screenshot for debugging -- snapshots provide structured data.
If the user provided a suspected element selector, configure the detector first. This avoids manual code editing -- the script reads the config at injection time:
agent-browser eval "window.__flashDetectorConfig = { selector: '[data-dnd-overlay]' }; 'configured'"
Configuration options (all optional):
selector -- CSS selector for suspected elements. Overrides the default overlay selectors.maxDetections -- Maximum entries to record (default: 50).Then read scripts/flash-detector.js and inject it as-is:
{ printf '('; cat scripts/flash-detector.js; printf ')()'; } | agent-browser eval --stdin
Available scripts:
| Script | Purpose | Configure via |
| ---------------------------- | ---------------------------------------------- | ------------------------------ |
| flash-detector.js | Dual-strategy detector (MutationObserver + rAF)| window.__flashDetectorConfig |
| collect-flash-results.js | Collects, deduplicates, and summarizes results | None |
| lingering-fixed-elements.js| Scans for leftover fixed/absolute elements | None |
Trigger the bug using the appropriate command:
# Drag-and-drop
agent-browser drag @e1 @e2
# Click
agent-browser click @e3
agent-browser snapshot -i
# Hover
agent-browser hover @e3
# Keyboard
agent-browser press <key>
Flash bugs are timing-dependent. If the first attempt shows zero detections, repeat the interaction 2-3 more times without re-injecting the detector. It stays active and accumulates results across multiple interactions.
Note: The detector stays active until you inject collect-flash-results.js,
which stops it. For another detection round after collecting, re-inject the
detector (and optionally the config) before reproducing again.
Read scripts/collect-flash-results.js and inject it:
{ printf '('; cat scripts/collect-flash-results.js; printf ')()'; } | agent-browser eval --stdin
The collector separates results into high-confidence flashes and lower-confidence noise:
{
"total": 5,
"flashCount": 2,
"noiseCount": 3,
"summary": { "position-lost": 1, "flash": 1, "added": 2, "attr-change": 1 },
"flashes": [ ... ],
"noise": [ ... ]
}
Focus on the flashes array -- these are actual flash bugs. The noise
array contains MutationObserver events that may be normal DOM activity;
investigate these only if flashes is empty and the user reports a visible
flash.
High-confidence indicators (in flashes):
| Finding | Meaning |
| --------------------------------------- | ------------------------------------------------------ |
| type: "position-lost" | Element was fixed/absolute, became static -- FLASH BUG |
| type: "flash", position is "static" | Overlay has content without positioning |
| type: "transform-lost" at (0,0) | Animation cleared transform before unmount |
Lower-confidence indicators (in noise):
| Finding | Meaning |
| --------------------------------------- | ------------------------------------------------------ |
| type: "added" with suspicious: true | New element at (0,0) or off-viewport -- investigate |
| type: "attr-change" on overlay | Framework changed attributes -- may be normal |
If zero flashes after 3 attempts, use a Performance trace:
agent-browser trace start
# Reproduce the interaction
agent-browser trace stop /tmp/trace.json
After the interaction, check for leftover positioned elements:
{ printf '('; cat scripts/lingering-fixed-elements.js; printf ')()'; } | agent-browser eval --stdin
This reports both position: fixed elements and position: absolute
elements with z-index > 100. Compare against the page's expected
fixed elements (navbar, toast) to identify ghost leftovers.
Match findings to a root cause pattern. For detailed patterns and
framework-specific fixes, read references/root-causes.md.
Quick reference:
| Pattern | Trigger | Key Signal | Fix Strategy |
| ------- | ------- | ---------- | ------------ |
| Framework cleanup vs React re-render | dnd-kit, Radix, Floating UI | position-lost | CSS: hide when positioning attr absent |
| flushSync vs async setState | Mixed sync/async updates | Intermediate state visible | Batch into single render pass |
| Drop animation clearing transform | dnd-kit drop, Framer exit | transform-lost at (0,0) | CSS: hide when transform absent |
| Portal outliving positioning context | Popover/tooltip close | flash on portal element | CSS: display: none when empty |
| Opacity transition initial flash | Mount animations | Element visible before transition | Set opacity: 0 in CSS, not JS |
| AnimatePresence exit timing | Framer Motion unmount | Ghost during exit animation | mode="wait" or onExitComplete |
| GSAP timeline vs React unmount | GSAP .kill() timing | Element at default position | useLayoutEffect cleanup |
| Suspense/lazy FOUC | Code splitting | Unstyled content flash | Matching Suspense fallback layout |
| Z-index pop-through | Reorder, modal stacking | Element briefly behind another | CSS z-index + will-change: transform |
After applying a fix, reload and re-run the full detection cycle:
agent-browser open "..."
agent-browser snapshot -i
# Optionally re-configure
agent-browser eval "window.__flashDetectorConfig = { selector: '...' }; 'ok'"
# Inject detector
{ printf '('; cat scripts/flash-detector.js; printf ')()'; } | agent-browser eval --stdin
# Reproduce interaction 2-3 times
# ...
# Collect -- expect zero flashes
{ printf '('; cat scripts/collect-flash-results.js; printf ')()'; } | agent-browser eval --stdin
# Visual confirmation
agent-browser screenshot /tmp/after-fix.png
agent-browser doctoragent-browser snapshot -i() => { ... }For the full agent-browser command list and flags, load the agent-browser skill (agent-browser skills get core).
development
Use when: (1) building a skill/command that orchestrates other skills by spawning subagents, (2) a delegated sub-skill reads git working-tree state (e.g. `git branch --show-current`) instead of taking it as an argument, (3) a sub-skill is supposed to "reflect on this session" / capture session learnings (like revise-claude-md) but runs in a subagent, (4) base branch ends up wrong or a session-reflection step sees an empty/trivial context. Keywords: orchestrator, subagent isolated context, git branch --show-current, base branch checkout, revise-claude-md, this session.
development
Run the full ArinHub feature-development pipeline end-to-end with the "ah" prefix. Use for "ah workflow", "ah run workflow", "ah full workflow", a GitHub issue URL to take from issue to PR, or any request to run the whole ah pipeline at once. Takes a feature description + issue number + base branch, OR a GitHub issue URL (resolved via references/resolve-gh-issue.md). Sequentially launches subagents: ah-create-prd-adr -> ah-create-tasks -> ah-implement-tasks -> optional ah-check-qa verification -> ah-finalize-code (creates the PR), anchored with /goal and guarded by retry + escalation.
testing
Verify that a PR or local changes fully implement requirements from a linked GitHub issue, with the "ah" prefix. Use for "ah verify requirements coverage", "ah verify requirements coverage issue 42", "ah verify requirements coverage PR 123", or both ("PR 123, issue 42").
development
Submit a completed code review with line-specific comments and suggestions to a GitHub PR, with the "ah" prefix. Use for "ah submit code review 123".