Bootstrap 5 Admin Dashboard with Sidebar and Data Table
A document manager dashboard with an icon rail, filter sidebar, content tabs and a selectable table with bulk archive.
<section class="wdw-stage p-0">
<div class="container-fluid px-3 px-lg-4 py-4" style="max-width:1200px">
<div class="wdw-dash wdw-card overflow-hidden d-flex">
<nav class="wdw-rail d-none d-sm-flex flex-column align-items-center gap-2 py-3" aria-label="Main"><a href="#" class="wdw-rail-link" aria-label="Search"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="M20 20l-3.5-3.5"/></svg></a><a href="#" class="wdw-rail-link" aria-label="Files"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/></svg></a><a href="#" class="wdw-rail-link is-active" aria-label="Documents" aria-current="page"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8z"/><path d="M14 3v5h5M9 13h6M9 17h6"/></svg></a><a href="#" class="wdw-rail-link" aria-label="Analytics"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 20V10M10 20V4M16 20v-7M22 20H2"/></svg></a><a href="#" class="wdw-rail-link" aria-label="Settings"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6h10M18 6h2M4 12h2M10 12h10M4 18h12"/><circle cx="16" cy="6" r="2"/><circle cx="8" cy="12" r="2"/><circle cx="18" cy="18" r="2"/></svg></a></nav>
<div class="flex-grow-1 min-w-0 d-flex flex-column flex-lg-row">
<aside class="wdw-side p-3 p-lg-4">
<h1 class="h4 wdw-heading mb-3 mb-lg-4">Docs</h1>
<div class="nav nav-pills flex-lg-column gap-1" id="doc-filter" aria-label="Filter documents">
<button class="nav-link active text-start" data-filter="all" type="button">All</button>
<button class="nav-link text-start" data-filter="mine" type="button">Mine</button>
<button class="nav-link text-start" data-filter="shared" type="button">Shared</button>
<button class="nav-link text-start" data-filter="drafts" type="button">Drafts</button>
</div>
</aside>
<main class="flex-grow-1 min-w-0 p-3 p-lg-4 bg-body-tertiary">
<ul class="nav nav-underline mb-3 flex-nowrap overflow-auto" aria-label="Document types">
<li class="nav-item"><a class="nav-link active" href="#" aria-current="page">Reports</a></li>
<li class="nav-item"><a class="nav-link" href="#">Presentations</a></li>
<li class="nav-item"><a class="nav-link" href="#">Pinboards</a></li>
<li class="nav-item"><a class="nav-link" href="#">Tickets</a></li>
<li class="nav-item"><a class="nav-link" href="#">Forms</a></li>
</ul>
<div class="bg-body rounded-4 border overflow-hidden">
<div class="d-flex align-items-center justify-content-between px-3 py-2 border-bottom" style="min-height:52px">
<span class="small text-body-secondary" id="doc-count" role="status">0 selected</span>
<button type="button" class="btn btn-sm btn-primary rounded-pill px-3" id="doc-archive" disabled>Archive</button>
</div>
<div class="table-responsive">
<table class="table table-hover align-middle mb-0" id="doc-table">
<thead><tr class="small text-uppercase text-body-secondary"><th class="ps-3" style="width:48px"><input class="form-check-input" type="checkbox" id="doc-all" aria-label="Select all"></th><th>Name</th><th class="d-none d-md-table-cell">Description</th><th class="text-end pe-3">Created</th></tr></thead>
<tbody><tr data-cat="mine"><td class="ps-3"><input class="form-check-input row-check" type="checkbox" aria-label="Select Last quarter revenue"></td><td class="fw-semibold">Last quarter revenue</td><td class="text-body-secondary d-none d-md-table-cell text-truncate" style="max-width:260px">Revenue for last quarter in state America for year 2025</td><td class="text-body-secondary text-end pe-3 text-nowrap small">6 minutes ago</td></tr><tr data-cat="mine"><td class="ps-3"><input class="form-check-input row-check" type="checkbox" aria-label="Select Expenses in 2025"></td><td class="fw-semibold">Expenses in 2025</td><td class="text-body-secondary d-none d-md-table-cell text-truncate" style="max-width:260px">Annual expense breakdown by department</td><td class="text-body-secondary text-end pe-3 text-nowrap small">2 hours ago</td></tr><tr data-cat="shared"><td class="ps-3"><input class="form-check-input row-check" type="checkbox" aria-label="Select Accounting"></td><td class="fw-semibold">Accounting</td><td class="text-body-secondary d-none d-md-table-cell text-truncate" style="max-width:260px">Ledger exports and reconciliations</td><td class="text-body-secondary text-end pe-3 text-nowrap small">5 hours ago</td></tr><tr data-cat="shared"><td class="ps-3"><input class="form-check-input row-check" type="checkbox" aria-label="Select Starbucks orders"></td><td class="fw-semibold">Starbucks orders</td><td class="text-body-secondary d-none d-md-table-cell text-truncate" style="max-width:260px">Supplier orders for the Riga office</td><td class="text-body-secondary text-end pe-3 text-nowrap small">12 hours ago</td></tr><tr data-cat="shared"><td class="ps-3"><input class="form-check-input row-check" type="checkbox" aria-label="Select East coast customers"></td><td class="fw-semibold">East coast customers</td><td class="text-body-secondary d-none d-md-table-cell text-truncate" style="max-width:260px">Customer list and renewal dates</td><td class="text-body-secondary text-end pe-3 text-nowrap small">Yesterday</td></tr><tr data-cat="drafts"><td class="ps-3"><input class="form-check-input row-check" type="checkbox" aria-label="Select Last quarter expenses"></td><td class="fw-semibold">Last quarter expenses</td><td class="text-body-secondary d-none d-md-table-cell text-truncate" style="max-width:260px">Travel, software and contractor spend</td><td class="text-body-secondary text-end pe-3 text-nowrap small">2 days ago</td></tr><tr data-cat="drafts"><td class="ps-3"><input class="form-check-input row-check" type="checkbox" aria-label="Select HR worksheet"></td><td class="fw-semibold">HR worksheet</td><td class="text-body-secondary d-none d-md-table-cell text-truncate" style="max-width:260px">Headcount plan and onboarding checklist</td><td class="text-body-secondary text-end pe-3 text-nowrap small">2 days ago</td></tr><tr data-cat="mine"><td class="ps-3"><input class="form-check-input row-check" type="checkbox" aria-label="Select Salaries last quarter"></td><td class="fw-semibold">Salaries last quarter</td><td class="text-body-secondary d-none d-md-table-cell text-truncate" style="max-width:260px">Payroll summary, restricted access</td><td class="text-body-secondary text-end pe-3 text-nowrap small">1 week ago</td></tr></tbody>
</table>
</div>
</div>
</main>
</div>
</div>
</div>
</section>
/* 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 */
.wdw-dash { min-height: 560px; }
.min-w-0 { min-width: 0; }
.wdw-rail { width: 64px; flex: none; background: #1e1b4b; }
.wdw-rail-link { width: 44px; height: 44px; display: inline-flex; align-items: center; justify-content: center; border-radius: .875rem; color: rgba(255,255,255,.65); text-decoration: none; transition: background .15s, color .15s; }
.wdw-rail-link:hover { color: #fff; background: rgba(255,255,255,.1); }
.wdw-rail-link.is-active { color: #fff; background: var(--bs-primary); }
.wdw-rail-link:focus-visible { outline: 2px solid #c7d2fe; outline-offset: 2px; }
@media (min-width: 992px) { .wdw-side { width: 200px; flex: none; border-right: 1px solid var(--bs-border-color); } }
@media (max-width: 991.98px) { .wdw-side { border-bottom: 1px solid var(--bs-border-color); } #doc-filter { flex-wrap: wrap; } }
.wdw-dash .nav-underline { --bs-nav-underline-link-active-color: var(--bs-primary); --bs-nav-underline-border-width: .15rem; }
.wdw-dash .nav-underline .nav-link.active { border-bottom-color: var(--bs-primary); }
.wdw-dash tbody tr.is-selected > * { background: var(--bs-primary-bg-subtle); }
.wdw-dash .table > :not(caption) > * > * { padding-block: .8rem; }
.wdw-dash .form-check-input { width: 1.1rem; height: 1.1rem; }
(function () {
function initDocTable() {
var table = document.getElementById('doc-table');
if (!table) return;
var all = document.getElementById('doc-all');
var count = document.getElementById('doc-count');
var archive = document.getElementById('doc-archive');
var rows = Array.prototype.slice.call(table.querySelectorAll('tbody tr'));
function visible() { return rows.filter(function (r) { return !r.hidden; }); }
function update() {
var vis = visible();
var checked = vis.filter(function (r) { return r.querySelector('.row-check').checked; });
rows.forEach(function (r) { r.classList.toggle('is-selected', r.querySelector('.row-check').checked); });
count.textContent = checked.length + ' selected';
archive.disabled = checked.length === 0;
all.checked = vis.length > 0 && checked.length === vis.length;
all.indeterminate = checked.length > 0 && checked.length < vis.length;
}
all.addEventListener('change', function () {
visible().forEach(function (r) { r.querySelector('.row-check').checked = all.checked; });
update();
});
table.addEventListener('change', function (e) { if (e.target.classList.contains('row-check')) update(); });
archive.addEventListener('click', function () {
rows.forEach(function (r) { if (r.querySelector('.row-check').checked) r.remove(); });
rows = rows.filter(function (r) { return r.isConnected; });
update();
});
var filter = document.getElementById('doc-filter');
if (filter) filter.addEventListener('click', function (e) {
var btn = e.target.closest('[data-filter]');
if (!btn) return;
filter.querySelectorAll('.nav-link').forEach(function (b) { b.classList.toggle('active', b === btn); });
var f = btn.dataset.filter;
rows.forEach(function (r) { r.hidden = f !== 'all' && r.dataset.cat !== f; });
update();
});
update();
}
initDocTable();
})();
<section class="app-stage">
<div class="app" id="docsApp">
<nav class="rail" aria-label="Primary">
<a href="#" class="rail-link" aria-label="Search"><svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="m20 20-3.500-3.500"/></svg></a>
<a href="#" class="rail-link" aria-label="Files"><svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/></svg></a>
<a href="#" class="rail-link active" aria-label="Documents" aria-current="page"><svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8z"/><path d="M14 3v5h5M9 13h6M9 17h6"/></svg></a>
<a href="#" class="rail-link" aria-label="Analytics"><svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 20V10M10 20V4M16 20v-7M22 20H2"/></svg></a>
<a href="#" class="rail-link" aria-label="Settings"><svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 6h10M18 6h2M4 12h4M12 12h8M4 18h12M20 18h0"/><circle cx="16" cy="6" r="2"/><circle cx="10" cy="12" r="2"/><circle cx="18" cy="18" r="2"/></svg></a>
</nav>
<nav class="dc-side" aria-label="Documents filter">
<h2 class="fs-xl fw-light mb-0">Docs</h2>
<div class="dc-side-links"><a href="#" class="dc-side-link active" aria-current="page">All</a><a href="#" class="dc-side-link">Mine</a><a href="#" class="dc-side-link">Shared</a><a href="#" class="dc-side-link">Drafts</a></div>
</nav>
<main class="dc-main">
<div class="dc-tabs" role="tablist" aria-label="Document types"><button type="button" role="tab" class="dc-tab active" id="dc-tab-reports" data-type="reports" aria-selected="true" aria-controls="dcPanel" tabindex="0">Reports</button><button type="button" role="tab" class="dc-tab" id="dc-tab-presentations" data-type="presentations" aria-selected="false" aria-controls="dcPanel" tabindex="-1">Presentations</button><button type="button" role="tab" class="dc-tab" id="dc-tab-pinboards" data-type="pinboards" aria-selected="false" aria-controls="dcPanel" tabindex="-1">Pinboards</button><button type="button" role="tab" class="dc-tab" id="dc-tab-tickets" data-type="tickets" aria-selected="false" aria-controls="dcPanel" tabindex="-1">Tickets</button><button type="button" role="tab" class="dc-tab" id="dc-tab-forms" data-type="forms" aria-selected="false" aria-controls="dcPanel" tabindex="-1">Forms</button><button type="button" role="tab" class="dc-tab" id="dc-tab-others" data-type="others" aria-selected="false" aria-controls="dcPanel" tabindex="-1">Others</button></div>
<div class="dc-panel" id="dcPanel" role="tabpanel" aria-labelledby="dc-tab-reports">
<div class="dc-bar fs-sm fg-secondary" aria-live="polite"><span data-selected-count>1 selected</span></div>
<div class="table-responsive">
<table class="table dc-table mb-0">
<thead>
<tr>
<th scope="col" class="dc-check"><input type="checkbox" class="check" aria-label="Select all documents" data-select-all></th>
<th scope="col">Name</th>
<th scope="col" class="dc-desc">Description</th>
<th scope="col" class="dc-date">Date created</th>
</tr>
</thead>
<tbody>
<tr data-type="reports">
<td class="dc-check"><input type="checkbox" class="check" aria-label="Select Last quarter revenue"></td>
<th scope="row" class="dc-name">Last quarter revenue</th>
<td class="dc-desc">Revenue for last quarter in state America for year 2013, with regional breakdown</td>
<td class="dc-date"><time>6 minutes ago</time> <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></svg></td>
</tr>
<tr data-type="reports">
<td class="dc-check"><input type="checkbox" class="check" aria-label="Select Expenses in 2013" checked></td>
<th scope="row" class="dc-name">Expenses in 2013</th>
<td class="dc-desc"><span class="fg-3">—</span></td>
<td class="dc-date"><time>2 hours ago</time> <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></svg></td>
</tr>
<tr data-type="reports">
<td class="dc-check"><input type="checkbox" class="check" aria-label="Select Accounting"></td>
<th scope="row" class="dc-name">Accounting</th>
<td class="dc-desc">Ledger summary and reconciliation notes for the finance team.</td>
<td class="dc-date"><time>5 hours ago</time> <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></svg></td>
</tr>
<tr data-type="forms">
<td class="dc-check"><input type="checkbox" class="check" aria-label="Select Starbucks orders"></td>
<th scope="row" class="dc-name">Starbucks orders</th>
<td class="dc-desc">Weekly beverage orders grouped by office and cost centre.</td>
<td class="dc-date"><time>12 hours ago</time> <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></svg></td>
</tr>
<tr data-type="reports">
<td class="dc-check"><input type="checkbox" class="check" aria-label="Select East coast customers in 2012"></td>
<th scope="row" class="dc-name">East coast customers in 2012</th>
<td class="dc-desc"><span class="fg-3">—</span></td>
<td class="dc-date"><time>Yesterday</time> <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></svg></td>
</tr>
<tr data-type="presentations">
<td class="dc-check"><input type="checkbox" class="check" aria-label="Select Last quarter expenses"></td>
<th scope="row" class="dc-name">Last quarter expenses</th>
<td class="dc-desc">Travel, software and contractor spend for the period.</td>
<td class="dc-date"><time>2 days ago</time> <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></svg></td>
</tr>
<tr data-type="others">
<td class="dc-check"><input type="checkbox" class="check" aria-label="Select HR 2013 worksheet"></td>
<th scope="row" class="dc-name">HR 2013 worksheet</th>
<td class="dc-desc">Headcount plan and hiring pipeline by department.</td>
<td class="dc-date"><time>2 days ago</time> <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></svg></td>
</tr>
<tr data-type="tickets">
<td class="dc-check"><input type="checkbox" class="check" aria-label="Select Salaries last quarter"></td>
<th scope="row" class="dc-name">Salaries last quarter</th>
<td class="dc-desc">Payroll totals ahead of the quarterly review.</td>
<td class="dc-date"><time>1 week ago</time> <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></svg></td>
</tr>
<tr data-type="presentations">
<td class="dc-check"><input type="checkbox" class="check" aria-label="Select Q4 board update"></td>
<th scope="row" class="dc-name">Q4 board update</th>
<td class="dc-desc">Slides for the board meeting with revenue and hiring highlights.</td>
<td class="dc-date"><time>3 days ago</time> <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></svg></td>
</tr>
<tr data-type="presentations">
<td class="dc-check"><input type="checkbox" class="check" aria-label="Select Product roadmap 2026"></td>
<th scope="row" class="dc-name">Product roadmap 2026</th>
<td class="dc-desc">Themes and milestones for the next four quarters.</td>
<td class="dc-date"><time>1 week ago</time> <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></svg></td>
</tr>
<tr data-type="pinboards">
<td class="dc-check"><input type="checkbox" class="check" aria-label="Select Brand inspiration"></td>
<th scope="row" class="dc-name">Brand inspiration</th>
<td class="dc-desc">Colour, type and photography references.</td>
<td class="dc-date"><time>Yesterday</time> <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></svg></td>
</tr>
<tr data-type="pinboards">
<td class="dc-check"><input type="checkbox" class="check" aria-label="Select Launch moodboard"></td>
<th scope="row" class="dc-name">Launch moodboard</th>
<td class="dc-desc">Visual direction for the spring campaign.</td>
<td class="dc-date"><time>4 days ago</time> <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></svg></td>
</tr>
<tr data-type="tickets">
<td class="dc-check"><input type="checkbox" class="check" aria-label="Select Billing page error"></td>
<th scope="row" class="dc-name">Billing page error</th>
<td class="dc-desc">Customer reports a failed card update on the billing page.</td>
<td class="dc-date"><time>5 hours ago</time> <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></svg></td>
</tr>
<tr data-type="forms">
<td class="dc-check"><input type="checkbox" class="check" aria-label="Select Expense claim"></td>
<th scope="row" class="dc-name">Expense claim</th>
<td class="dc-desc">Reimbursement request form for travel and supplies.</td>
<td class="dc-date"><time>2 weeks ago</time> <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></svg></td>
</tr>
<tr data-type="others">
<td class="dc-check"><input type="checkbox" class="check" aria-label="Select Office floor plan"></td>
<th scope="row" class="dc-name">Office floor plan</th>
<td class="dc-desc">Seating layout for the second floor.</td>
<td class="dc-date"><time>3 weeks ago</time> <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></svg></td>
</tr>
</tbody>
</table>
</div>
</div>
</main>
</div>
</section>
.app-stage { min-height: 100vh; display: grid; place-items: center; padding: 1.5rem 1rem; background: var(--bs-bg-2); }
.app { width: 100%; max-width: 72rem; height: min(36rem, calc(100vh - 3rem)); min-height: 28rem; display: grid; grid-template-columns: 4.25rem 11rem minmax(0, 1fr); background: var(--bs-bg-1); border: 1px solid var(--bs-border-color); border-radius: var(--bs-radius-8); overflow: hidden; box-shadow: var(--bs-box-shadow-xl); }
.rail { background: #1f2229; display: flex; flex-direction: column; }
.rail-link { display: grid; place-items: center; height: 3.5rem; color: #8b95a1; border-left: 3px solid transparent; text-decoration: none; }
.rail-link:hover { color: #fff; }
.rail-link:focus-visible { outline: 2px solid #fff; outline-offset: -4px; }
.rail-link.active { color: #fff; border-left-color: #5e9cf7; background: rgb(255 255 255 / .06); }
.rail-link:first-child { background: #3b82f6; color: #fff; border-left-color: transparent; }
.dc-side { padding: 1.25rem .5rem 1rem 1.25rem; display: flex; flex-direction: column; gap: 1.25rem; }
.dc-side-links { display: flex; flex-direction: column; gap: .125rem; }
.dc-side-link { padding: .4rem .75rem; border-radius: var(--bs-radius-4); font-size: .8125rem; font-weight: 600; color: var(--bs-fg-3); text-decoration: none; }
.dc-side-link:hover { color: var(--bs-fg-body); background: var(--bs-bg-3); }
.dc-side-link.active { color: var(--bs-fg-body); background: var(--bs-bg-3); }
.dc-side-link:focus-visible, .dc-tab:focus-visible { outline: 2px solid var(--bs-primary-base); outline-offset: 2px; }
.dc-main { display: flex; flex-direction: column; min-width: 0; padding: 0 1.25rem 1.25rem 0; }
.dc-tabs { display: flex; gap: .25rem; overflow-x: auto; scrollbar-width: none; padding: .75rem 0 .75rem .25rem; }
.dc-tab { all: unset; box-sizing: border-box; cursor: pointer; white-space: nowrap; padding: .45rem .9rem; border-radius: var(--bs-radius-pill); font-size: .8125rem; font-weight: 600; color: var(--bs-fg-3); }
.dc-tab:hover { color: var(--bs-fg-body); }
.dc-tab.active { background: var(--bs-bg-3); color: var(--bs-primary-base); box-shadow: var(--bs-box-shadow-xs); }
.dc-tab:focus-visible { outline: 2px solid var(--bs-primary-base); }
.dc-panel { flex: 1; min-height: 0; display: flex; flex-direction: column; background: var(--bs-bg-body); border: 1px solid var(--bs-border-color); border-radius: var(--bs-radius-6); overflow: hidden; }
.dc-bar { padding: .625rem 1rem; border-bottom: 1px solid var(--bs-border-color); }
.dc-panel .table-responsive { flex: 1; overflow: auto; }
.dc-table th, .dc-table td { padding: .7rem 1rem; vertical-align: middle; font-size: .875rem; }
.dc-table thead th { position: sticky; top: 0; background: var(--bs-bg-body); font-size: .6875rem; text-transform: uppercase; letter-spacing: .06em; color: var(--bs-fg-3); font-weight: 600; z-index: 1; }
.dc-check { width: 2.75rem; }
.dc-name { font-weight: 600; white-space: nowrap; }
.dc-desc { color: var(--bs-fg-2); max-width: 22rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dc-date { text-align: right; white-space: nowrap; color: var(--bs-fg-3); }
.dc-date svg { vertical-align: -2px; margin-left: .25rem; }
.dc-table tbody tr.is-selected > * { background: var(--bs-primary-bg-subtle); }
@media (width < 900px) { .dc-desc { display: none; } }
@media (width < 700px) {
.app { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto auto minmax(0, 1fr); height: auto; max-height: none; }
.rail { flex-direction: row; overflow-x: auto; }
.rail-link { flex: 1; height: 3rem; border-left: 0; border-bottom: 3px solid transparent; }
.rail-link.active { border-bottom-color: #5e9cf7; }
.dc-side { padding: .75rem 1rem 0; gap: .5rem; }
.dc-side-links { flex-direction: row; overflow-x: auto; }
.dc-main { padding: 0 1rem 1rem; }
.dc-panel { max-height: 24rem; }
.dc-date svg { display: none; }
.dc-table th, .dc-table td { padding: .65rem .625rem; }
.dc-name { white-space: normal; }
.dc-date { font-size: .75rem; }
.dc-check { width: 2.25rem; }
}
.dc-table tbody tr[hidden] { display: none; }
function initDocsTable(root) {
if (!root) return;
const all = root.querySelector('[data-select-all]');
const rows = [...root.querySelectorAll('tbody tr')];
const count = root.querySelector('[data-selected-count]');
if (!all || !rows.length) return;
const visible = () => rows.filter((r) => !r.hidden);
const box = (r) => r.querySelector('input[type="checkbox"]');
function sync() {
rows.forEach((r) => r.classList.toggle('is-selected', box(r).checked));
const vis = visible();
const n = vis.filter((r) => box(r).checked).length;
all.checked = vis.length > 0 && n === vis.length;
all.indeterminate = n > 0 && n < vis.length;
if (count) count.textContent = n ? `${n} selected` : `${vis.length} documents`;
}
all.addEventListener('change', () => { visible().forEach((r) => { box(r).checked = all.checked; }); sync(); });
rows.forEach((r) => box(r).addEventListener('change', sync));
root.addEventListener('dc:filter', (e) => { rows.forEach((r) => { r.hidden = r.dataset.type !== e.detail; }); sync(); });
root.dispatchEvent(new CustomEvent('dc:filter', { detail: 'reports' }));
}
function initTabs(root) {
if (!root) return;
const app = root.closest('#docsApp');
const panel = document.getElementById('dcPanel');
const tabs = [...root.querySelectorAll('[role="tab"]')];
function select(tab, focus) {
tabs.forEach((t) => {
const on = t === tab;
t.classList.toggle('active', on);
t.setAttribute('aria-selected', String(on));
t.tabIndex = on ? 0 : -1;
});
if (panel) panel.setAttribute('aria-labelledby', tab.id);
if (focus) tab.focus();
app.dispatchEvent(new CustomEvent('dc:filter', { detail: tab.dataset.type }));
}
tabs.forEach((tab, i) => {
tab.addEventListener('click', () => select(tab));
tab.addEventListener('keydown', (e) => {
let n = -1;
if (e.key === 'ArrowRight') n = (i + 1) % tabs.length;
else if (e.key === 'ArrowLeft') n = (i - 1 + tabs.length) % tabs.length;
else if (e.key === 'Home') n = 0;
else if (e.key === 'End') n = tabs.length - 1;
if (n < 0) return;
e.preventDefault();
select(tabs[n], true);
});
});
}
function initSideLinks(root) {
if (!root) return;
root.querySelectorAll('.dc-side-link').forEach((link) => {
link.addEventListener('click', (e) => {
e.preventDefault();
root.querySelectorAll('.dc-side-link').forEach((l) => { l.classList.toggle('active', l === link); l.toggleAttribute('aria-current', l === link); if (l === link) l.setAttribute('aria-current', 'page'); });
});
});
}
const app = document.getElementById('docsApp');
initDocsTable(app);
initTabs(app?.querySelector('.dc-tabs'));
initSideLinks(app);
Built on Bootstrap 6.0.0-alpha.1 (ES modules, no jQuery). Load Bootstrap through an import map and run the JavaScript as type="module" — the downloaded file is already set up that way. Alpha releases can still change class names.
Built on Bootstrap 5.3.8 with plain JavaScript — no jQuery. Supports light and dark mode via data-bs-theme.