Introduction
What's in the box, and how this documentation is organized.
AdminPro is an HTML, CSS and vanilla JavaScript admin dashboard template. It ships as two complete layouts — a collapsible left sidebar and a horizontal top navigation — covering 29 unique page templates each, plus a shared design system so both layouts always look and behave consistently.
There's no build step, no framework and no package manager involved. Every page is a plain .html file you can open directly in a browser, edit in any code editor, and deploy to any static host.
New here? Start with Installation, then read Sidebar vs. horizontal to decide which shell to build on before you start customizing pages.
Installation
There's nothing to install — open it and go.
- Unzip the package you downloaded.
- Open
landing.htmlin a browser to see the overview page, or open any page insidesidebar/orhorizontal/directly. - To deploy, upload the whole folder to any static host (Netlify, Vercel, S3, cPanel, GitHub Pages) — no server-side runtime is required.
Use a local server while editing, if you can. Every page works fine opened directly from disk (file://). If you later add features that fetch() local JSON files, browsers block that over file:// — running a simple local server (e.g. the VS Code "Live Server" extension, or python3 -m http.server) avoids the issue.
Requirements
- Any modern code editor (VS Code, Sublime, etc.) — no IDE-specific setup needed.
- An evergreen browser (Chrome, Edge, Firefox, Safari) for previewing.
- An internet connection on first load, for the Google Fonts, Bootstrap Icons and Chart.js CDN links — see Credits to self-host them instead.
Folder structure
Where everything lives in the package.
├── landing.html — overview page with both live previews
├── documentation.html — this file
│
├── sidebar/ — left-sidebar layout
│ ├── index.html, analytics.html, orders.html, …
│ └── assets/
│ ├── css/ style.css, orders.css, users.css, …
│ └── js/ script.js, orders.js, users.js, …
│
└── horizontal/ — horizontal-nav layout
├── dashboard-horizontal.html, analytics-horizontal.html, …
└── assets/
├── css/ style.css, orders-horizontal.css, …
└── js/ script.js, orders-horizontal.js, …
The two layouts are self-contained. sidebar/ and horizontal/ each have their own style.css and script.js — they're not shared between the two folders, because each layout's shared script exposes slightly different behavior (see JavaScript architecture). If you only need one layout, you can delete the other folder entirely.
Sidebar vs. horizontal
Both cover the same 29 pages with the same components — the difference is purely navigational.
| Layout | Navigation | Best for | Entry point |
|---|---|---|---|
| Sidebar | Collapsible left sidebar, with sub-menu drill-down | Apps with deep, hierarchical navigation | sidebar/index.html |
| Horizontal | Top nav bar, scrolls on narrow screens | Wide tables and chart-heavy screens | horizontal/dashboard-horizontal.html |
Every other page name in horizontal/ matches its sidebar/ counterpart with a -horizontal suffix — for example sidebar/orders.html and horizontal/orders-horizontal.html are the same screen, built on each shell.
CSS architecture
One shared stylesheet, one small file per page.
Each layout folder's assets/css/style.css holds everything shared across every page: CSS custom properties (colors, radius, shadow, spacing), resets, typography, the sidebar/nav shell, buttons, cards, badges, tables, avatars and the preloader.
Every page then loads one additional, small CSS file with only what's unique to that page — for example orders.css holds just the order-status colors and the stock-badge styling that orders.html needs.
<!-- in orders.html -->
<link rel="stylesheet" href="assets/css/style.css">
<link rel="stylesheet" href="assets/css/orders.css">
Naming convention
A page's own CSS file always matches its HTML filename: settings.html → assets/css/settings.css, orders-horizontal.html → assets/css/orders-horizontal.css. If a page needs no unique styling at all (like the dashboard, which is built entirely from shared components), it simply doesn't load a page CSS file.
CSS custom properties
Every color, radius and shadow in the template is a variable, defined once in style.css:
| Variable | Light value | Used for |
|---|---|---|
--ap-primary | #4F46E5 | Brand color — buttons, links, active states |
--ap-success | #16A34A | Positive status, growth indicators |
--ap-warning | #F59E0B | Pending / low-stock status |
--ap-danger | #DC2626 | Errors, destructive actions |
--ap-info | #0EA5E9 | Informational badges, secondary chart series |
--ap-surface | #FFFFFF | Card and panel backgrounds |
--ap-bg | #F5F7FB | Page background |
--ap-text / --ap-text-strong | #475569 / #0F172A | Body text / headings |
--ap-border | #E7EBF1 | Card borders, dividers |
--ap-radius / --ap-radius-sm | 0.75rem / 0.5rem | Card / control corner radius |
To rebrand the template, change the --ap-primary group in style.css — every button, active nav item and focus ring updates automatically, in both light and dark mode.
JavaScript architecture
One shared script, one small file per page — mirroring the CSS.
assets/js/script.js runs on every page and handles the behavior that's identical everywhere:
- Fixing dropdown menus so they aren't clipped inside scrollable tables
- Sidebar collapse (desktop) and slide-out drawer (mobile)
- Sidebar sub-menu expand/collapse (sidebar layout only)
- Light/dark theme toggle
- Fullscreen toggle
- Hiding the preloader once the page has loaded
Each page then loads one page-specific file — orders.js, users.js, and so on — containing only the data and logic that page needs: table rendering, search/filter/pagination, and any Chart.js instances.
<!-- always in this order, at the end of the page -->
<script src="assets/js/script.js"></script>
<script src="assets/js/orders.js"></script>
Keep script.js first. Page scripts read variables like shell and root that script.js declares. Loading a page script before script.js, or removing script.js, will throw a ReferenceError.
Colors & dark mode
Both driven by the same set of CSS variables — nothing to duplicate.
Dark mode is not a second set of pages or a separate stylesheet. It's the same variables, redefined once inside a [data-theme="dark"] block:
:root {
--ap-surface: #FFFFFF;
--ap-bg: #F5F7FB;
--ap-text: #475569;
}
[data-theme="dark"] {
--ap-surface: #151B2C;
--ap-bg: #0B1120;
--ap-text: #93A0B8;
}
Clicking the theme toggle in the topbar sets data-theme="dark" on the <html> element; every component repaints instantly because it was already reading from the variables above. To make dark mode the default, set data-theme="dark" directly on the <html> tag in your HTML.
Working with charts
Chart.js instances that already know about light and dark mode.
Chart-heavy pages define a small themeColors() helper that reads the current theme and returns a matching color set, and a drawAllCharts() function that destroys and rebuilds every chart on the page:
function themeColors() {
const dark = root.getAttribute('data-theme') === 'dark';
return {
grid: dark ? 'rgba(255,255,255,.06)' : 'rgba(15,23,42,.06)',
text: dark ? '#93A0B8' : '#64748B',
primary: '#4F46E5', info: '#0EA5E9'
};
}
drawAllCharts() is called once on page load, again whenever the theme toggle is clicked, and again whenever a chart's range buttons (Week / Month / Year) are clicked — so any chart you add following the same pattern gets theme-switching and range-switching for free.
Point-style legends (the small circular markers) all use a shared generateLabels override that adds spacing between the dot and the label text. Copy that pattern when adding a new chart with usePointStyle: true so its legend matches the rest of the template.
Replacing demo data
Every table, chart and list runs on a plain JavaScript array — swap it for a real API call.
Data lives at the top of each page's own JS file as a plain array of objects, for example in orders.js:
const orders = [
{ id: '#ORD-4821', name: 'Meera Shah', amount: '$248.00', status: 'success' },
// …
];
The rest of the file reads from that array to render rows, filter, sort and paginate. To connect a real backend, replace the array with the result of a fetch() call and call the existing render function once the response arrives — the rendering, search and pagination logic don't need to change.
Page reference
Every page, and where to find it in each layout.
In horizontal/, every filename above (except auth.html) carries a -horizontal suffix. *Lock screen and error pages ship in sidebar/ only, since they're standalone screens with no navigation shell.
Browser support
Tested on the latest evergreen browsers.
| Browser | Support |
|---|---|
| Chrome / Edge (Chromium) | Fully supported |
| Firefox | Fully supported |
| Safari (macOS & iOS) | Fully supported |
| Internet Explorer 11 | Not supported (uses CSS custom properties and modern JS) |
Credits
Third-party resources used, and where they're loaded from.
| Resource | Used for | Loaded via |
|---|---|---|
| Bootstrap 5.3.8 | Grid, layout utilities, base components | CDN / local assets/css+js |
| Bootstrap Icons 1.11 | All icons throughout the template | CDN |
| Chart.js 4 | All charts (line, bar, doughnut, sparklines) | CDN / local assets/js |
| Google Fonts — Inter | UI and body typeface | CDN |
| Google Fonts — Roboto Mono | Order IDs, SKUs, invoice numbers, code blocks | CDN |
| i.pravatar.cc / picsum.photos | Placeholder avatars and product photos in demo data | Remote images — replace with your own before going live |
Want to work offline? Bootstrap and Chart.js already ship locally in assets/css and assets/js in most pages — swap any remaining CDN <link>/<script> tag for Bootstrap Icons and Google Fonts with a self-hosted copy to remove the last external requests.
FAQ
Common questions before and after purchase.
Yes — delete whichever of sidebar/ or horizontal/ you don't need. They don't share files, so removing one won't affect the other.
Copy the closest existing page as a starting point, keep its <link>/<script> order intact (shared style.css/script.js first, then a new page-specific CSS/JS file named to match), and swap the content inside .ap-main.
Almost always a missing or misordered script tag — double-check assets/js/script.js is loaded, and loaded before your page's own script, as shown in JavaScript architecture.
Yes. See Replacing demo data — every table and chart reads from a plain JavaScript array that you can populate from an API response instead.
Changelog
Version history for this item.
| Version | Notes |
|---|---|
| 1.0.0 | Initial release — sidebar and horizontal layouts, 29 pages each, light/dark mode, documentation. |