npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@excom/event-handler

v0.1.4

Published

<event-handler> custom element

Readme

event-handler

Stitch behaviors of your app together, primarily through events.

<section>
    <event-handler target-ref=":scope + content-drawer" command-name="--toggle">
        Toggle drawer
    </event-handler>
    <content-drawer class="absolute">
        <h2>Open!</h2>
    </content-drawer>
</section>

Features

  • Fire custom events Map any input event, such as clicks, or lifecycles to custom output events
  • Commands command-name invokes the HTML Command API — built-in verbs (show-modal) and the --verb commands elements accept (--submit, --close)
  • Retarget Aim events/commands at any selector (target-ref) — where a <button commandfor> needs an id
  • Global / host listening Listen to events globally or on any element. Helpful for: escape to dismiss, global shortcuts, etc.
  • Keycode filter Escape to dismiss, Shift+K shortcuts — keys / modifier chords
  • Listen filters Debounce, selector, and pathname gates
  • Custom Event Payloads detail-* attributes and forms convert to event.detail JSON

In a view that already has a <quark-sheet>, the same wiring is a rule: @on click (target: "[data-add]") { @dispatch cart-add (detail: (sku: attr("data-sku"))); } — @on options cover selector-filter / keycode-filter / is-debounced / host-ref, and @dispatch / @command cover fire-event / target-ref / form-ref / command-name. Keep <event-handler> for markup without a sheet.

Installation

@excom/event-handler v0.1.4

pnpm add @excom/event-handler
npm install @excom/event-handler
yarn add @excom/event-handler

Import

import "@excom/event-handler";

Usage

Listen (default click), then either fire-event or command-name.

<event-handler fire-event="cart-add">
  Add to cart
</event-handler>

Or, for example: a successful form submit re-fetches the related data by invoking the provider's --fetch command.

<event-handler listen-for="super-form-success" target-ref="#fetch-todos" command-name="--fetch">
  <super-form>
    <form>
      <!-- form to create a new todo -->
    </form>
  </super-form>
</event-handler>

In a Nucleus Stack application, this will be one of the most heavily used elements. It is the primary method of linking a functional cause and effect.

API Reference

Attributes

| Name | Surface | Type | Default | Values | Description | Inherited from | | --- | --- | --- | --- | --- | --- | --- | | target-ref | option | string | | <CSS Selector> | Where outgoing events / mutations / commands apply. Unset = this element. Supports :scope for relative targeting (e.g. :scope ~ dialog). | | | fire-event | option | tokenlist | | <EventName>… | Space-separated event names to dispatch on the target. Each name gets the same merged detail. Ignored when mutate-target is set. Pair with detail-* attributes for static detail fields. | | | command-name | option | tokenlist | | <command>… | Space-separated commands to invoke on the target: custom --verb commands (--submit, --close) dispatch a command event, as a <button command commandfor> would; built-in verbs (show-modal, close, toggle-popover) run through the platform. | | | not-bubbles | option | boolean | | | Outgoing events use bubbles: false (default: bubble). | | | not-cancelable | option | boolean | | | Outgoing events use cancelable: false (default: cancelable). | | | not-composed | option | boolean | | | Outgoing events use composed: false (default: composed / cross shadow roots). | | | form-ref | option | string | | <CSS Selector> | <form> whose fields merge into event detail, or become attributes when mutate-target is set. | | | mutate-target | option | boolean | | | @deprecated Write attributes on the target instead of firing events (sources: attr-* on this element plus form-ref fields). Use a Quark @on <event> { … } block instead — it writes State from the event without one element mutating another. Kept for compatibility; will be removed in a future major. | | | host-ref | option | string | | <CSS Selector> | "window" | "document" | "html" | "body" | "head" | Listen on another element / window / document — e.g. Escape to dismiss a dialog from a global keydown. Defaults to :scope. Used with listen-for. Not compatible with listen-for-lifecycle. The selector MUST resolve when host-ref is set — it will not wait for a match to appear. | @excom/listenable-element | | listen-for | option | tokenlist | | <EventName>… | Space-separated event names to listen for. Defaults to click when unset (and no lifecycle list is set). | @excom/listenable-element | | listen-for-lifecycle | option | tokenlist | | "connected" | "disconnected" | "adopted" | Space-separated element lifecycles to handle. | @excom/listenable-element | | listen-once | option | boolean | | | Handle each distinct event name / lifecycle at most once. | @excom/listenable-element | | selector-filter | option | string | | <CSS Selector> | Only handle events whose event.target matches this CSS selector. Does not support :scope in the selector. | @excom/listenable-element | | keycode-filter | option | tokenlist | | <key | mod+key>… | Space-separated key filters (OR). Join modifiers with + (AND, any order): shift+k tab → Shift+K or Tab. Modifiers: shift, alt, ctrl/control, meta/cmd. Name the space bar space / spacebar and the plus key plus (shift+space). Case-insensitive. | @excom/listenable-element | | pathname-filter | option | tokenlist | | <pathname>… | Only handle when location.pathname is one of these values — route-aware behaviors without a separate router element. | @excom/listenable-element | | prevent-default | option | boolean | | | Call preventDefault() on matched events (ignored for lifecycles). | @excom/listenable-element | | stop-propagation | option | boolean | | | Call stopPropagation() on matched events (ignored for lifecycles). | @excom/listenable-element | | stop-immediate-propagation | option | boolean | | | Call stopImmediatePropagation() on matched events (ignored for lifecycles). | @excom/listenable-element | | vibrate-ms | option | number | "20 (when attribute is present with no value)" | | Vibrate on handle (navigator.vibrate). Empty / 0 uses a 20ms pulse. | @excom/listenable-element | | delay-ms | option | number | | | Delay handling by this many milliseconds. | @excom/listenable-element | | is-debounced | option | boolean | | | With delay-ms, coalesce bursts into one trailing call (debounce). | @excom/listenable-element |

