JavaScript API
13 vanilla JS modules — all IIFE, zero dependencies, each exposing a window.WB* API. Loaded by a single <script> tag.
Module summary
| Module | window.* | Key methods |
|---|---|---|
| theme.js | WBTheme | setMode, setAccent, setPreset, setRadius, setDensity |
| modal.js | WBModal | open(id), close(id) |
| drawer.js | WBDrawer | open(id), close(id) |
| dropdown.js | WBDropdown | open(el), close(el) |
| tabs.js | WBTabs | activate(tabEl) |
| accordion.js | WBAccordion | open(itemEl), close(itemEl) |
| sidebar.js | WBSidebar | open(), close() |
| nav-group.js | WBNavGroup | open(groupEl), close(groupEl), init() |
| toast.js | WBToast | show(msg, opts), dismiss(toastEl) |
| popover.js | WBPopover | open(wrapperEl), close(wrapperEl), closeAll() |
| tooltip.js | WBTooltip | show(el), hide(el), hideAll() |
| dismiss.js | WBDismiss | dismiss(el) |
| command-palette.js | WBCommandPalette | open(id), close(id), register(id, opts) |
| ajax-toggle.js | WBAjaxToggle | handle(checkboxEl) — auto via change event |
WBTheme
theme.jsManages all theme axes — sets data-* attributes on <html> and persists values in localStorage.
// Set dark mode
WBTheme.setMode('dark');
// Switch accent color
WBTheme.setAccent('forest');
// Apply a preset (bundles radius/shadow/density/font/border)
WBTheme.setPreset('rounded');
// Fine-tune individual axes
WBTheme.setRadius('sharp');
WBTheme.setDensity('compact');
Data attribute triggers (no JS needed)
<!-- Cycle: light → dark → auto --> <button data-wb-mode-cycle>Toggle mode</button> <!-- Set a specific mode --> <button data-wb-mode-set="dark">Dark</button> <button data-wb-mode-set="light">Light</button> <button data-wb-mode-set="auto">Auto</button> <!-- Set accent color --> <button data-wb-accent-set="ocean">Ocean</button> <button data-wb-accent-set="forest">Forest</button>
wb-mode, wb-accent, wb-preset, wb-radius, wb-density.
Values are restored automatically on page load. To reset, clear these keys.
WBModal
modal.jsOpens and closes dialog overlays. Handles is-open/is-leaving states, body scroll lock, Escape key, and backdrop click.
WBModal.open('my-modal');
WBModal.close('my-modal');
Data attribute triggers
<!-- Open --> <button data-wb-toggle="modal" data-wb-target="#my-modal">Open</button> <!-- Close (inside the modal) --> <button data-wb-dismiss="modal">Close</button> <div class="wb-modal-backdrop" data-wb-dismiss="modal"></div>
Confirmation dialog (wb-confirm)
<div class="wb-modal wb-confirm" id="confirm-delete">
<div class="wb-modal-backdrop" data-wb-dismiss="modal"></div>
<div class="wb-modal-dialog">
<div class="wb-confirm-icon wb-confirm-danger">!</div>
<h4 class="wb-confirm-title">Delete record?</h4>
<p class="wb-confirm-desc">This action cannot be undone.</p>
<div class="wb-confirm-actions">
<button class="wb-btn wb-btn-ghost" data-wb-dismiss="modal">Cancel</button>
<button class="wb-btn wb-btn-danger" id="confirm-btn">Delete</button>
</div>
</div>
</div>
WBDrawer
drawer.jsSlide-in side panel with full focus trap. Keyboard: Escape closes; Tab cycles within the panel.
WBDrawer.open('my-drawer');
WBDrawer.close('my-drawer');
Data attribute triggers
<button data-wb-toggle="drawer" data-wb-target="#my-drawer">Open</button> <button data-wb-dismiss="drawer">Close</button>
WBDropdown
dropdown.jsToggles is-open on a wb-dropdown wrapper. Closes on outside click and Escape.
var wrapper = document.querySelector('.wb-dropdown');
WBDropdown.open(wrapper);
WBDropdown.close(wrapper);
Data attribute trigger
<div class="wb-dropdown"> <button data-wb-toggle="dropdown">Open menu</button> <div class="wb-dropdown-menu">...</div> </div>
WBTabs
tabs.jsActivates tab items and their corresponding panels.
var tab = document.querySelector('[data-wb-tab="tab2"]');
WBTabs.activate(tab);
HTML structure
<div class="wb-tabs">
<div class="wb-tab-list">
<button class="wb-tab-item is-active" data-wb-tab="panel1">Tab 1</button>
<button class="wb-tab-item" data-wb-tab="panel2">Tab 2</button>
</div>
<div class="wb-tab-panels">
<div class="wb-tab-panel is-active" id="panel1">Content 1</div>
<div class="wb-tab-panel" id="panel2">Content 2</div>
</div>
</div>
WBAccordion
accordion.jsToggles is-open on accordion items. Clicks on wb-accordion-trigger are wired automatically.
var item = document.querySelector('.wb-accordion-item');
WBAccordion.open(item);
WBAccordion.close(item);
WBSidebar
sidebar.jsOpens and closes the mobile sidebar overlay. Adds/removes is-open on .wb-sidebar and .wb-dashboard-shell.
WBSidebar.open(); WBSidebar.close();
Data attribute trigger
<button data-wb-toggle="sidebar">☰ Menu</button>
WBToast
toast.jsProgrammatically shows notification toasts. Auto-creates the container on first call.
Options
| Option | Type | Default | Description |
|---|---|---|---|
| type | string | 'default' | 'default' · 'success' · 'error' · 'warning' · 'info' |
| duration | number | 4000 | Auto-dismiss delay in ms. Set to 0 to disable. |
| position | string | 'top-right' | 'top-right' · 'top-center' · 'top-left' · 'bottom-center' · 'bottom-left' |
| dismissible | boolean | true | Show a close button on the toast |
// Simple
WBToast.show('Settings saved.');
// Typed
WBToast.show('Record deleted!', { type: 'success' });
WBToast.show('Upload failed.', { type: 'error' });
// Custom duration + position
WBToast.show('Processing…', {
type: 'info',
duration: 0, // stays until dismissed
position: 'bottom-center'
});
// Programmatic dismiss
var toast = WBToast.show('Long message');
WBToast.dismiss(toast);
WBPopover
popover.jsToggles is-open on a wb-popover-wrapper. Closes on outside click and Escape.
<div class="wb-popover-wrapper">
<button data-wb-toggle="popover">Click me</button>
<div class="wb-popover wb-popover-top">
<div class="wb-popover-title">Title</div>
<div class="wb-popover-body">Content here.</div>
</div>
</div>
<!-- Placements: wb-popover-top / -right / -bottom / -left -->
WBTooltip
tooltip.jsShows/hides tooltips defined via data-wb-tooltip. Triggers on hover and focus. 300ms show delay by default.
<!-- HTML -->
<button data-wb-tooltip="Click to save"
data-wb-tooltip-placement="top">
Save
</button>
<!-- Placements: top (default) / right / bottom / left -->
<!-- JS -->
var el = document.querySelector('[data-wb-tooltip]');
WBTooltip.show(el);
WBTooltip.hide(el);
WBDismiss
dismiss.jsDismisses alerts and banners with an is-leaving animation before removal.
<!-- HTML -->
<div class="wb-alert wb-alert-info" id="my-alert">
Message text.
<button data-wb-dismiss="alert" class="wb-alert-close">×</button>
</div>
<!-- JS -->
WBDismiss.dismiss(document.getElementById('my-alert'));
The data-wb-dismiss="alert" attribute wires dismissal automatically.
Programmatic use is for cases where you need to dismiss from external code.
WBCommandPalette
command-palette.jsFull-screen command palette overlay with keyboard navigation. Opened via Cmd+K / Ctrl+K.
Options
| Option | Type | Description |
|---|---|---|
| onSearch | function(query, results) | Called on every keystroke. Receives query string and filtered result NodeList. |
// Basic registration (built-in text search)
WBCommandPalette.register('#my-cmd');
// With custom search handler
WBCommandPalette.register('#my-cmd', {
onSearch: function(query, results) {
// results is a NodeList of .wb-cmd-result elements matching query
console.log(query, results.length + ' results');
}
});
// Open/close programmatically
WBCommandPalette.open('#my-cmd');
WBCommandPalette.close('#my-cmd');
Keyboard shortcuts
| Key | Action |
|---|---|
| Cmd/Ctrl+K | Open first registered palette |
| ↑ / ↓ | Navigate results |
| Enter | Activate selected result |
| Escape | Close palette |
WBAjaxToggle
ajax-toggle.jsSends an AJAX POST request when a checkbox changes. Reads CSRF token from the meta[name=csrf-token] tag. Reverts on error and shows a toast.
Data attributes
| Attribute | Required | Description |
|---|---|---|
| data-wb-ajax-toggle | Yes | Marks the checkbox for AJAX toggle behavior |
| data-wb-url | Yes | POST URL for the toggle request |
| data-wb-field | No | JSON field name to send ({ field: checked }). Default: value |
| data-wb-success | No | Toast message shown on success |
| data-wb-error | No | Toast message shown on error (default: 'Error occurred') |
<!-- HTML -->
<input
type="checkbox"
class="wb-switch"
data-wb-ajax-toggle
data-wb-url="/api/users/42/toggle-active"
data-wb-field="is_active"
data-wb-success="Status updated"
data-wb-error="Failed to update status"
checked
>
<!-- Laravel Blade example -->
<input
type="checkbox"
class="wb-switch"
data-wb-ajax-toggle
data-wb-url="{{ route('users.toggle', $user) }}"
data-wb-field="is_active"
data-wb-success="Status updated"
{{ $user->is_active ? 'checked' : '' }}
>
<!-- CSRF meta tag (required in <head>) -->
<meta name="csrf-token" content="{{ csrf_token() }}">
Request format
// POST to data-wb-url
// Headers: Content-Type: application/json, X-CSRF-TOKEN: ...
// Body: { "is_active": true }
// On success: show WBToast(data-wb-success, { type: 'success' })
// On error: revert checkbox state + show WBToast(data-wb-error, { type: 'error' })
JS Conventions
Shared patterns used across all modules.
State classes
| Class | Usage |
|---|---|
| is-open | Element is visible/active (modal, drawer, dropdown, sidebar) |
| is-active | Selected state (tabs, nav links, filter chips) |
| is-selected | Selection state (table rows, list items) |
| is-leaving | Exit animation in progress — added before removing is-open |
is-leaving animation pattern
All close animations follow this sequence to prevent abrupt disappearance:
// 1. Add is-leaving (triggers CSS exit transition)
element.classList.add('is-leaving');
// 2. Wait for transitionend (with 300ms fallback)
element.addEventListener('transitionend', function handler() {
element.classList.remove('is-open', 'is-leaving');
element.removeEventListener('transitionend', handler);
}, { once: true });
// Fallback in case transitionend doesn't fire
setTimeout(function() {
element.classList.remove('is-open', 'is-leaving');
}, 300);
Universal data attribute wiring
<!-- Open any toggleable component -->
<button data-wb-toggle="modal|drawer|dropdown|sidebar|cmd"
data-wb-target="#target-id">Open</button>
<!-- Close from inside -->
<button data-wb-dismiss="modal|drawer|alert">Close</button>
(function(){ 'use strict'; … })() —
and expose their API on window.
No global namespace pollution occurs; each module is fully self-contained.