Simple Vanilla JavaScript Accordion Library

Lightweight, accessible, and dependency-free

Vanilla JavaScript accordion with CSS Grid motion, automatic ARIA, opt-in auto-init, and a theme-less default that still looks great. Under 2KB gzipped.

v1.3.1

Overview

Why choose Shutters

Highlights of what makes this accordion different.

What's new in v1.3

  • isOpen(index) — query open state by panel index
  • Stronger ARIA wiring — automatic aria-controls linked to panel ids
  • Motion Lab — compare CSS Grid, max-height, and native <details> side by side
  • Marketing site — demos and docs at shuttersjs.com

Why Choose Shutters Accordion?

Zero Dependencies & Tiny Footprint

Shutters is a lightweight JavaScript accordion with zero external dependencies. The entire component — JavaScript and CSS combined — weighs under 2KB gzipped, making it one of the smallest accordion solutions available. Unlike jQuery-based accordion plugins or heavy UI frameworks, Shutters adds virtually no overhead to your page load time.

Built-In Accessibility (WCAG 2.1 Compliant)

Accessibility isn't an afterthought — it's built into the core of Shutters. The component automatically applies WAI-ARIA Accordion Pattern attributes — role="button", tabindex="0", aria-expanded, and aria-controls linked to panel ids — on every header at initialization, so you never need to write them in your HTML. Full keyboard navigation is supported via Enter, Space, Arrow Up, Arrow Down, Home, and End keys, with visible :focus-visible indicators for keyboard users.

Smooth 60fps CSS Grid Animations

Instead of using JavaScript-driven height calculations or the jittery max-height hack, Shutters leverages CSS Grid's grid-template-rows transition with transition-behavior: allow-discrete for smooth expand and collapse at 60fps. Separate open/close easing curves and a body opacity fade make motion feel more natural. On the JS side, Shutters uses event delegation — just one click listener per container instead of one per header — keeping memory usage minimal even with many panels.

Simple API & Easy Integration

Get an accordion running in under 5 minutes. Shutters provides both ES Module and UMD bundle formats, so it works with Vite, Webpack, Rollup, or a simple <script> tag. The programmatic API offers open(), close(), toggle(), openAll(), closeAll(), and destroy() methods for full control. Query state with isOpen(index). Subscribe to state changes with on('shutters:open', callback) and on('shutters:close', callback).

Live demos

Try Shutters in the browser

Interactive examples — click panels, use keyboard navigation, and explore each feature.

Basic Accordion Demo

What is the Shutters Accordion?

Shutters Accordion is a lightweight, dependency-free JavaScript accordion component that provides smooth expand/collapse functionality using modern CSS Grid animations and vanilla JavaScript. It's designed for easy integration into any web project — from static HTML pages to React, Vue, or Svelte applications. Install via npm, include via CDN, or add the source files directly.

How does the CSS Grid animation technique work?

Shutters uses the modern CSS Grid grid-template-rows transition technique to animate content height from 0fr to 1fr. This avoids the common pitfalls of max-height hacks or JavaScript-measured height animations. The result is smooth, GPU-accelerated 60fps animations that work with any content height.

  • Vanilla JavaScript with zero dependencies
  • CSS Grid animations for smooth, jank-free transitions
  • Full accessibility with auto ARIA setup and keyboard navigation (Arrow/Home/End)
  • Event delegation — one listener per container for optimal performance
  • Custom events (shutters:open/shutters:close) for reactive integrations
  • Works with dynamic content — no fixed heights needed
What are the key features?

Key features of Shutters Accordion include:

  • Lightweight: Under 2KB gzipped — zero dependencies, pure vanilla JavaScript and CSS
  • Accessible: WAI-ARIA accordion pattern with full keyboard navigation and screen reader support
  • Smooth Animations: CSS Grid-based 60fps expand/collapse transitions
  • Modular CSS: Default core.css (polished, theme-less) + optional theme.css
  • Auto-init: Opt-in data-shutters + shutters-accordion/auto
  • Motion: allow-discrete grid transitions, open/close easing, prefers-reduced-motion
  • Customizable: Theme via CSS custom properties — colors, timing, easing
  • Auto-Close Mode: Optional single-panel-open behavior
  • Programmatic API: open(), close(), toggle(), openAll(), closeAll(), destroy() methods
  • Custom Events: Subscribe to shutters:open and shutters:close via on()/off()
  • Event Delegation: One listener per container — optimal performance at any scale
  • Universal: ES Module + UMD bundles — works with any build tool or plain HTML

Auto-Close Accordion Demo

In auto-close mode, only one accordion panel can be open at a time. Opening a new section automatically collapses the previously open one — ideal for FAQs and settings panels.

What is auto-close mode?

Auto-close mode ensures only one accordion panel is expanded at any time. When a user opens a new section, the previously open section automatically collapses. Simply add the shutters-autoclose CSS class to your accordion container to enable this behavior.

When should I use auto-close?

Auto-close mode is ideal for FAQ sections, settings panels, mobile navigation menus, and any interface where showing one section at a time reduces cognitive load and keeps the layout compact.

