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

@data-slot/accordion

v0.2.165

Published

Headless accordion component for vanilla JavaScript. Accessible, unstyled, tiny.

Readme

@data-slot/accordion

Headless accordion component for vanilla JavaScript. Accessible, unstyled, tiny.

Installation

npm install @data-slot/accordion

Quick Start

<div data-slot="accordion" data-default-value="one">
  <div data-slot="accordion-item" data-value="one">
    <button data-slot="accordion-trigger">
      <span>Section One</span>
      <svg data-slot="accordion-trigger-icon" viewBox="0 0 12 12" aria-hidden="true">
        <path d="M6.75 0H5.25V5.25H0V6.75H5.25V12H6.75V6.75H12V5.25H6.75V0Z" />
      </svg>
    </button>
    <div data-slot="accordion-content">
      <div data-slot="accordion-content-inner">Content for section one</div>
    </div>
  </div>
  <div data-slot="accordion-item" data-value="two">
    <button data-slot="accordion-trigger">Section Two</button>
    <div data-slot="accordion-content">
      <div data-slot="accordion-content-inner">Content for section two</div>
    </div>
  </div>
</div>

<script type="module">
  import { create } from "@data-slot/accordion";

  const controllers = create();
</script>

API

create(scope?)

Auto-discover and bind all accordion instances in a scope (defaults to document).

import { create } from "@data-slot/accordion";

const controllers = create(); // Returns AccordionController[]

createAccordion(root, options?)

Create a controller for a specific element.

import { createAccordion } from "@data-slot/accordion";

const accordion = createAccordion(element, {
  multiple: true,
  defaultValue: ["one"],
  orientation: "vertical",
  loopFocus: true,
  hiddenUntilFound: false,
  onValueChange: (values) => console.log(values),
});

Options

| Option | Type | Default | Description | | --- | --- | --- | --- | | multiple | boolean | false | Allow multiple items open at once | | defaultValue | string \| string[] | undefined | Initially expanded item(s) | | disabled | boolean | false | Disable all user interaction for the accordion | | orientation | "horizontal" \| "vertical" | "vertical" | Controls roving-focus arrow keys | | loopFocus | boolean | true | Wrap roving focus at the ends | | hiddenUntilFound | boolean | false | Use hidden="until-found" on closed panels | | onValueChange | (value: string[]) => void | undefined | Callback when expanded items change | | collapsible | boolean | true | Deprecated single-mode alias for “can close the last open item” |

Deprecated Option

The following option is deprecated and will be removed in the next major release:

createAccordion(element, {
  // Deprecated: use the default Base UI-style collapsible behavior instead.
  collapsible: false,
});

Data Attributes

Options can also be set via data attributes on the root element. JS options take precedence.

| Attribute | Type | Default | Description | | --- | --- | --- | --- | | data-multiple | boolean | false | Allow multiple items open at once | | data-default-value | string | none | Initially expanded item, or a JSON array string for multiple defaults | | data-disabled | boolean | false | Disable the entire accordion | | data-orientation | "horizontal" \| "vertical" | "vertical" | Controls roving-focus arrow keys | | data-loop-focus | boolean | true | Wrap roving focus at the ends | | data-hidden-until-found | boolean | false | Use hidden="until-found" on closed panels | | data-collapsible | boolean | true | Deprecated single-mode compatibility alias |

Boolean attributes: present or "true" = true, "false" = false, absent = default.

<div
  data-slot="accordion"
  data-multiple
  data-default-value="one"
  data-orientation="horizontal"
>
  ...
</div>

For multiple default items in HTML, encode the value as JSON:

<div
  data-slot="accordion"
  data-multiple
  data-default-value='["one","two"]'
>
  ...
</div>

Controller

| Method/Property | Description | | --- | --- | | expand(value) | Expand an item by value | | collapse(value) | Collapse an item by value | | toggle(value) | Toggle an item by value | | value | Currently expanded values (readonly string[]) | | destroy() | Cleanup all event listeners |

Markup Structure

<div data-slot="accordion">
  <div data-slot="accordion-item" data-value="unique-id">
    <button data-slot="accordion-trigger">
      <span>Trigger</span>
      <span data-slot="accordion-trigger-icon">+</span>
    </button>
    <div data-slot="accordion-content">
      <div data-slot="accordion-content-inner">Content</div>
    </div>
  </div>
