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

@andersseen/behaviors

v0.2.0

Published

Framework-agnostic DOM behaviors driven by `and-*` attributes (splitter, drag & drop, tooltip, dialog). Progressive enhancement, no framework required.

Downloads

211

Readme

@andersseen/behaviors

Framework-agnostic DOM behaviors driven by and-* attributes. Progressive enhancement for the browser — no framework runtime required. Attach interaction patterns (splitter, drag & drop, tooltip, dialog) to existing HTML using either declarative attributes or low-level imperative APIs.

Sibling packages: @andersseen/headless-components are pure state machines with no DOM; @andersseen/behaviors is the layer that wires interaction directly onto elements already in the page.

Installation

pnpm add @andersseen/behaviors

Usage

Declarative attributes

Call defineBehaviors() once after your DOM is ready. It scans for [and-*] attributes and wires up the corresponding behaviors.

<!doctype html>
<html>
  <body>
    <div and-splitter="horizontal" and-splitter-default-position="30">
      <div and-splitter-panel="primary">Left</div>
      <div and-splitter-handle></div>
      <div and-splitter-panel="secondary">Right</div>
    </div>

    <button and-tooltip="Save changes" and-tooltip-placement="top">Save</button>

    <button and-dialog-trigger="my-dialog">Open dialog</button>
    <div id="my-dialog" and-dialog-position="center">
      <h2>Hello</h2>
      <button and-dialog-close>Close</button>
    </div>

    <script type="module">
      import { defineBehaviors } from '@andersseen/behaviors';
      const cleanup = defineBehaviors({ observe: true });
      // cleanup() destroys every behavior and stops observing.
    </script>
  </body>
</html>

defineBehaviors({ observe: true }) uses a MutationObserver so elements added after the initial scan are wired up automatically.

Imperative APIs

For advanced use cases you can create and destroy behaviors manually. Import the whole surface or a per-behavior subpath for tighter tree-shaking:

import { createSplitter } from '@andersseen/behaviors';
import { createTooltip } from '@andersseen/behaviors/tooltip';

const splitter = createSplitter(document.getElementById('splitter')!, {
  orientation: 'vertical',
});
// ...later
splitter.destroy();

Behaviors

| Attribute | Behavior | Imperative API | Subpath | | -------------------- | --------------------------------- | -------------------------------------- | --------------------------------- | | and-splitter | Resizable panel container | createSplitter(container, options) | @andersseen/behaviors/splitter | | and-draggable | HTML5 drag source | createDraggable(element, config) | @andersseen/behaviors/drag-drop | | and-drop-zone | Drop target with optional sorting | createDropZone(element, config) | @andersseen/behaviors/drag-drop | | and-tooltip | Hover/focus tooltip | createTooltip(element, options) | @andersseen/behaviors/tooltip | | and-dialog-trigger | Modal/drawer trigger | createDialog(targetElement, options) | @andersseen/behaviors/dialog |

Attribute reference

Splitter — on the container: and-splitter="horizontal|vertical", and-splitter-min, and-splitter-max, and-splitter-step, and-splitter-default-position. Children need and-splitter-panel="primary", and-splitter-handle, and-splitter-panel="secondary". Emits and-splitter-change, and-splitter-dragstart, and-splitter-dragend.

Drag & dropand-draggable (+ and-draggable-data, and-draggable-handle, and-draggable-disabled, and-draggable-type), and-drop-zone (+ and-drop-zone-accept, and-drop-zone-sortable, and-drop-zone-disabled). Emits and-dragstart, and-dragend, and-dragenter, and-dragover, and-dragleave, and-drop. Sortable zones auto-reorder on drop.

Tooltipand-tooltip="text" (+ and-tooltip-placement, and-tooltip-delay / and-tooltip-show-delay, and-tooltip-hide-delay, and-tooltip-offset, and-tooltip-disabled, and-tooltip-interactive). Dismissable with Escape (WCAG 1.4.13).

Dialog — trigger with and-dialog-trigger="target-id"; the target and/or trigger accept and-dialog-position, and-dialog-backdrop, and-dialog-close-on-backdrop, and-dialog-close-on-escape, and-dialog-width, and-dialog-height, and-dialog-panel-class, and-dialog-backdrop-class. Any [and-dialog-close] inside closes it. Focus is trapped (disabled/hidden controls skipped) and restored on close.

Accessibility

  • Splitter handle gets role="separator", aria-orientation, aria-valuemin/max/now, keyboard resize (arrows / Home / End), and a default aria-label when the author supplies none.
  • Tooltip wires aria-describedby and can be dismissed with Escape without moving the pointer.
  • Dialog sets role="dialog", aria-modal, traps focus (skipping [disabled] / [aria-hidden="true"]), locks body scroll, and restores focus to the trigger on close.

Browser support

All behaviors touch the DOM and are browser-only. Call defineBehaviors() only after document.body is available (e.g. inside a DOMContentLoaded handler or at the end of <body>).

Development

pnpm -C packages/behaviors test        # Vitest (jsdom)
pnpm -C packages/behaviors lint
pnpm -C packages/behaviors build       # ESM + CJS

License

MIT