Skip to content

Repository files navigation

hitstack

Sample a coordinate and see the browser's rendered DOM hit-test stack there, the top sampled candidate, and the computed state that can explain surprising interception.

One dependency-free script. No extension. No account. No network.


What it does

Inject dist/hitstack.js into any page. Call window.__hitstack.activate(). Move your pointer to a coordinate. Press F2. The instrument shows:

  • every element the browser returns for that coordinate, ordered top to bottom
  • the computed pointer-events, opacity, and visibility for each candidate
  • a Copy receipt button that writes a hitstack-snapshot/v1 JSON to your clipboard

Press Escape or call deactivate() to tear down completely.

What this is not: a "target," "receiver," "handler," or event-delivery truth. elementsFromPoint is a hit-test primitive, not an event-delivery oracle. The element at index 0 is the top sampled candidate — not a claim about what the page would deliver a click to. Known divergences include disabled controls, pointer capture, shadow retargeting, cross-origin frames, and top-layer chains.


Loading

<script src="dist/hitstack.js"></script>

Or paste the contents of dist/hitstack.js into a browser console.

dist/hitstack.js is a self-contained IIFE. No imports, no exports, no bundler required. It installs window.__hitstack and nothing else.


API

const controller = window.__hitstack.activate({ sampleKey: 'F2' });
// sampleKey is optional; defaults to 'F2'

controller.deactivate();       // full teardown
controller.isActive();         // boolean
controller.getReceipt();       // returns the hitstack-snapshot/v1 object, or null

Sampling model

  • F2 only (configurable at activation). The key is captured in the capture phase and stopPropagation'd — the host page never receives it while the instrument is active. This is a deliberate event-flow mutation.
  • No hover sampling. Pointer position supplies the coordinate; moving the pointer never triggers a sample.
  • No armed click. Sampling never dispatches a page click.
  • One frozen sample. Coordinate, ordered candidates, and computed state are captured once at F2. Scroll or resize after the sample invalidates it with an explicit message; the instrument never auto-re-samples.

Copy receipt — hitstack-snapshot/v1

The copied JSON contains tag only in each candidate's descriptor — no id, no class. The on-screen rail may display id/class tokens for legibility; those are never copied. Copy is explicit and local only; nothing is uploaded or stored.

Example (an opacity:0; pointer-events:auto overlay above a button, sampled at the button's center):

{
  "schema": "hitstack-snapshot/v1",
  "point": { "space": "viewport-client", "x": 50, "y": 183 },
  "candidates": [
    { "index": 0, "descriptor": { "tag": "div" },
      "state": { "pointerEvents": "auto", "opacity": "0", "visibility": "visible" } },
    { "index": 1, "descriptor": { "tag": "button" },
      "state": { "pointerEvents": "auto", "opacity": "1", "visibility": "visible" } },
    { "index": 2, "descriptor": { "tag": "div" },
      "state": { "pointerEvents": "auto", "opacity": "1", "visibility": "visible" } },
    { "index": 3, "descriptor": { "tag": "body" },
      "state": { "pointerEvents": "auto", "opacity": "1", "visibility": "visible" } },
    { "index": 4, "descriptor": { "tag": "html" },
      "state": { "pointerEvents": "auto", "opacity": "1", "visibility": "visible" } }
  ],
  "boundaries": {
    "scope": "top-document-only",
    "shadowInternals": "not-descended",
    "iframeContents": "not-inspected"
  },
  "engine": { "family": "Chromium", "userAgent": "..." }
}

Index 0 (opacity: "0", pointer-events: "auto") is the invisible interceptor. Index 1 is the underlying visible button. Both appear in one F2 sample.


Observability boundaries

Boundary v1 treatment
Closed shadow internals Opaque; host shown as terminal
Open shadow internals Not descended in v1; host shown as terminal
All iframe contents (same-origin and cross-origin) Opaque; labelled iframe-contents-not-inspected
Pseudo-elements Host element only
Top-layer chains (<dialog>) Stack is not DOM ancestry; global caveat
Disabled controls May be top candidate; no click-delivery claim
Pointer capture / delegation Out of scope; never inferred

Modal and fullscreen

While a host showModal() dialog or fullscreen element is active, the instrument's overlay may be occluded — position: fixed cannot paint above the top layer. The instrument does not detect modal or fullscreen state and provides no fallback surface. Sampled data remains correct; only the visual presentation is unsupported in this configuration.


Engine support

Engine Status
Chromium Supported
Firefox Supported
WebKit / Safari Out of scope — untested, unsupported
Mobile / touch Out of scope — no touch sampling path

What this is not

Not a browser extension, DevTools panel, or inspector suite. Not an event-listener debugger. Not an AI fixer. Not a CSS / z-index / accessibility inspector. Not a selector generator. Not a mutation or design-editing tool. Not a persistent inspection session. No account, backend, telemetry, or dashboard.

See LIMITATIONS.md for the full caveat list.


Development

npm install            # Playwright is the only dev dependency
npx playwright install chromium firefox

npm run build          # regenerate dist/hitstack.js from src/
npm run test:unit      # node --test: pure modules + build fidelity
npm run test:e2e       # Playwright, Chromium + Firefox
npm test               # both

dist/hitstack.js is generated. Edit src/ and re-run npm run build; a test asserts the committed bundle is byte-identical to a fresh build, so an unbuilt change fails rather than shipping quietly.


License

MIT. See LICENSE.

About

A page-local instrument for inspecting the rendered DOM hit-test stack at a coordinate.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages