AGENTS.md
August 7, 2026 · View on GitHub
Cross-tool agent instructions for Gentelella v4. Read by Aider, Cline, Codex, Continue, and any tool following the agents.md convention. Claude Code reads CLAUDE.md; Cursor reads .cursor/rules/; GitHub Copilot reads .github/copilot-instructions.md. Content is intentionally overlapping — each tool only sees its own file.
What this is
Gentelella v4 (4.1.1) — free admin dashboard template by Colorlib. 58 server-rendered HTML pages in production/, built with Vite 8 (Rolldown). Vanilla ES2022, no Bootstrap, no jQuery, no SPA framework. SCSS only. Heavyweight runtime deps are limited to ECharts 6, DataTables.net 3, and Leaflet 1.9 — all lazy-imported per page.
Live preview: https://preview.colorlib.com/theme/gentelella/.
Setup
npm install
npm run dev # Vite dev server on :9173 → opens /production/index.html
Build / preview / deploy:
npm run build # → dist/
npm run preview # serve built dist/ on :9174
npm run deploy:preview # build + sync to R2 with cache headers
Architecture
- Single entry src/main-v4.js. Imports
scss/v4/main.scss, mounts the shell, runsinitCharts/initTables/initCommandPalette/initPageActions, then lazy-imports page-specific modules guarded by DOM presence (if (document.getElementById('inbox-root')) import(...)). - Shell injection at build time. vite.config.js's
shellInjectionPlugininlines sidebar/topbar/footer into every page whose body hasdata-shell="admin". No FOUC. Runtime src/v4/shell.jsmountShell()is a fallback for opening raw HTML. - Auto-discovered entries.
discoverEntries()in vite.config.js walksproduction/*.htmland registers each as a Rollup input. No hand-maintained input list. - Three lazy vendor chunks:
vendor-echarts(chart pages),vendor-tables(table pages),vendor-maps(map page). Everything else ships in the main chunk. - NAV is one constant.
NAVin src/v4/shell-render.js, 7 groups. Pages match into NAV bydata-page↔ leafkey. - Theming via CSS custom properties. Tokens in src/scss/v4/_tokens.scss under
:rootand[data-theme="dark"]. Pre-paint inline script (in the Vite plugin) setsdata-themeon<html>fromlocalStoragebefore body renders. - PWA. Service worker registered only in
import.meta.env.PROD.site.webmanifest+ meta tags injected into every page by the Vite plugin. Subpath-safe: paths useimport.meta.env.BASE_URL.
Directory layout
src/
main-v4.js # Entry — mounts shell, lazy-loads modules
scss/v4/ # 10 partials, main.scss is the @use'd entry
v4/
shell.js # mountShell — runtime shell behavior
shell-render.js # Pure renderers + NAV + ICONS
menus.js # openMenu / openPanel
modal.js # showModal
toast.js # showToast
charts.js # ECharts wrapper + factories
tables.js # DataTables wrapper
command-palette.js # ⌘K
page-actions.js
inbox.js kanban.js calendar.js settings.js file-manager.js
form-controls.js # Date range, multi-select, rich text
details.js markup.js data-adapter.js
product-images.js product-mockups.js
production/ # 58 HTML entry pages (auto-discovered)
public/ # Copied verbatim to dist/
types/gentelella.d.ts # Type declarations for the public JS surface
scripts/
new-page.mjs # npm run new -- <slug>
screenshots.mjs # npm run screenshots
smoke.mjs # npm run smoke
deploy-preview.sh # npm run deploy:preview
examples/ # Standalone integrations (Express/SQLite, etc.)
Conventions
- Vanilla DOM only.
querySelector,classList,addEventListener. No jQuery, no SPA framework. - Lazy import per-page modules with a DOM-presence guard so the main bundle never ships unused code.
- Idempotent
init<Name>()exports. Safe to call when the root element is absent; safe to call twice. - Event delegation on
documentfor common interactions (toggles, todo checkboxes, chart tabs) — see the bottom of src/main-v4.js. Components that own their state (inbox, kanban, command palette) register on their own root. showModal()/showToast()(v4/modal.js, v4/toast.js) for overlays;openMenu()/openPanel()(v4/menus.js) for dropdowns and slide-outs. Both handle outside-click / escape / focus return.- CSS custom properties for colors. Never hex literals in components. Charts read them via
getComputedStyle(document.documentElement).getPropertyValue('--…')so dark-mode redraw is automatic. - Subpath-safe URLs. Use
import.meta.env.BASE_URLin JS and${base}in the Vite plugin. Insideproduction/*.html, use relative paths. - No
console.*in shipped code. Terser drops them in production builds; lint flags them so you catch them earlier. - ESLint + Prettier (single quotes, semicolons, 2-space indent). Run before committing; CI doesn't gate.
- Shell opt-in. Pages without
data-shell="admin"don't get a sidebar/topbar (login, marketing, error pages).
Anti-patterns
- Don't add jQuery, Bootstrap, or any SPA framework. The whole pitch of v4 is "vanilla and small."
- Don't write Vite entry input lists by hand — drop the file in
production/. - Don't hand-roll your own modal/toast/dropdown — use v4/modal.js, v4/toast.js, v4/menus.js.
- Don't hard-code
/in asset paths. Useimport.meta.env.BASE_URL. - Don't bypass
mountShell()to wire up sidebar/topbar yourself — setdata-shell="admin"and let the Vite plugin inject. - Don't import all of ECharts. Use modular imports — match the pattern in src/v4/charts.js.
- Don't edit files in
dist/,node_modules/, ordocs/screenshots/— generated. - Don't introduce a build step besides Vite. No PostCSS pipeline, no Webpack alongside, no Tailwind.
- Don't use
new bootstrap.Modal(...)— there is no Bootstrap.
Recipes
Add a new page
Preferred — scaffolder writes the HTML, body attributes, and (optionally) the NAV entry:
npm run new -- reports --title "Reports" --nav-group "Admin"
npm run new -- user-roles --title "User roles" \
--breadcrumb "Home > User management|user_management.html > Roles" \
--nav-group "Admin" --icon profile
By hand:
production/<slug>.htmlwith<body data-shell="admin" data-page="<slug>" data-breadcrumb="Home > …">and a<script type="module" src="/src/main-v4.js"></script>in<head>.- Append to the right group in
NAVin src/v4/shell-render.js.keymatchesdata-page. - New icon? Add to
ICONSin the same file (inline SVG,currentColorstroke).
Breadcrumb segments link automatically when their text matches a NAV item (Forms → form.html; a parent group resolves to its first child). Point anywhere else with a pipe — data-breadcrumb="Home > Projects|projects.html > Acme Redesign". The last segment is the current page and is never a link. A segment with no match and no explicit target renders as plain text, so drop grouping-only levels (Apps, Layouts) rather than shipping a dead crumb.
Add a chart
<div class="card chart-card"><div class="chart" data-chart="<id>"></div></div>in the page.- Add a
case '<id>':ininitCharts()in src/v4/charts.js that builds and returns the EChartsoption. - Read colors via
getComputedStyle(document.documentElement).getPropertyValue('--token-name')— dark mode redraw is automatic.
Add a modal or toast
import { showModal } from './v4/modal.js';
showModal({
title: 'Delete project?',
body: 'This cannot be undone.',
actions: [
{ label: 'Cancel', variant: 'ghost' },
{ label: 'Delete', variant: 'danger', action: () => { /* … */ } }
]
});
import { showToast } from './v4/toast.js';
showToast('Saved', { variant: 'success' });
Add a page-local module
// At the bottom of src/main-v4.js:
if (document.querySelector('.reports-root')) {
import('./v4/reports.js').then((m) => m.initReports());
}
Export a single initReports() from src/v4/reports.js. Guard re-entry; idempotent.
Subpath / deploy
BASE_PATH=/theme/gentelella/ npm run build # build under a subpath
PREVIEW_SLUG=gentelella npm run deploy:preview # build + R2 sync, scoped to /theme/gentelella/
scripts/deploy-preview.sh does three passes: long-cache for hashed assets, short-cache for HTML, no-cache for sw.js and site.webmanifest. This works around Cloudflare APO pinning stale HTML at deleted hashed asset URLs.
TypeScript
No .ts files, but types/gentelella.d.ts declares the public JS surface. package.json "types" points to it; VS Code / your editor picks it up automatically for IntelliSense across src/v4/*.js.
Commands reference
npm run dev # Dev server on :9173 (PORT to override)
npm run build # Production build → dist/
npm run preview # Serve dist/ on :9174
npm run lint # ESLint
npm run lint:fix
npm run format # Prettier write
npm run format:check
npm run new -- <slug> # Scaffold a page
npm run screenshots # 22 pages × light+dark → docs/screenshots/
npm run smoke # Boot dev server, fetch every page, assert 200
npm run analyze # Build + open dist/stats.html
npm run deploy:preview # Build + R2 sync