</div>

data-slot="accordion-trigger-icon" and data-slot="accordion-content-inner" are optional styling hooks.

Styling

State Hooks

The accordion exposes these useful styling hooks:

| Element | Hooks | | --- | --- | | root | data-disabled, data-orientation | | item | data-state, data-open, data-closed, data-index, data-disabled | | trigger | data-state, data-panel-open, data-disabled, aria-expanded | | content | data-state, data-open, data-closed, data-index, data-disabled, data-orientation, data-starting-style, data-ending-style |

CSS Variables

The content element exposes size variables for height or width transitions:

| Variable | Description | | --- | --- | | --accordion-panel-height | Panel height (auto at rest, measured px during transitions, 0px when closed) | | --accordion-panel-width | Panel width (auto at rest, measured px during transitions, 0px when closed) | | --radix-accordion-content-height | Compatibility alias for Tailwind/Radix accordion keyframes | | --radix-accordion-content-width | Compatibility alias for width-based integrations |

CSS Example

[data-slot="accordion-item"] {
  border-bottom: 1px solid #e5e7eb;
}

[data-slot="accordion-trigger"] {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 1rem;
  background: transparent;
  border: 0;
  text-align: left;
}

[data-slot="accordion-trigger-icon"] {
  margin-left: auto;
  width: 1rem;
  height: 1rem;
  color: #6b7280;
  transition: transform 0.2s ease;
}

[data-slot="accordion-item"][data-state="open"] [data-slot="accordion-trigger-icon"] {
  transform: rotate(45deg);
}

[data-slot="accordion-content"] {
  overflow: hidden;
  height: var(--accordion-panel-height);
  transition: height 0.2s ease;
}

[data-slot="accordion-content"][data-starting-style] {
  height: 0;
}

[data-slot="accordion-content-inner"] {
  padding: 0 1rem 1rem;
}

Tailwind Example

<div data-slot="accordion" class="overflow-hidden rounded-2xl border">
  <div
    data-slot="accordion-item"
    data-value="one"
    class="group border-b data-[open]:bg-muted/50"
  >
    <button
      data-slot="accordion-trigger"
      class="flex w-full items-center gap-6 p-4 text-left text-sm font-medium hover:underline"
    >
      <span>Section One</span>
      <svg
        data-slot="accordion-trigger-icon"
        viewBox="0 0 12 12"
        class="ml-auto size-4 shrink-0 text-muted-foreground transition-transform group-data-[state=open]:rotate-45"
      >
        <path d="M6.75 0H5.25V5.25H0V6.75H5.25V12H6.75V6.75H12V5.25H6.75V0Z" />
      </svg>
    </button>
    <div
      data-slot="accordion-content"
      class="overflow-hidden text-sm data-open:animate-accordion-down data-closed:animate-accordion-up"
    >
      <div
        data-slot="accordion-content-inner"
        class="h-(--accordion-panel-height) px-4 pb-4 pt-0 data-ending-style:h-0 data-starting-style:h-0"
      >
        Content
      </div>
    </div>
  </div>
</div>

Keyboard Navigation

| Key | Action | | --- | --- | | Enter / Space | Toggle focused item | | ArrowDown / ArrowUp | Move focus in vertical accordions | | ArrowRight / ArrowLeft | Move focus in horizontal accordions | | Home | Move focus to first enabled trigger | | End | Move focus to last enabled trigger |

Disabled items are skipped during roving focus.

Events

Outbound Events

Listen for changes via custom events:

element.addEventListener("accordion:change", (e) => {
  console.log("Expanded items:", e.detail.value);
});

Inbound Events

Control the accordion via events:

| Event | Detail | Description | | --- | --- | --- | | accordion:set | { value: string \| string[] } | Set expanded items programmatically |

element.dispatchEvent(
  new CustomEvent("accordion:set", { detail: { value: "one" } })
);

element.dispatchEvent(
  new CustomEvent("accordion:set", { detail: { value: ["one", "two"] } })
);

accordion:set and controller methods still work when the accordion is disabled. User-triggered click and keyboard interaction do not.

License

MIT