Can I mix auto-close and multi-open accordions?

Yes! Shutters supports multiple accordion instances on the same page. Each container is independently configured — you can have one accordion in auto-close mode and another allowing multiple panels open simultaneously.

Default CSS: Core vs Theme

v1.2 ships theme-less core.css as the default npm import — clean dividers, focus ring, and smooth motion. Add optional theme.css for a decorative black-and-white style.

Core only (core.css)

Polished minimal presentation — ideal for custom design systems.

Minimal default styling

Inherits your page typography and colors.

Core + theme (theme.css)

Optional decorative layer — borders, hover fills, content spacing.

Themed presentation

Drop-in visual style for prototypes and marketing pages.

Opt-in Auto-Init

No manual new ShuttersAccordion() required. Add data-shutters and import shutters-accordion/auto (or call initAll() from this demo's source).

Initialized via data-shutters

This container uses data-shutters and data-shutters-default-open="first". Auto-close is enabled with shutters-autoclose.

Zero boilerplate JavaScript

Perfect for static sites, CMS templates, and quick prototypes.

Motion & Accessibility

Fluid CSS Grid transitions

Uses grid-template-rows: 0fr → 1fr with transition-behavior: allow-discrete, separate open/close easing curves, and a subtle opacity fade on .shutters-body.

prefers-reduced-motion

When reduced motion is requested at the OS level, Shutters disables transitions instantly — content remains fully accessible without animation.

Comparisons

How Shutters stacks up

Side-by-side motion techniques and library alternatives.

Looking for a simple JavaScript accordion without jQuery, max-height hacks, or framework lock-in? Shutters is a vanilla JS accordion library that uses CSS Grid for smooth height animation.

Motion Lab

Compare three expand/collapse techniques side by side. Open the same panel in each column and notice how timing and smoothness differ.

CSS Grid (Shutters)

True content height — no arbitrary cap

Panel A

Grid 0fr → 1fr transition tracks actual height at 60fps.

Panel B

Separate open/close easing and a body opacity fade.

max-height hack

Fixed cap — fast open, sluggish close

Animates to a guessed max height — often feels uneven.

Content taller than the cap still clips or stalls mid-animation.

<details> native

Instant toggle — no CSS height transition

Panel A

Built-in semantics with zero JavaScript — but no smooth animation.

Panel B

Keyboard support is partial compared to a full accordion pattern.

Shutters vs HTML <details>/<summary>

Native <details> is great for zero-JavaScript disclosure, but it is not a full FAQ accordion solution. There is no smooth height animation, no auto-close mode (only one panel open), no open()/close() API, and keyboard behavior varies by browser. Shutters gives you a vanilla JavaScript accordion library with CSS Grid motion, WAI-ARIA patterns, and programmatic control — still under 2KB gzipped.

Shutters vs native details element
Feature Shutters HTML <details>
JavaScript required Small (~2KB gzip) None
Smooth height animation CSS Grid 0fr → 1fr Instant toggle only
Auto-close (one open panel) shutters-autoclose Not built-in
Programmatic API open, close, toggle, isOpen, … None
Full accordion keyboard pattern Enter, Space, arrows, Home, End Partial / inconsistent

Shutters vs Other Accordion Libraries

When choosing an accordion component for your website, key factors include bundle size, accessibility compliance, animation quality, and dependency requirements. Here's how Shutters compares to common alternatives:

Shutters vs jQuery UI Accordion

jQuery UI's accordion requires jQuery as a dependency (~90KB minified), making it significantly heavier. Shutters has zero dependencies and weighs under 2KB gzipped. Shutters also uses modern CSS Grid animations instead of jQuery's JavaScript-driven height calculations, resulting in smoother 60fps transitions. For new projects, Shutters offers a modern, lightweight alternative.

Bundle size comparison: Shutters vs jQuery UI
Solution Approx. gzip size Runtime dependencies
Shutters Accordion < 2KB (JS + core CSS) Zero
jQuery UI Accordion ~90KB+ (jQuery + widget) jQuery
Shutters vs HTML <details>/<summary>

The native <details>/<summary> elements provide basic expand/collapse without JavaScript, but lack smooth animated transitions, auto-close behavior, custom events, and programmatic control. Shutters adds smooth CSS Grid animations, an auto-close mode, custom events (shutters:open/shutters:close), a full programmatic API with on()/off() listeners, and consistent cross-browser styling — while maintaining the same level of accessibility.

Shutters vs Framework-Specific Accordions

Framework-specific accordions (React Aria, Radix UI, Headless UI) are tightly coupled to their ecosystems. Shutters is framework-agnostic — it works with vanilla HTML, React, Vue, Svelte, Angular, Astro, or any other framework. If you need a single accordion component that works everywhere without lock-in, Shutters is the pragmatic choice.

Documentation

Get started

Installation, HTML structure, API reference, and customization.

Copy-paste demo (60 seconds)

A minimal working accordion — paste the HTML, load Shutters from a CDN, and initialize:

<div class="shutters-accordion">
  <div class="shutters-item">
    <div class="shutters-header">
      <span class="shutters-title">Question one</span>
      <span class="shutters-icon"></span>
    </div>
    <div class="shutters-content">
      <div class="shutters-body"><p>Answer one.</p></div>
    </div>
  </div>
</div>

<link rel="stylesheet" href="https://unpkg.com/shutters-accordion@1.3.1/dist/core.css">
<script src="https://unpkg.com/shutters-accordion@1.3.1/dist/shutters.umd.js"></script>
<script>
  new ShuttersAccordion({ container: '.shutters-accordion' });
</script>

How to Use Shutters Accordion

How to install Shutters accordion with npm

Install Shutters Accordion via npm, or include it directly from a CDN:

npm install shutters-accordion
<link rel="stylesheet" href="core.css" />
<!-- optional: -->
<link rel="stylesheet" href="theme.css" />

CDN with Subresource Integrity (hashes refreshed via npm run sri after each release build):

Loading CDN integrity snippet…

CDN mirrors (replace @1.3.1 with your version):

  • unpkg: https://unpkg.com/shutters-accordion@1.3.1/dist/
  • jsDelivr: https://cdn.jsdelivr.net/npm/shutters-accordion@1.3.1/dist/

Files: core.css, optional theme.css, shutters.es.js, shutters.umd.js, shutters.auto.es.js.

How to build a vanilla JS FAQ accordion

For FAQ pages, add shutters-autoclose so only one question is open at a time. Shutters applies ARIA and keyboard support automatically — no manual attributes in your HTML.

<div class="shutters-accordion shutters-autoclose">
  <div class="shutters-item">
    <div class="shutters-header">
      <span class="shutters-title">How do I install Shutters?</span>
      <span class="shutters-icon"></span>
    </div>
    <div class="shutters-content">
      <div class="shutters-body">
        <p>Run <code>npm install shutters-accordion</code>.</p>
      </div>
    </div>
  </div>
</div>

<script type="module">
  import ShuttersAccordion from 'https://unpkg.com/shutters-accordion@1.3.1/dist/shutters.es.js';
  import 'https://unpkg.com/shutters-accordion@1.3.1/dist/core.css';
  new ShuttersAccordion({ container: '.shutters-accordion', defaultOpen: 'none' });
</script>

2. HTML Structure

Create your accordion with clean, minimal HTML. ARIA attributes (role, tabindex, aria-expanded) are added automatically by JavaScript — you don't need to write them:

<div class="shutters-accordion">
  <div class="shutters-item">
    <div class="shutters-header">
      <span class="shutters-title">Section Title</span>
      <span class="shutters-icon"></span>
    </div>
    <div class="shutters-content">
      <div class="shutters-body">
        <p>Your content goes here</p>
      </div>
    </div>
  </div>
</div>

How to make an accessible accordion in vanilla JavaScript

Shutters applies the WAI-ARIA accordion pattern automatically — you do not write role, tabindex, aria-expanded, or aria-controls in your HTML. Full keyboard support (Enter, Space, arrow keys, Home, End) and visible :focus-visible rings are built in. See the About page for accessibility details.

How to use Shutters without a framework

Initialize the accordion with optional configuration options, or pick a framework tab:

import ShuttersAccordion from 'shutters-accordion';
import 'shutters-accordion/core.css';

const accordion = new ShuttersAccordion({
  container: '.shutters-accordion',
  animationDuration: 350,
  animationEasing: 'cubic-bezier(0.4, 0, 0.2, 1)',
  defaultOpen: 'none'
});

Or opt-in auto-init (no manual new ShuttersAccordion()):

import 'shutters-accordion/core.css';
import 'shutters-accordion/auto';

4. Configuration Options

container
CSS selector, DOM element, or array of containers (default: '.shutters-accordion')
animationDuration
Animation speed in milliseconds (default: 300)
animationEasing
CSS easing function (default: 'ease-in-out')
defaultOpen
Default open items: 'first', 'all', 'none', or an index array like [0, 2]

5. Programmatic API

Control accordion items programmatically with the JavaScript API:

accordion.open(0);       // Open item by index
accordion.close(2);      // Close item by index
accordion.toggle(1);     // Toggle item by index
accordion.openAll();     // Open all items
accordion.closeAll();    // Close all items
accordion.destroy();     // Remove all listeners and clean up

// Listen for state changes
accordion.on('shutters:open', (e) => {
  console.log('Opened:', e.detail.item);
});

accordion.on('shutters:close', (e) => {
  console.log('Closed:', e.detail.item);
});

6. Auto-Close Mode

Add the shutters-autoclose class to restrict the accordion to one open panel at a time:

<div class="shutters-accordion shutters-autoclose">
  <!-- Only one item can be open at a time -->
</div>

7. Customization via CSS Custom Properties

Override CSS custom properties to match your design system:

.shutters-accordion {
  --shutters-animation-duration: 0.35s;
  --shutters-animation-easing: cubic-bezier(0.4, 0, 0.2, 1);
  --shutters-animation-easing-close: cubic-bezier(0.4, 0, 1, 1);
  --shutters-bg-color: #fff;
  --shutters-border-color: #000;
  --shutters-focus-color: #0066cc;
}