Bootstrap 5 Admin Panel with Sidebar Navigation
An admin panel shell with a top bar, an offcanvas sidebar built from nav pills, KPI tiles, and an orders table with search, status filter and pagination. Other sections show a tidy empty state.
<section class="wdw-stage ap-stage">
<div class="container px-2 px-lg-3" style="max-width: 1120px">
<div class="wdw-card overflow-hidden" id="ap">
<header class="ap-top d-flex align-items-center gap-3 px-3 px-lg-4">
<button class="btn btn-outline-secondary btn-sm d-lg-none" type="button" data-bs-toggle="offcanvas" data-bs-target="#apNav" aria-controls="apNav" aria-label="Open navigation"><i class="bi bi-list fs-5"></i></button>
<div class="d-flex align-items-center gap-2"><span class="wdw-icon ap-logo"><i class="bi bi-shield-check"></i></span><strong class="wdw-heading fs-6">Admin Panel</strong></div>
<div class="ms-auto d-flex align-items-center gap-3">
<span class="small text-body-secondary d-none d-sm-inline">George User</span>
<img src="https://i.pravatar.cc/80?img=12" alt="George User" width="36" height="36" class="rounded-circle">
<button class="btn btn-sm btn-outline-secondary" type="button" aria-label="Log out"><i class="bi bi-box-arrow-right"></i></button>
</div>
</header>
<div class="d-lg-flex">
<aside class="ap-side">
<div class="offcanvas-lg offcanvas-start" tabindex="-1" id="apNav" aria-labelledby="apNavLabel">
<div class="offcanvas-header d-lg-none">
<h2 class="offcanvas-title h6 mb-0" id="apNavLabel">Navigation</h2>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#apNav" aria-label="Close"></button>
</div>
<div class="offcanvas-body flex-column">
<nav aria-label="Main">
<ul class="nav nav-pills flex-column gap-1" id="apTabs">
<li class="nav-item"><button type="button" class="nav-link d-flex align-items-center gap-3 w-100 text-start" data-ap-tab="Dashboard"><i class="bi bi-grid-1x2"></i><span>Dashboard</span></button></li>
<li class="nav-item"><button type="button" class="nav-link d-flex align-items-center gap-3 w-100 text-start active" data-ap-tab="Orders" aria-current="page"><i class="bi bi-archive"></i><span>Orders</span></button></li>
<li class="nav-item"><button type="button" class="nav-link d-flex align-items-center gap-3 w-100 text-start" data-ap-tab="Users"><i class="bi bi-people"></i><span>Users</span></button></li>
<li class="nav-item"><button type="button" class="nav-link d-flex align-items-center gap-3 w-100 text-start" data-ap-tab="Content"><i class="bi bi-file-text"></i><span>Content</span></button></li>
<li class="nav-item"><button type="button" class="nav-link d-flex align-items-center gap-3 w-100 text-start" data-ap-tab="Inbox"><i class="bi bi-envelope"></i><span>Inbox</span><span class="badge rounded-pill text-bg-primary ms-auto">13</span></button></li>
<li class="nav-item"><button type="button" class="nav-link d-flex align-items-center gap-3 w-100 text-start" data-ap-tab="Statistics"><i class="bi bi-graph-up"></i><span>Statistics</span></button></li>
<li class="nav-item"><button type="button" class="nav-link d-flex align-items-center gap-3 w-100 text-start" data-ap-tab="Settings"><i class="bi bi-gear"></i><span>Settings</span></button></li>
</ul>
</nav>
</div>
</div>
</aside>
<main class="ap-main flex-grow-1 p-3 p-lg-4">
<h1 class="wdw-heading h4 mb-3" id="apTitle">Orders</h1>
<div id="apOrders">
<div class="row g-3 mb-3">
<div class="col-4"><div class="ap-kpi"><div class="ap-kpi-label">Open orders</div><div class="ap-kpi-value">24</div></div></div>
<div class="col-4"><div class="ap-kpi"><div class="ap-kpi-label">Revenue</div><div class="ap-kpi-value">$12.4k</div></div></div>
<div class="col-4"><div class="ap-kpi"><div class="ap-kpi-label">Shipped</div><div class="ap-kpi-value">86%</div></div></div>
</div>
<ul class="nav nav-pills gap-2 mb-3" id="apSub">
<li class="nav-item"><button class="nav-link active" type="button" data-ap-sub="list">List</button></li>
<li class="nav-item"><button class="nav-link" type="button" data-ap-sub="new">New order</button></li>
</ul>
<div id="apList">
<div class="d-flex flex-wrap gap-2 mb-2">
<div class="flex-grow-1 position-relative">
<label class="visually-hidden" for="apSearch">Search orders</label>
<i class="bi bi-search ap-search-icon"></i>
<input id="apSearch" type="search" class="form-control ps-5" placeholder="Search orders">
</div>
<label class="visually-hidden" for="apStatus">Filter by status</label>
<select id="apStatus" class="form-select w-auto"><option value="">All statuses</option><option value="shipped">Shipped</option><option value="processing">Processing</option><option value="cancelled">Cancelled</option></select>
</div>
<div class="table-responsive">
<table class="table align-middle mb-0">
<thead><tr class="small text-body-secondary text-uppercase"><th class="d-none d-md-table-cell">Order</th><th>Customer</th><th class="d-none d-md-table-cell">City</th><th class="d-none d-md-table-cell">Status</th><th class="text-end">Total</th></tr></thead>
<tbody id="apRows">
<tr data-ap-row="marta keller berlin shipped 4821" data-status="shipped"><td class="fw-semibold d-none d-md-table-cell">#4821</td><td><div class="fw-semibold">Marta Keller</div><div class="d-md-none small text-body-secondary mt-1">#4821 · <span class="badge rounded-pill text-success-emphasis bg-success-subtle">Shipped</span></div></td><td class="d-none d-md-table-cell text-body-secondary">Berlin</td><td class="d-none d-md-table-cell"><span class="badge rounded-pill text-success-emphasis bg-success-subtle">Shipped</span></td><td class="text-end fw-semibold">$240.00</td></tr>
<tr data-ap-row="jonas weber hamburg processing 4820" data-status="processing"><td class="fw-semibold d-none d-md-table-cell">#4820</td><td><div class="fw-semibold">Jonas Weber</div><div class="d-md-none small text-body-secondary mt-1">#4820 · <span class="badge rounded-pill text-warning-emphasis bg-warning-subtle">Processing</span></div></td><td class="d-none d-md-table-cell text-body-secondary">Hamburg</td><td class="d-none d-md-table-cell"><span class="badge rounded-pill text-warning-emphasis bg-warning-subtle">Processing</span></td><td class="text-end fw-semibold">$89.50</td></tr>
<tr data-ap-row="lena fischer munich shipped 4819" data-status="shipped"><td class="fw-semibold d-none d-md-table-cell">#4819</td><td><div class="fw-semibold">Lena Fischer</div><div class="d-md-none small text-body-secondary mt-1">#4819 · <span class="badge rounded-pill text-success-emphasis bg-success-subtle">Shipped</span></div></td><td class="d-none d-md-table-cell text-body-secondary">Munich</td><td class="d-none d-md-table-cell"><span class="badge rounded-pill text-success-emphasis bg-success-subtle">Shipped</span></td><td class="text-end fw-semibold">$412.00</td></tr>
<tr data-ap-row="tom becker cologne cancelled 4818" data-status="cancelled"><td class="fw-semibold d-none d-md-table-cell">#4818</td><td><div class="fw-semibold">Tom Becker</div><div class="d-md-none small text-body-secondary mt-1">#4818 · <span class="badge rounded-pill text-danger-emphasis bg-danger-subtle">Cancelled</span></div></td><td class="d-none d-md-table-cell text-body-secondary">Cologne</td><td class="d-none d-md-table-cell"><span class="badge rounded-pill text-danger-emphasis bg-danger-subtle">Cancelled</span></td><td class="text-end fw-semibold">$56.00</td></tr>
<tr data-ap-row="ana silva lisbon processing 4817" data-status="processing"><td class="fw-semibold d-none d-md-table-cell">#4817</td><td><div class="fw-semibold">Ana Silva</div><div class="d-md-none small text-body-secondary mt-1">#4817 · <span class="badge rounded-pill text-warning-emphasis bg-warning-subtle">Processing</span></div></td><td class="d-none d-md-table-cell text-body-secondary">Lisbon</td><td class="d-none d-md-table-cell"><span class="badge rounded-pill text-warning-emphasis bg-warning-subtle">Processing</span></td><td class="text-end fw-semibold">$178.25</td></tr>
</tbody>
</table>
</div>
<div class="d-flex flex-wrap align-items-center justify-content-between gap-2 pt-3">
<span class="small text-body-secondary" id="apCount" aria-live="polite">Showing 5 of 24 orders</span>
<nav aria-label="Orders pages"><ul class="pagination pagination-sm mb-0"><li class="page-item disabled"><span class="page-link">Prev</span></li><li class="page-item active"><span class="page-link" aria-current="page">1</span></li><li class="page-item"><a class="page-link" href="#">2</a></li><li class="page-item"><a class="page-link" href="#">Next</a></li></ul></nav>
</div>
</div>
<form id="apNew" class="row g-3" hidden>
<div class="col-sm-6"><label class="form-label small fw-semibold" for="apCust">Customer</label><input id="apCust" class="form-control" placeholder="Marta Keller"></div>
<div class="col-sm-6"><label class="form-label small fw-semibold" for="apCity">City</label><input id="apCity" class="form-control" placeholder="Berlin"></div>
<div class="col-12"><label class="form-label small fw-semibold" for="apNote">Notes</label><textarea id="apNote" class="form-control" rows="3" placeholder="Delivery instructions"></textarea></div>
<div class="col-12"><button type="submit" class="btn btn-primary rounded-pill px-4">Create order</button></div>
</form>
</div>
<div id="apEmpty" class="text-center py-5" hidden>
<span class="wdw-icon mb-3"><i class="bi bi-inbox"></i></span>
<h2 class="h6 mb-1">Nothing here yet</h2>
<p class="small text-body-secondary mb-0">The <span id="apEmptyName">Dashboard</span> section has no content to show.</p>
</div>
</main>
</div>
</div>
</div>
</section>
@import url("https://cdnjs.cloudflare.com/ajax/libs/bootstrap-icons/1.11.3/font/bootstrap-icons.min.css");
/* Web Designer Wall library style — shared base for every Bootstrap 5 snippet.
Indigo accent, Plus Jakarta Sans, soft cards, pill buttons. Works in light and dark. */
@import url("https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap");
:root,
[data-bs-theme="light"] {
--wdw-font: "Plus Jakarta Sans", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
--bs-body-font-family: var(--wdw-font);
--bs-primary: #4f46e5;
--bs-primary-rgb: 79, 70, 229;
--bs-primary-text-emphasis: #3730a3;
--bs-primary-bg-subtle: #eef2ff;
--bs-primary-border-subtle: #c7d2fe;
--bs-link-color: #4f46e5;
--bs-link-color-rgb: 79, 70, 229;
--bs-link-hover-color: #4338ca;
--bs-link-hover-color-rgb: 67, 56, 202;
--bs-focus-ring-color: rgba(79, 70, 229, 0.25);
--bs-border-radius: 0.75rem;
--bs-border-radius-lg: 1rem;
--bs-border-radius-xl: 1.25rem;
--wdw-radius-card: 1.25rem;
--wdw-shadow: 0 1px 2px rgba(16, 24, 40, 0.05), 0 16px 40px -16px rgba(16, 24, 40, 0.22);
--wdw-shadow-sm: 0 1px 2px rgba(16, 24, 40, 0.06), 0 4px 12px -4px rgba(16, 24, 40, 0.12);
--wdw-stage: #f5f6fb;
}
[data-bs-theme="dark"] {
--bs-primary: #818cf8;
--bs-primary-rgb: 129, 140, 248;
--bs-primary-text-emphasis: #c7d2fe;
--bs-primary-bg-subtle: rgba(129, 140, 248, 0.14);
--bs-primary-border-subtle: rgba(129, 140, 248, 0.35);
--bs-link-color: #a5b4fc;
--bs-link-color-rgb: 165, 180, 252;
--bs-link-hover-color: #c7d2fe;
--bs-link-hover-color-rgb: 199, 210, 254;
--bs-focus-ring-color: rgba(129, 140, 248, 0.35);
--wdw-shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 16px 40px -16px rgba(0, 0, 0, 0.7);
--wdw-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4), 0 4px 12px -4px rgba(0, 0, 0, 0.5);
--wdw-stage: #0f1117;
}
body { font-family: var(--wdw-font); -webkit-font-smoothing: antialiased; }
/* Bootstrap compiles its blue into these components; point them at the accent. */
.btn-primary {
--bs-btn-bg: var(--bs-primary); --bs-btn-border-color: var(--bs-primary);
--bs-btn-hover-bg: #4338ca; --bs-btn-hover-border-color: #4338ca;
--bs-btn-active-bg: #3730a3; --bs-btn-active-border-color: #3730a3;
--bs-btn-disabled-bg: var(--bs-primary); --bs-btn-disabled-border-color: var(--bs-primary);
--bs-btn-focus-shadow-rgb: var(--bs-primary-rgb);
}
[data-bs-theme="dark"] .btn-primary { --bs-btn-color: #0f1117; --bs-btn-hover-bg: #a5b4fc; --bs-btn-hover-border-color: #a5b4fc; --bs-btn-hover-color: #0f1117; --bs-btn-active-color: #0f1117; }
.btn-outline-primary {
--bs-btn-color: var(--bs-primary); --bs-btn-border-color: var(--bs-primary);
--bs-btn-hover-bg: var(--bs-primary); --bs-btn-hover-border-color: var(--bs-primary);
--bs-btn-active-bg: var(--bs-primary); --bs-btn-active-border-color: var(--bs-primary);
--bs-btn-focus-shadow-rgb: var(--bs-primary-rgb);
}
.btn { --bs-btn-font-weight: 600; min-height: 44px; display: inline-flex; align-items: center; justify-content: center; gap: .5rem; }
.btn-sm { min-height: 36px; }
.form-control, .form-select { min-height: 44px; border-radius: .75rem; }
.form-control:focus, .form-select:focus, .form-check-input:focus { border-color: rgba(var(--bs-primary-rgb), .6); box-shadow: 0 0 0 .25rem var(--bs-focus-ring-color); }
.form-check-input:checked { background-color: var(--bs-primary); border-color: var(--bs-primary); }
.nav-pills { --bs-nav-pills-link-active-bg: var(--bs-primary); }
.nav { --bs-nav-link-color: var(--bs-secondary-color); --bs-nav-link-hover-color: var(--bs-emphasis-color); }
.pagination { --bs-pagination-active-bg: var(--bs-primary); --bs-pagination-active-border-color: var(--bs-primary); --bs-pagination-color: var(--bs-body-color); --bs-pagination-focus-box-shadow: 0 0 0 .25rem var(--bs-focus-ring-color); }
.progress, .progress-stacked { --bs-progress-bar-bg: var(--bs-primary); }
.list-group { --bs-list-group-active-bg: var(--bs-primary); --bs-list-group-active-border-color: var(--bs-primary); }
.dropdown-menu { --bs-dropdown-link-active-bg: var(--bs-primary); border-radius: .875rem; box-shadow: var(--wdw-shadow-sm); }
.accordion { --bs-accordion-active-bg: var(--bs-primary-bg-subtle); --bs-accordion-active-color: var(--bs-primary-text-emphasis); --bs-accordion-btn-focus-box-shadow: 0 0 0 .25rem var(--bs-focus-ring-color); }
.badge { font-weight: 600; }
/* Library building blocks — use these instead of re-inventing them per snippet. */
.wdw-stage { min-height: 100vh; display: flex; align-items: center; background: var(--wdw-stage); padding-block: 3rem; }
.wdw-card { background: var(--bs-body-bg); border: 1px solid var(--bs-border-color-translucent); border-radius: var(--wdw-radius-card); box-shadow: var(--wdw-shadow); }
.wdw-card-sm { background: var(--bs-body-bg); border: 1px solid var(--bs-border-color-translucent); border-radius: 1rem; box-shadow: var(--wdw-shadow-sm); }
.wdw-aurora { position: relative; color: #fff; background: radial-gradient(60% 55% at 100% 0, #818cf8 0, transparent 70%), radial-gradient(65% 60% at 0 100%, #ec4899 0, transparent 65%), #1e1b4b; }
.wdw-eyebrow { display: inline-block; font-size: .75rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--bs-primary); }
.wdw-heading { font-weight: 800; letter-spacing: -.02em; color: var(--bs-emphasis-color); }
.wdw-muted { color: var(--bs-secondary-color); }
.wdw-icon { width: 2.75rem; height: 2.75rem; flex: none; display: inline-flex; align-items: center; justify-content: center; border-radius: .875rem; background: var(--bs-primary-bg-subtle); color: var(--bs-primary); }
/* Component */
.ap-stage { padding-block: 1.25rem; }
.ap-top { height: 60px; border-bottom: 1px solid var(--bs-border-color-translucent); }
.ap-logo { width: 2.1rem; height: 2.1rem; border-radius: .6rem; }
@media (min-width: 992px) {
.ap-side { width: 230px; flex: none; padding: 1rem; background: var(--bs-tertiary-bg); border-right: 1px solid var(--bs-border-color-translucent); }
.ap-main { min-height: 520px; }
}
#apTabs .nav-link { min-height: 40px; font-size: .925rem; font-weight: 500; --bs-nav-link-padding-y: .35rem; }
#apTabs .nav-link i { font-size: 1.05rem; color: var(--bs-secondary-color); }
#apTabs .nav-link.active { background: var(--bs-primary-bg-subtle); color: var(--bs-primary-text-emphasis); font-weight: 600; }
#apTabs .nav-link.active i { color: var(--bs-primary); }
.ap-kpi { border: 1px solid var(--bs-border-color-translucent); border-radius: .875rem; padding: .75rem 1rem; background: var(--bs-tertiary-bg); }
.ap-kpi-label { font-size: .7rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--bs-secondary-color); }
.ap-kpi-value { font-size: 1.4rem; font-weight: 800; letter-spacing: -.02em; color: var(--bs-emphasis-color); line-height: 1.3; }
.ap-search-icon { position: absolute; left: 1rem; top: 50%; transform: translateY(-50%); color: var(--bs-secondary-color); pointer-events: none; }
.table { --bs-table-bg: transparent; }
.table th { font-weight: 700; letter-spacing: .05em; font-size: .7rem; }
.table > :not(caption) > * > * { padding-block: .55rem; }
.ap-main #apSub .nav-link { border-radius: 999px; min-height: 40px; display: inline-flex; align-items: center; padding-inline: 1.1rem; font-weight: 600; font-size: .9rem; }
@media (max-width: 575.98px) { .ap-kpi { padding: .6rem .7rem; } .ap-kpi-value { font-size: 1.15rem; } }
(function () {
var root = document.getElementById('ap');
if (!root) return;
function initNav() {
var tabs = root.querySelectorAll('#apTabs [data-ap-tab]');
var orders = document.getElementById('apOrders');
var empty = document.getElementById('apEmpty');
tabs.forEach(function (btn) {
btn.addEventListener('click', function () {
tabs.forEach(function (b) {
var on = b === btn;
b.classList.toggle('active', on);
if (on) b.setAttribute('aria-current', 'page'); else b.removeAttribute('aria-current');
});
var name = btn.dataset.apTab;
document.getElementById('apTitle').textContent = name;
var isOrders = name === 'Orders';
orders.hidden = !isOrders;
empty.hidden = isOrders;
document.getElementById('apEmptyName').textContent = name;
var nav = document.getElementById('apNav');
if (nav.classList.contains('show')) bootstrap.Offcanvas.getOrCreateInstance(nav).hide();
});
});
}
function initSubTabs() {
var list = document.getElementById('apList');
var form = document.getElementById('apNew');
var tabs = root.querySelectorAll('[data-ap-sub]');
tabs.forEach(function (btn) {
btn.addEventListener('click', function () {
tabs.forEach(function (b) { b.classList.toggle('active', b === btn); });
var isNew = btn.dataset.apSub === 'new';
list.hidden = isNew;
form.hidden = !isNew;
});
});
form.addEventListener('submit', function (e) { e.preventDefault(); form.reset(); tabs[0].click(); });
}
function initFilter() {
var search = document.getElementById('apSearch');
var status = document.getElementById('apStatus');
var rows = document.querySelectorAll('#apRows tr');
var count = document.getElementById('apCount');
function apply() {
var q = search.value.trim().toLowerCase();
var n = 0;
rows.forEach(function (r) {
var show = (!q || r.dataset.apRow.indexOf(q) !== -1) && (!status.value || r.dataset.status === status.value);
r.hidden = !show;
if (show) n++;
});
count.textContent = 'Showing ' + n + ' of 24 orders';
}
search.addEventListener('input', apply);
status.addEventListener('change', apply);
}
initNav();
initSubTabs();
initFilter();
})();
Built on Bootstrap 5.3.8 with plain JavaScript — no jQuery. Supports light and dark mode via data-bs-theme.