Theming & Bootswatch
Child apps use a custom SCSS pipeline built on Bootstrap 5 and the Bootswatch theme format. The result is a compiled CSS file that replaces Bootstrap entirely — including all variable overrides, component styles, and glassmorphism effects.
Why Bootstrap 5 + Bootswatch
Building on Bootstrap 5 means the entire HTML structure comes from one of the most mature, well-documented component systems on the web. Every card, form, modal, navbar, and grid layout uses standard Bootstrap markup — which means any developer (or AI agent) already knows the patterns.
The Bootswatch integration exists to showcase portability. The theme selector isn't just a novelty — it proves that the application's UI is completely decoupled from any single visual style. Switch from the glassmorphism theme to Darkly, Slate, or Sandstone and the app still works perfectly. That portability is the point.
The included glassmorphism theme is a starting point and a reference, not a requirement. It demonstrates the full depth of what's possible: SCSS variable overrides, a glass mixin, dark/light mode, canvas backgrounds, and styled versions of every Bootstrap component. From there, an AI agent can build an entirely new Bootstrap theme from scratch — a new _variables.scss, a new _bootswatch.scss, compiled to a single CSS file that drops in and replaces the default. No HTML changes needed.
How the Theme Stack Works
Three layers combine to create the final appearance:
| Layer | File | Purpose |
|---|---|---|
| 1. Admin base | admin/assets/css/style.css | Sidebar layout, navbar structure, collapse behavior, badge hide on collapse |
| 2. Theme overrides | admin/assets/css/bs-theme-overrides.css | Dark/light mode Bootstrap overrides for standard Bootswatch themes |
| 3. App theme | your-app/assets/css/custom.css | Your compiled SCSS theme — replaces Bootstrap CSS entirely |
The theme stylesheet is loaded with id="themeStylesheet", which allows theme.js to swap it at runtime when the user picks a different Bootswatch theme.
Bootswatch Theme Switching
The child-app includes a live theme selector that pulls themes from the Bootswatch API. Users can switch between:
- Custom (default) — Your compiled glassmorphism theme (
custom.css) - Any Bootswatch theme — Loaded from CDN (Cerulean, Cosmo, Cyborg, Darkly, Flatly, Journal, Litera, Lumen, Lux, Materia, Minty, Morph, Pulse, Quartz, Sandstone, Simplex, Sketchy, Slate, Solar, Spacelab, Superhero, United, Vapor, Yeti, Zephyr)
- Registered themes — Custom themes registered by sibling apps via the admin's theme registration API
Theme Selector HTML
<select class="form-select form-select-sm" id="themeSelector" style="width: auto;"
data-custom-css="../assets/css/custom.css"
data-has-custom="<?= has_custom_theme() ? '1' : '0' ?>"
data-registered-themes='<?= get_registered_themes_json() ?>'>
<option value="custom">Custom</option>
</select>
How theme.js Works
- Fetches
https://bootswatch.com/api/5.jsonto populate the dropdown - On selection, swaps
#themeStylesheethref to the Bootswatch CDN URL - For the "Custom" option, points back to
custom.css - Persists the choice in
localStorage(key:wn_theme) and a cookie - Toggles sidebar mode: glass (transparent, canvas visible) for custom theme, dark (solid bg) for Bootswatch themes
Canvas Background
The #bgCanvas element is a fixed full-viewport canvas behind all UI. When the glassmorphism theme is active, all components are semi-transparent so the canvas shows through. When a standard Bootswatch theme is selected, theme.js hides the canvas automatically.
<canvas id="bgCanvas" style="position:fixed;top:0;left:0;width:100%;height:100%;z-index:0;pointer-events:none;"></canvas>
bg-canvas.js renders a gradient + grid pattern that responds to data-bs-theme (dark/light mode). Replace its draw() function with your own rendering — particle effects, animated gradients, game backgrounds, etc.
SCSS Theme Pipeline
The child-app compiles its theme from SCSS following the Bootswatch pattern:
custom.scss (Entry Point)
// 1. Your variable overrides
@import "variables";
// 2. Full Bootstrap (uses your overridden variables)
@import "bootstrap/scss/bootstrap";
// 3. Post-Bootstrap component overrides and extras
@import "bootswatch";
This is exactly how Bootswatch themes are structured. You can take any Bootswatch theme's _variables.scss and _bootswatch.scss, drop them in, and compile — instant theme.
_variables.scss
Override Bootstrap's default Sass variables. The reference theme sets:
// Colors — vivid accents on transparent backgrounds
$primary: #6366f1; // indigo-500
$secondary: #94a3b8; // slate-400
$success: #22c55e; // green-500
$info: #06b6d4; // cyan-500
$warning: #eab308; // yellow-500
$danger: #ef4444; // red-500
// Body — transparent so canvas shows through
$body-bg: transparent;
$body-color: rgba(255, 255, 255, 0.9);
// Cards — glass panels
$card-bg: rgba(255, 255, 255, 0.08);
$card-border-color: rgba(255, 255, 255, 0.12);
// Forms — glass inputs
$input-bg: rgba(255, 255, 255, 0.08);
$input-border-color: rgba(255, 255, 255, 0.15);
$input-focus-border-color: rgba(99, 102, 241, 0.5);
Full variable reference: node_modules/bootstrap/scss/_variables.scss
_bootswatch.scss (Glass Mixin)
The glassmorphism mixin is the core of the transparent theme:
@mixin glass($bg: rgba(255, 255, 255, 0.06), $blur: 12px) {
background: $bg;
backdrop-filter: blur($blur);
-webkit-backdrop-filter: blur($blur);
}
// Applied to every component:
.card { @include glass(rgba(255, 255, 255, 0.06), 12px); }
.navbar { @include glass(rgba(15, 15, 35, 0.05), 3px); }
.modal { @include glass(rgba(20, 20, 45, 0.9), 24px); }
.alert { @include glass(rgba(255, 255, 255, 0.06), 10px); }
Dark/Light Mode
Every component has two style sets:
- Base (dark mode) —
rgba(255, 255, 255, ...)for glass overlays on dark backgrounds - Light mode —
[data-bs-theme="light"] { ... }flips torgba(0, 0, 0, ...)
The color mode toggle (color-mode.js) sets data-bs-theme on <html> and persists to localStorage. The canvas bg-canvas.js also observes this attribute and redraws with appropriate colors.
Themed Components
The _bootswatch.scss file styles these components for both modes:
- Cards (glass panels with optional left-accent bars)
- Navbar (glass bar, 48px height)
- Sidebar (glass panel, transparent nav links)
- Forms (glass inputs, focus glow)
- Dropdowns (glass menus with shadow)
- Modals (deep glass with blur)
- Offcanvas (glass panel for right panel)
- Tables (transparent rows, subtle borders)
- Alerts (glass + colored left accent bar, no background fills)
- DataTables (search input, pagination, length select)
- Breadcrumbs, pagination, badges, toasts
- Scrollbars (custom WebKit)
- Dashboard hero (primary accent glass)
- Live search bar (
.nav-search) - Footer (
.app-footer)
Build Commands
npm install # Install Bootstrap + sass
npm run build:css # One-time compile → assets/css/custom.css
npm run watch # Watch for changes during development
npm run build:themes # Compile additional themes in themes/
Creating Your Own Theme
To create a different visual style while keeping the same structure:
- Copy
_variables.scssand change the color palette. Set$body-bg: transparentif you want canvas effects, or a solid color for a standard theme. - Edit
_bootswatch.scss— keep theglass()mixin or replace it with your own effects. The light mode overrides in the[data-bs-theme="light"]block must match. - Run
npm run build:cssto compile toassets/css/custom.css. - Test with the theme selector — switch between your custom theme and any Bootswatch theme to verify the switcher still works.
Shared Assets from Admin
Child apps reference core's CSS and JS via relative paths — never copy these files.
CSS (loaded in <head>)
<link rel="stylesheet" href="../../admin/assets/css/style.css">
<link rel="stylesheet" href="../../admin/assets/css/bs-theme-overrides.css">
<link rel="stylesheet" href="<?= h(get_app_theme_css_url()) ?>" id="themeStylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
JS (loaded in footer, after Bootstrap)
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script src="../../admin/assets/js/bs-init.js"></script> <!-- apiPost() AJAX helper -->
<script src="../../admin/assets/js/sidebar.js"></script> <!-- sidebar collapse/expand -->
<script src="../../admin/assets/js/color-mode.js"></script> <!-- dark/light mode toggle -->
<script src="../../admin/assets/js/notifications.js"></script> <!-- notification bell -->
<!-- Your app-specific JS: -->
<script src="../assets/js/theme.js"></script> <!-- Bootswatch switcher -->
<script src="../assets/js/bg-canvas.js"></script> <!-- canvas background -->
Template Shell
The reference views/template.php provides a complete application shell with these regions:
- Sidebar — collapsible nav with icon+text links, item count badges (
.sidebar-badgeauto-hides on collapse) - Top navigation — live search, notification bell, color mode toggle, theme switcher, user dropdown
- Right panel — offcanvas-end with tabbed content (General, Preferences, About)
- Footer — sticky to bottom, Terms/Privacy links, copyright
- Toast — session flash messages rendered as Bootstrap toasts
- Large modal —
#largeModalcontainer for AJAX content viamodalView() - Canvas —
#bgCanvasfixed background for animated effects