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

@agencecinq/disclosure-button

v2.0.0

Published

Accessible WAI-ARIA disclosure button Web Component.

Readme

@agencecinq/disclosure-button

Accessible WAI-ARIA disclosure button Web Component.

A disclosure button shows or hides a section of content. <cinq-disclosure-button> wraps a <button>, wires up aria-expanded / aria-controls, toggles hidden on controlled regions, and dispatches open/close events.

Implementation follows the WAI-ARIA Authoring Practices disclosure pattern. Inspired by @19h47/disclosure-button.

Installation

pnpm add @agencecinq/disclosure-button

Usage

import "@agencecinq/disclosure-button";
<cinq-disclosure-button>
  <button
    type="button"
    aria-expanded="false"
    aria-controls="details-1"
  >
    Show more
  </button>
</cinq-disclosure-button>

<div id="details-1" class="foo" hidden>
  Disclosure content
</div>
.foo[hidden] {
  display: none;
}

.foo {
  display: flex;
}

Importing the package registers the custom element. Controlled regions live outside the host and are resolved via ariaControlsElements.

HTML is the source of truth. The component will not auto-set role, auto-migrate attributes, or warn about missing labels. Use an a11y linter (axe-core, Lighthouse) to catch invalid markup.

Required markup

| Attribute / element | Required | Role | | ------------------- | -------- | ---- | | Inner <button> | Yes | Focusable trigger inside <cinq-disclosure-button>. | | aria-expanded | Yes | Current disclosure state on the trigger. | | aria-controls | Yes | Space-separated ID(s) of the controlled region(s). | | hidden | Yes | On each controlled region when collapsed. |

API

| Method | Description | | ------ | ----------- | | open(emit?) | Shows every controlled element. | | close(emit?) | Hides every controlled element. | | toggle() | Closes if any controlled element is visible; otherwise opens all. | | update() | Syncs aria-expanded from the DOM (e.g. after an external dismiss). | | destroy() | Removes listeners. |

| Property | Description | | -------- | ----------- | | $button | The inner <button>. | | elements | Controlled elements from ariaControlsElements at connect time. | | expanded | Reads aria-expanded on the trigger. Call update() after external DOM changes. |

open() / close() accept an optional emit flag (default true).

When several regions are controlled: aria-expanded is true while at least one is visible; a trigger click closes all if any remain open.

Style via the trigger, e.g. button[aria-expanded="true"] or cinq-disclosure-button:has([aria-expanded="true"]).

Keyboard & focus

The trigger must be a native <button type="button">. Enter and Space activate it through the browser; the component listens for click. Focus stays on the trigger when opening or closing. While collapsed, hidden keeps the region out of the tab order and accessibility tree.

On focus / blur, a .focus class is toggled on the trigger:

cinq-disclosure-button button.focus {
  outline: 2px solid currentColor;
  outline-offset: 2px;
}

One button, multiple targets

aria-controls is an ID reference list — one control may reference several elements (see the ariaControlsElements MDN example). After a partial dismiss (region.hidden = true), call update() so aria-expanded stays honest.

Multiple buttons, one target

Several triggers can share the same ID. Use one <cinq-disclosure-button> per trigger and sync sibling aria-expanded yourself from event.detail.open / event.detail.el.

Programmatic API

const $host = document.querySelector("cinq-disclosure-button");

$host.open();
$host.close();
$host.toggle();
$host.update();
$openLink.addEventListener("click", () => {
  $host.open();
});

$dismissButton.addEventListener("click", () => {
  $host.close();
});

Events

| Event | Cancelable | Detail | Description | | ----- | ---------- | ------ | ----------- | | disclosure-button:open | Yes | { ids, elements, el, open: true } | Fired on the trigger before open. Cancel to abort. | | disclosure-button:close | Yes | { ids, elements, el, open: false } | Fired on the trigger before close. Cancel to abort. |

import { EVENTS } from "@agencecinq/utils";

$host.addEventListener(EVENTS.DISCLOSURE_BUTTON_OPEN, (event) => {
  if (!userMayOpen()) {
    event.preventDefault();
  }
});

event.detail carries { ids, elements, el, open }. Events fire before the DOM mutates — use detail.open rather than reading hidden yet.

Updating the button label

The component does not change the trigger’s visible text. Listen to open/close and update copy in your app:

import { EVENTS } from "@agencecinq/utils";

const labels = { closed: "Show details", open: "Hide details" };

$button.addEventListener(EVENTS.DISCLOSURE_BUTTON_OPEN, () => {
  $button.textContent = labels.open;
});

$button.addEventListener(EVENTS.DISCLOSURE_BUTTON_CLOSE, () => {
  $button.textContent = labels.closed;
});

Build setup

pnpm -C packages/disclosure-button build

Acknowledgments

See the interactive docs for live examples.