CSS Classes

| Name | Description | | --- | --- | | .unstyled | Skip the pointer cursor from the listenable mixin. |

CSS Aliases

| Alias | Kind | Matches | Description | | --- | --- | --- | --- | | :--event-handler | element | event-handler, .tag-event-handler | |

Examples

Fire a custom event

Click dispatches cart-add event with { sku: "sku-1" } in event.detail.

<div>
  <event-handler fire-event="cart-add" detail-sku="sku-1">
    Add to cart
  </event-handler>
  <output>Waiting…</output>
  <quark-sheet>
    @use "quark:util" as *;

    :scope {
      @on cart-add { output { content: to-json(event.detail); } }
    }
  </quark-sheet>
</div>

Open a dialog

command-name invokes the HTML Command API — here the built-in show-modal opens the sibling <dialog>. Custom commands (command-name="--close") reach any element that handles them, such as <content-drawer>, through a relative target-ref.

<section>
  <event-handler target-ref=":scope + dialog" command-name="show-modal">
    Open dialog
  </event-handler>
  <dialog>
    Hello!
    <form method="dialog"><button>Close</button></form>
  </dialog>
</section>

Close on Enter

keycode-filter gates keyboard handling — here Enter, focused in the input, invokes the drawer's --close command (local / bubbling events only).

<section>
  <event-handler target-ref=":scope + content-drawer" command-name="--toggle">
    Toggle drawer
  </event-handler>
  <content-drawer id="demo-enter-close-drawer" is-open class="absolute">
    <event-handler listen-for="keydown" keycode-filter="enter" target-ref="#demo-enter-close-drawer" command-name="--close">
      <label>
        Favorite color
        <input type="text" placeholder="Type something, then press Enter to close drawer" autofocus />
      </label>
    </event-handler>
  </content-drawer>
</section>

Escape to dismiss (global)

host-ref="window" listens for keydown on the window — Escape closes the dialog even when focus is outside it.

<section>
  <event-handler target-ref=":scope ~ dialog" command-name="show-modal">
    Open dialog
  </event-handler>
  <event-handler
    host-ref="window"
    listen-for="keydown"
    keycode-filter="escape"
    target-ref=":scope ~ dialog"
    command-name="close"
  ></event-handler>
  <dialog>
    Press Escape anywhere to close.
    <form method="dialog"><button>Close</button></form>
  </dialog>
</section>

Cancel a link click

prevent-default cancels the native action — here the wrapped <a> never navigates. Use stop-propagation / stop-immediate-propagation the same way when you need to stop bubbling.

<event-handler prevent-default>
  <a href="https://example.com">This native link does nothing</a>
</event-handler>

Retarget

target-ref aims the outgoing event at another element — useful when the target sits outside of the bubble path.

<div>
  <output>Waiting…</output>
  <event-handler target-ref="output:has(+ :scope)" fire-event="demo-ping"
    detail-label="pong">
    Ping previous output
  </event-handler>
  <quark-sheet>
    output {
      @on demo-ping { content: event.detail.label; }
    }
  </quark-sheet>
</div>

Debounced input / form data

Inherited delay-ms + is-debounced coalesce noisy input into one search-query event. Demo below is debounced every 200ms.

Also demonstrated is form conversion into JSON: text -> string, checkbox -> boolean, etc. Including the data structure: detail.strict -> {detail: {strict}}.

<div>
  <event-handler listen-for="input" delay-ms="200" is-debounced fire-event="search-query" form-ref=":scope form">
    <form>
      <label>
        <input name="detail.q" type="search" placeholder="Type to search…" />
      </label>
      <label>
        Strict search
        <input name="detail.strict" type="checkbox" role="switch" />
      </label>
    </form>
  </event-handler>
  <output>Waiting…</output>
  <quark-sheet>
    @use "quark:util" as *;

    :scope {
      @on search-query { output { content: to-json(event.detail); } }
    }
  </quark-sheet>
</div>

Release notes

0.1.1 (2026-09-23)

  • Ship only dist (and declared extras) in the npm tarball; drop build logs, tests and sources

Full documentation: https://nucleus.excom.dev/packages/event-handler