A Adminator v4.1.4

Architecture

Adminator is built around a single JS shell that renders all 18 pages from one NAV manifest, and a token-driven CSS-variable design system with light and dark variants. Here's how the pieces fit together.

Last updated May 21, 2026

The 2026 redesign rewrote Adminator around three constraints:

  1. One bundle. Every page loads the same JS and the same CSS. Pages are HTML files that fill three placeholder divs.
  2. One source of truth for navigation. A NAV array in Shell.js drives the sidebar, the breadcrumbs, and the ⌘K command palette.
  3. One source of truth for styling. Every color, shadow, radius, and font size lives in _tokens.scss as a CSS variable with light + dark variants. Touch one variable, every component updates.

This page walks through what that looks like in practice.

File layout

src/
├── *.html                       # 18 pages — each ~500 lines, mostly content
├── assets/
│   ├── scripts/2026/            # The only JS
│   │   ├── index.js             # entry — imports SCSS, mounts shell, runs init
│   │   ├── Shell.js             # NAV manifest + sidebar/topbar/footer renderers
│   │   ├── init.js              # theme toggle, dropdowns, nav-groups, accordions
│   │   ├── charts.js            # Chart.js seeds + tokens()
│   │   ├── calendar.js          # FullCalendar seed events + toolbar binding
│   │   └── maps.js              # jsvectormap world map
│   └── styles/2026/             # The only SCSS
│       ├── index.scss           # entry — imports all partials below
│       ├── _tokens.scss         # CSS variables, light + dark
│       ├── _base.scss           # reset, body, .eyebrow, .mono
│       ├── _shell.scss          # .shell, sidebar, topbar, footer chrome
│       ├── _components.scss     # .hero, .btn, .card, .grid, .table, .tag
│       ├── _forms.scss          # inputs, select, textarea, check, switch
│       ├── _ui.scss             # alerts, badges, progress, tabs, accordion, modal
│       ├── _charts.scss / _calendar.scss / _fullcalendar.scss
│       ├── _dashboard.scss / _email.scss / _chat.scss / _data.scss
│       ├── _auth.scss / _error.scss
│       └── _responsive.scss     # all media queries in one place

There is no Bootstrap.scss, no jquery.js, no legacy admin.js. The whole runtime is six JS files (~47 KB unminified) plus 18 SCSS partials. Webpack splits the output into runtime.js, 2026.js, plus auto-split vendor-fullcalendar.js / vendor-chartjs.js / vendors.js for the three heavy libraries.

Page anatomy

Every shell page is the same skeleton:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Adminator · Dashboard</title>
    <script>
      // Early-paint theme bootstrap — sets data-theme before CSS arrives,
      // so dark-mode users don't see a light flash.
      (function () {
        try {
          var saved = localStorage.getItem('dash26-theme');
          var prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
          document.documentElement.setAttribute('data-theme', saved || (prefersDark ? 'dark' : 'light'));
        } catch (e) {
          document.documentElement.setAttribute('data-theme', 'light');
        }
      })();
    </script>
  </head>
  <body data-active="dashboard" data-crumbs="Workspace | Dashboard">
    <div class="shell">
      <div data-shell-sidebar></div>
      <div class="main">
        <div data-shell-topbar></div>
        <main class="content">
          <!-- Page-specific content goes here -->
        </main>
        <div data-shell-footer></div>
      </div>
    </div>
  </body>
</html>

Three pieces matter:

  • data-active matches a key in the NAV manifest (e.g. dashboard, email, calendar). The shell renderer marks the corresponding sidebar item with is-active.
  • data-crumbs is a |-separated list. The topbar renders it as breadcrumbs, with the last segment highlighted as the current page.
  • Three placeholder divsdata-shell-sidebar, data-shell-topbar, data-shell-footer — get replaced at runtime by Shell.js.

Standalone pages (sign-in, sign-up, 404, 500) skip the .shell wrapper and use their own layout (.auth-shell, .error-shell).

The early-paint theme script

That <script> block in <head> is doing real work. Without it, a dark-mode user sees a flash of light styling between the HTML parse and the first stylesheet load — known as a FOUC (flash of unstyled content).

The script reads localStorage.dash26-theme (if previously set) or falls back to prefers-color-scheme, then sets data-theme on <html> before CSS arrives. CSS variables under :root[data-theme="dark"] activate immediately. No flash.

This is one of the few places the template embeds JS inline rather than in the bundle — because the bundle hasn’t loaded yet when this runs.

The shell renderer

Shell.js exports a single NAV array — that’s the source of truth for the sidebar, breadcrumbs, and command palette. A simplified entry looks like:

export const NAV = [
  {
    label: 'Workspace',
    items: [
      { key: 'dashboard', text: 'Dashboard', href: 'index.html',
        icon: '<path d="M3 12 12 3l9 9"/><path d="M5 10v10h14V10"/>' },
    ],
  },
  {
    label: 'Components',
    items: [
      { key: 'pages', text: 'Pages',
        icon: '<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>',
        children: [
          { key: 'blank', text: 'Blank', href: 'blank.html' },
          { key: '404',   text: '404',   href: '404.html' },
        ],
      },
    ],
  },
];

Items with children render as expandable nav groups. Icons are inline SVG paths (24×24 viewBox, stroked) so they inherit currentColor and re-color on theme change without any extra work.

The mountShell() function finds the three placeholder divs and replaces each with the rendered HTML.

How charts and maps stay in sync with the theme

Chart.js, FullCalendar, and jsvectormap don’t know about your CSS variables — they accept colors as JS values. The 2026 template solves this with a small tokens() function in each integration module:

function tokens() {
  const cs = getComputedStyle(document.documentElement);
  return {
    primary: cs.getPropertyValue('--primary').trim(),
    success: cs.getPropertyValue('--success').trim(),
    danger:  cs.getPropertyValue('--danger').trim(),
    text:    cs.getPropertyValue('--t-base').trim(),
    border:  cs.getPropertyValue('--border').trim(),
    // ...
  };
}

When the user toggles the theme, a MutationObserver watching documentElement for data-theme changes fires the rebuild — charts.js destroys its Chart instances and re-instantiates them with the new tokens. Same for FullCalendar and the vector map.

The user sees an instant theme swap. Charts redraw in the new color scheme without a page reload.

Conventions

A few patterns that show up everywhere in the codebase:

  • CSS variables, never hex. All colors live in _tokens.scss. If you find yourself writing #2563EB in a partial, stop — use var(--primary) and add the token if it doesn’t exist.
  • is-active / is-open / is-done for state classes (BEM-ish modifiers). Toggled by JS.
  • data- attributes drive JS, never ids except for themeToggle and heroDate.
  • Inline <svg> icons (24×24 viewBox, stroke-width: 1.75–2, fill: none). Avoid icon-font dependencies.
  • 'Inter' for body, 'Inter Tight' for display, 'JetBrains Mono' for numerics/eyebrows. Loaded once via @import url(...) at the top of index.scss.

What’s not in the bundle

Things you’d expect in a typical jQuery-era admin template that Adminator 4 deliberately removed:

  • Bootstrap — replaced with custom UI primitives in _components.scss, _ui.scss, _forms.scss
  • jQuery — replaced with querySelectorAll, classList, addEventListener
  • Popper.js — dropdown positioning is plain CSS position: absolute
  • Day.js — date formatting uses native Intl.DateTimeFormat
  • Perfect Scrollbar — native browser scrollbars styled via CSS

Bundle size dropped from ~4.5 MB (v3) to ~700 KB (v4) — a 85% reduction. See the migration guide for what changed.