# AGENTS.md Cross-tool agent instructions for Gentelella v4. Read by Aider, Cline, Codex, Continue, and any tool following the [agents.md](https://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.0.0`) — free admin dashboard template by Colorlib. 58 server-rendered HTML pages in [production/](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 2**, and **Leaflet 1.9** — all lazy-imported per page. Live preview: . ## Setup ```bash npm install npm run dev # Vite dev server on :9173 → opens /production/index.html ``` Build / preview / deploy: ```bash 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](src/main-v4.js). Imports `scss/v4/main.scss`, mounts the shell, runs `initCharts/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](vite.config.js)'s `shellInjectionPlugin` inlines sidebar/topbar/footer into every page whose body has `data-shell="admin"`. No FOUC. Runtime [src/v4/shell.js](src/v4/shell.js) `mountShell()` is a fallback for opening raw HTML. - **Auto-discovered entries.** `discoverEntries()` in [vite.config.js](vite.config.js) walks `production/*.html` and 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.** `NAV` in [src/v4/shell-render.js](src/v4/shell-render.js), 7 groups. Pages match into NAV by `data-page` ↔ leaf `key`. - **Theming via CSS custom properties.** Tokens in [src/scss/v4/_tokens.scss](src/scss/v4/_tokens.scss) under `:root` and `[data-theme="dark"]`. Pre-paint inline script (in the Vite plugin) sets `data-theme` on `` from `localStorage` before 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 use `import.meta.env.BASE_URL`. ## Directory layout ```text 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 -- screenshots.mjs # npm run screenshots smoke.mjs # npm run smoke deploy-preview.sh # npm run deploy:preview examples/ # Standalone integrations (Express/SQLite, etc.) ``` ## Conventions 1. **Vanilla DOM only.** `querySelector`, `classList`, `addEventListener`. No jQuery, no SPA framework. 2. **Lazy import per-page modules** with a DOM-presence guard so the main bundle never ships unused code. 3. **Idempotent `init()` exports.** Safe to call when the root element is absent; safe to call twice. 4. **Event delegation on `document`** for common interactions (toggles, todo checkboxes, chart tabs) — see the bottom of [src/main-v4.js](src/main-v4.js). Components that own their state (inbox, kanban, command palette) register on their own root. 5. **`showModal()` / `showToast()`** ([v4/modal.js](src/v4/modal.js), [v4/toast.js](src/v4/toast.js)) for overlays; **`openMenu()` / `openPanel()`** ([v4/menus.js](src/v4/menus.js)) for dropdowns and slide-outs. Both handle outside-click / escape / focus return. 6. **CSS custom properties for colors.** Never hex literals in components. Charts read them via `getComputedStyle(document.documentElement).getPropertyValue('--…')` so dark-mode redraw is automatic. 7. **Subpath-safe URLs.** Use `import.meta.env.BASE_URL` in JS and `${base}` in the Vite plugin. Inside `production/*.html`, use relative paths. 8. **No `console.*` in shipped code.** Terser drops them in production builds; lint flags them so you catch them earlier. 9. **ESLint + Prettier** (single quotes, semicolons, 2-space indent). Run before committing; CI doesn't gate. 10. **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](src/v4/modal.js), [v4/toast.js](src/v4/toast.js), [v4/menus.js](src/v4/menus.js). - Don't hard-code `/` in asset paths. Use `import.meta.env.BASE_URL`. - Don't bypass `mountShell()` to wire up sidebar/topbar yourself — set `data-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](src/v4/charts.js). - Don't edit files in `dist/`, `node_modules/`, or `docs/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: ```bash npm run new -- reports --title "Reports" --nav-group "Admin" npm run new -- user-roles --title "User roles" \ --breadcrumb "Home > Admin > Roles" --nav-group "Admin" --icon profile ``` By hand: 1. `production/.html` with `` and a `` in ``. 2. Append to the right group in `NAV` in [src/v4/shell-render.js](src/v4/shell-render.js). `key` matches `data-page`. 3. New icon? Add to `ICONS` in the same file (inline SVG, `currentColor` stroke). ### Add a chart 1. `
` in the page. 2. Add a `case '':` in `initCharts()` in [src/v4/charts.js](src/v4/charts.js) that builds and returns the ECharts `option`. 3. Read colors via `getComputedStyle(document.documentElement).getPropertyValue('--token-name')` — dark mode redraw is automatic. ### Add a modal or toast ```js 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 ```js // 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 ```bash 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](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](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 ```bash 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 -- # 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 ```