A curated collection of framework-free UI patterns built with HTML, CSS, and Vanilla JavaScript.
No build step. No dependencies. No magic.
Just clean, readable UI primitives you can drop into any project.
I use these to power most of my mini apps: https://www.yuzool.com
(code demos included in each folder)
Modern frameworks abstract away simple UI behavior. That’s convenient—but it also hides fundamentals.
This repository is a reference library for:
- common UI interactions
- accessibility-aware patterns
- copy‑pasteable components
- understanding how things actually work
Each pattern is:
- self‑contained
- readable in under 5 minutes
- usable without modification
| Pattern | Description |
|---|---|
| Accordion | Expand / collapse sections (accessible) |
| Modal | Keyboard & focus‑safe modal dialog |
| Tabs | Simple tab navigation |
| Toast | Lightweight notification system |
| Dropdown | Click‑outside aware menu |
| Copy Button | Copy text to clipboard with feedback |
vanilla-js-ui-patterns/
├── accordion/
│ ├── index.html
│ ├── style.css
│ └── script.js
├── modal/
├── tabs/
├── toast/
├── dropdown/
├── copy-button/
└── README.mdEach folder works by opening index.html directly.
- Keyboard accessible
- Smooth height animation
- No hard‑coded heights
document.querySelectorAll(".accordion-header").forEach(btn => {
btn.addEventListener("click", () => {
const item = btn.parentElement;
item.classList.toggle("open");
});
});- Focus trap
- ESC to close
- Click‑outside to close
const openBtn = document.getElementById("open-modal");
const modal = document.getElementById("modal");
openBtn.onclick = () => modal.classList.add("open");
modal.onclick = e => {
if (e.target === modal) modal.classList.remove("open");
};
document.addEventListener("keydown", e => {
if (e.key === "Escape") modal.classList.remove("open");
});document.querySelectorAll(".tab").forEach(tab => {
tab.onclick = () => {
document.querySelectorAll(".tab, .panel").forEach(el => el.classList.remove("active"));
tab.classList.add("active");
document.getElementById(tab.dataset.panel).classList.add("active");
};
});function toast(message, duration = 2000) {
const el = document.createElement("div");
el.className = "toast";
el.textContent = message;
document.body.appendChild(el);
setTimeout(() => el.remove(), duration);
}const toggle = document.querySelector(".dropdown-toggle");
const menu = document.querySelector(".dropdown-menu");
toggle.onclick = e => {
e.stopPropagation();
menu.classList.toggle("open");
};
document.onclick = () => menu.classList.remove("open");document.querySelectorAll("[data-copy]").forEach(btn => {
btn.onclick = () => {
navigator.clipboard.writeText(btn.dataset.copy);
btn.textContent = "Copied!";
};
});- No frameworks
- Minimal JS
- Progressive enhancement
- Accessibility where it matters
- Readability > cleverness
- You want to understand the pattern
- You don’t want to install a library
- You’re building small tools or prototypes
- You want full control
MIT
Built as a practical reference.
More tools → https://www.yuzool.com

