← Component Library
Interactive Components

Interactive Patterns

Behaviors that respond to the user — toasts, drawers, wizards, and autocomplete. Each with a live demo and the HTML & CSS.

01 · Toast Notification

A brief, auto-dismissing message

A toast slides in, confirms an action, and disappears. Click the button to trigger one.

HTML

<button id="save">Save &amp; show toast</button>
<div id="toast" class="toast">Saved!</div>

CSS

.toast {
  position: fixed; bottom: 24px; right: 24px;
  transform: translateY(120%); opacity: 0;
  transition: transform .3s, opacity .3s;
}
.toast.show { transform: translateY(0); opacity: 1; }

JavaScript

const save = document.getElementById('save');
const toast = document.getElementById('toast');
save.addEventListener('click', () => {
  toast.classList.add('show');
  setTimeout(() => toast.classList.remove('show'), 2500);
});
03 · Drawer

Slide a panel in from the right

A drawer slides in from a screen edge for filters, a cart, or settings. This pure-CSS version toggles with a hidden checkbox. Click “Open”.

Page content…

HTML

<input type="checkbox" id="dr">
<label class="open" for="dr">Open ›</label>
<nav class="drawer">
  <label class="close" for="dr">×</label>
  <a href="#">Filters</a>
  <a href="#">Sort</a>
  <a href="#">Saved</a>
</nav>
<div class="body">Page content…</div>

CSS

.drawer {
  position: fixed; top: 0; right: 0; height: 100%;
  transform: translateX(100%);          /* off-screen right */
  transition: transform .3s ease;
}
#dr:checked ~ .drawer { transform: translateX(0); }
04 · Off-Canvas Panel

Slide in from the left

The same technique mirrored to the left edge — typical for a mobile navigation drawer. Click “Menu”.

Page content…

HTML

<input type="checkbox" id="oc">
<label class="open" for="oc">☰ Menu</label>
<nav class="offcanvas">
  <label class="close" for="oc">×</label>
  <a href="#">Home</a>
  <a href="#">Shop</a>
  <a href="#">About</a>
</nav>
<div class="body">Page content…</div>

CSS

.offcanvas {
  position: fixed; top: 0; left: 0; height: 100%;
  transform: translateX(-100%);          /* off-screen left */
  transition: transform .3s ease;
}
#oc:checked ~ .offcanvas { transform: translateX(0); }
05 · Wizard

Switch between steps

A multi-step panel where clicking a step shows its content — here driven entirely by radio inputs, no JavaScript. Click the step tabs.

Create your account — email and password.
Choose a plan that fits your needs.
All set! Review and finish.

HTML

<div class="wizard">
  <input type="radio" name="w" id="w1" checked>
  <input type="radio" name="w" id="w2">
  <input type="radio" name="w" id="w3">
  <div class="steps"><label for="w1">1. Account</label><label for="w2">2. Plan</label><label for="w3">3. Done</label></div>
  <div class="pane p1">Create your account — email and password.</div>
  <div class="pane p2">Choose a plan that fits your needs.</div>
  <div class="pane p3">All set! Review and finish.</div>
</div>

CSS

.pane { display: none; }
#w1:checked ~ .p1,
#w2:checked ~ .p2,
#w3:checked ~ .p3 { display: block; }   /* show the active step */
06 · Search Suggestions

Hint what to search

A list of popular or recent queries shown under the search box to guide the user before they finish typing.

  • flexbox basics
  • flex vs grid
  • flex-wrap examples

HTML

<input type="search" aria-label="Search" placeholder="Search…" value="flex">
<ul class="suggestions">
  <li>flexbox basics</li>
  <li>flex vs grid</li>
  <li>flex-wrap examples</li>
</ul>

CSS

.suggestions { border: 1px solid #e5e7eb; border-radius: 8px; }
.suggestions li { padding: 9px 12px; cursor: pointer; }
.suggestions li:hover { background: #f1f5f9; }
07 · Autocomplete

Filter the list as you type

Like suggestions, but the list narrows to match what's typed. Type in the box — a few lines of JavaScript do the filtering.

  • Canada
  • Chile
  • China
  • Colombia
  • Croatia

HTML

<input id="country" placeholder="Type a country…">
<ul id="list"><li>Canada</li><li>Chile</li><li>China</li><li>Colombia</li><li>Croatia</li></ul>

CSS

#list li.hidden { display: none; }   /* JS adds this class */

JavaScript

const input = document.getElementById('country');
const items = document.querySelectorAll('#list li');
input.addEventListener('input', () => {
  const q = input.value.toLowerCase();
  items.forEach(li =>
    li.classList.toggle('hidden', !li.textContent.toLowerCase().startsWith(q)));
});
08 · Accordion

Collapsible panels, one open at a time

An accordion stacks expandable panels — click a header to open it and the others close. Click the headers below. The chevron rotates to show open/closed state.

A set of stacked headers that each reveal a panel of content when clicked — great for FAQs and long forms where you want to save space.

When content is naturally chunked and users only need one piece at a time — settings, FAQs, product details.

Yes — use real <button> headers and toggle aria-expanded so screen readers announce the open/closed state.

HTML

<div class="accordion">
  <div class="item open">
    <button class="head" aria-expanded="true">What is an accordion?<span>▾</span></button>
    <div class="panel"><p>A set of stacked headers that each reveal a panel of content when clicked — great for FAQs and long forms where you want to save space.</p></div>
  </div>
  <div class="item">
    <button class="head" aria-expanded="false">When should I use one?<span>▾</span></button>
    <div class="panel"><p>When content is naturally chunked and users only need one piece at a time — settings, FAQs, product details.</p></div>
  </div>
  <div class="item">
    <button class="head" aria-expanded="false">Is it accessible?<span>▾</span></button>
    <div class="panel"><p>Yes — use real <button> headers and toggle aria-expanded so screen readers announce the open/closed state.</p></div>
  </div>
</div>

CSS — animate with max-height

.panel { max-height: 0; overflow: hidden; transition: max-height .25s; }
.item.open .panel { max-height: 220px; }            /* reveal */
.item.open .head span { transform: rotate(180deg); } /* flip chevron */

JavaScript — one open at a time

const heads = document.querySelectorAll('.accordion .head');
const items = document.querySelectorAll('.accordion .item');
heads.forEach(btn => btn.addEventListener('click', () => {
  const item = btn.parentElement;
  const isOpen = item.classList.contains('open');
  items.forEach(i => i.classList.remove('open'));   // close all
  if (!isOpen) item.classList.add('open');           // open the clicked one
  btn.setAttribute('aria-expanded', !isOpen);
}));
Want every panel independently toggleable instead of one-at-a-time? Just drop the “close all” line. For a no-JavaScript version, the native <details><summary> element works too — see the FAQ pattern.
09 · Related

Keep building

See Tabs, Modal, and Microinteractions — or browse the full Component Library.