--- description: Gentelella v4 — project conventions, recipes, anti-patterns globs: - "**/*.js" - "**/*.scss" - "**/*.html" - "**/*.css" alwaysApply: true --- # Gentelella v4 (`4.0.0`) Admin dashboard template by Colorlib. 58 server-rendered HTML pages in [production/](mdc:production), built with **Vite 8** (Rolldown). **Vanilla ES2022**, no Bootstrap, no jQuery, no SPA framework. SCSS only. Heavyweight deps (**ECharts 6**, **DataTables.net 2**, **Leaflet 1.9**) are lazy-imported per page. Full reference: [CLAUDE.md](mdc:CLAUDE.md). The rules below are the load-bearing ones — apply them by default. ## Hard rules - **Vanilla DOM only.** `querySelector`, `classList`, `addEventListener`. Never add jQuery, Bootstrap JS, or an SPA framework. - **Single entry**: [src/main-v4.js](mdc:src/main-v4.js). Every page loads it. Page-specific modules are lazy-imported inside `main-v4.js` guarded by DOM presence — match that pattern, don't add new ``. Then append a leaf to `NAV` in [src/v4/shell-render.js](mdc:src/v4/shell-render.js) (`key` matches `data-page`). ## Recipe: new chart 1. Markup: `
` in the page. 2. `case '':` in `initCharts()` in [src/v4/charts.js](mdc:src/v4/charts.js) returning the ECharts `option`. 3. Read tokens via `getComputedStyle(document.documentElement).getPropertyValue('--token')` for colors. ## Recipe: new page module In `src/main-v4.js`, add: ```js if (document.querySelector('.reports-root')) { import('./v4/reports.js').then((m) => m.initReports()); } ``` Export a single idempotent `initReports()` from `src/v4/reports.js`. ## Recipe: modal / 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' }); ``` ## Commands ```bash npm run dev # Vite dev server on :9173 npm run build # Production build → dist/ npm run preview # Serve built dist/ on :9174 npm run lint # ESLint over src/ npm run format # Prettier write npm run new -- # Scaffold a page npm run screenshots # Playwright capture (22 pages × light+dark) npm run smoke # Hit every page, assert 200 npm run analyze # Build + open dist/stats.html npm run deploy:preview # Build + R2 sync ``` Override the dev port with `PORT=…`. Build under a subpath with `BASE_PATH=/foo/ npm run build`.