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-controlslinked to panelids - 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?
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.
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.
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.
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
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.
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
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) + optionaltheme.css -
Auto-init: Opt-in
data-shutters+shutters-accordion/auto -
Motion:
allow-discretegrid 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:openandshutters:closevia 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.
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.
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.
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.
Inherits your page typography and colors.
Core + theme (theme.css)
Optional decorative layer — borders, hover fills, content spacing.
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).
This container uses data-shutters and data-shutters-default-open="first". Auto-close is enabled with shutters-autoclose.
Perfect for static sites, CMS templates, and quick prototypes.
Motion & Accessibility
Uses grid-template-rows: 0fr → 1fr with
transition-behavior: allow-discrete, separate open/close easing curves,
and a subtle opacity fade on .shutters-body.
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
Grid 0fr → 1fr transition tracks actual height at 60fps.
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.
| 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:
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.
| Solution | Approx. gzip size | Runtime dependencies |
|---|---|---|
| Shutters Accordion | < 2KB (JS + core CSS) | Zero |
| jQuery UI Accordion | ~90KB+ (jQuery + widget) | jQuery |
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.
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'
});
import { useEffect, useRef } from 'react';
import ShuttersAccordion from 'shutters-accordion';
import 'shutters-accordion/core.css';
export function FaqAccordion() {
const ref = useRef(null);
useEffect(() => {
const accordion = new ShuttersAccordion({ container: ref.current });
return () => accordion.destroy();
}, []);
return (
<div ref={ref} className="shutters-accordion">
{/* items… */}
</div>
);
}
<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue';
import ShuttersAccordion from 'shutters-accordion';
import 'shutters-accordion/core.css';
const root = ref(null);
let accordion;
onMounted(() => {
accordion = new ShuttersAccordion({ container: root.value });
});
onBeforeUnmount(() => accordion?.destroy());
</script>
<template>
<div ref="root" class="shutters-accordion">
<!-- items… -->
</div>
</template>
<script>
import { onMount, onDestroy } from 'svelte';
import ShuttersAccordion from 'shutters-accordion';
import 'shutters-accordion/core.css';
let root;
let accordion;
onMount(() => {
accordion = new ShuttersAccordion({ container: root });
});
onDestroy(() => accordion?.destroy());
</script>
<div bind:this={root} class="shutters-accordion">
<!-- items… -->
</div>
Or opt-in auto-init (no manual new ShuttersAccordion()):
import 'shutters-accordion/core.css';
import 'shutters-accordion/auto';
4. Configuration Options
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;
}