create-berna-stencil 1.0.51 → 1.0.53

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,45 +1,49 @@
1
- const fileSystem = require("fs");
2
- const path = require("path");
1
+ const fileSystem = require('fs');
2
+ const path = require('path');
3
3
 
4
- const { addSiteData, removeSiteData } = require("./updateData");
5
- const { addLayout, removeLayout } = require("./updateIncludes");
4
+ const { addSiteData, removeSiteData, renameSiteData } = require('./updateData');
5
+ const { addLayout, removeLayout, renameLayout } = require('./updateIncludes');
6
+ const { getCurrentOutputPath } = require('./updateOutputPath');
7
+ const { toCamelCase } = require('./utils');
6
8
 
7
9
  const TEMPLATES_DIR = path.join(__dirname, '..', 'res', 'templates');
8
10
 
9
- function toCamelCase(str) {
10
- return str.toLowerCase().replace(/[-_][a-z0-9]/g, (group) =>
11
- group.toUpperCase().replace('-', '').replace('_', '')
12
- );
11
+ // --- Helpers ---
12
+
13
+ // Returns the three file targets (scss, js, njk) for a given page name
14
+ function getPageTargets(pageName) {
15
+ const camelName = toCamelCase(pageName);
16
+ return [
17
+ { folder: 'src/frontend/scss/pages', templateFile: 'template.scss', fileName: `${camelName}.scss` },
18
+ { folder: 'src/frontend/js/pages', templateFile: 'template.js', fileName: `${camelName}.js` },
19
+ { folder: 'src/frontend/_routes', templateFile: 'template.njk', fileName: `${pageName}.njk` },
20
+ ];
13
21
  }
14
22
 
23
+ // --- Public API ---
24
+
15
25
  function addPage(pageName) {
16
26
  const camelName = toCamelCase(pageName);
17
27
 
18
- const targets = [
19
- { folder: "src/frontend/scss/pages", templateFile: "template.scss", fileName: `${camelName}.scss` },
20
- { folder: "src/frontend/js/pages", templateFile: "template.js", fileName: `${camelName}.js` },
21
- { folder: "src/frontend/_routes", templateFile: "template.njk", fileName: `${pageName}.njk` },
22
- ];
23
-
24
- targets.forEach(({ folder, templateFile, fileName }) => {
28
+ getPageTargets(pageName).forEach(({ folder, templateFile, fileName }) => {
25
29
  const destPath = path.join(folder, fileName);
26
30
  fileSystem.mkdirSync(folder, { recursive: true });
27
31
 
28
- if (!fileSystem.existsSync(destPath)) {
29
- const srcPath = path.join(TEMPLATES_DIR, templateFile);
32
+ if (fileSystem.existsSync(destPath)) return;
30
33
 
31
- if (templateFile === "template.njk") {
32
- let content = fileSystem.readFileSync(srcPath, 'utf8');
33
- content = content
34
- .replace(/^title:.*$/m, `title: "${camelName}"`)
35
- .replace(/^permalink:.*$/m, `permalink: "/${pageName}/"`);
36
- fileSystem.writeFileSync(destPath, content);
37
- } else {
38
- fileSystem.copyFileSync(srcPath, destPath);
39
- }
34
+ const srcPath = path.join(TEMPLATES_DIR, templateFile);
40
35
 
41
- console.log(`[created file] ${destPath}`);
36
+ if (templateFile === 'template.njk') {
37
+ // Patch the frontmatter placeholders with the actual page values
38
+ const content = fileSystem.readFileSync(srcPath, 'utf8')
39
+ .replace(/^title:.*$/m, `title: "${camelName}"`)
40
+ .replace(/^permalink:.*$/m, `permalink: "/${pageName}/"`);
41
+ fileSystem.writeFileSync(destPath, content);
42
+ } else {
43
+ fileSystem.copyFileSync(srcPath, destPath);
42
44
  }
45
+
46
+ console.log(`[created file] ${destPath}`);
43
47
  });
44
48
 
45
49
  addLayout(pageName);
@@ -50,10 +54,11 @@ function renamePage(oldName, newName) {
50
54
  const oldCamel = toCamelCase(oldName);
51
55
  const newCamel = toCamelCase(newName);
52
56
 
57
+ // Use consistent src/frontend/ paths (matching addPage)
53
58
  const filesToRename = [
54
- { src: `src/scss/pages/${oldCamel}.scss`, dest: `src/scss/pages/${newCamel}.scss` },
55
- { src: `src/js/pages/${oldCamel}.js`, dest: `src/js/pages/${newCamel}.js` },
56
- { src: `src/_routes/${oldName}.njk`, dest: `src/_routes/${newName}.njk` },
59
+ { src: `src/frontend/scss/pages/${oldCamel}.scss`, dest: `src/frontend/scss/pages/${newCamel}.scss` },
60
+ { src: `src/frontend/js/pages/${oldCamel}.js`, dest: `src/frontend/js/pages/${newCamel}.js` },
61
+ { src: `src/frontend/_routes/${oldName}.njk`, dest: `src/frontend/_routes/${newName}.njk` },
57
62
  ];
58
63
 
59
64
  filesToRename.forEach(({ src, dest }) => {
@@ -65,29 +70,30 @@ function renamePage(oldName, newName) {
65
70
  console.log(`[renamed] ${src} → ${dest}`);
66
71
  });
67
72
 
68
- removeLayout(oldName);
69
- addLayout(newName);
70
- removeSiteData(oldName);
71
- addSiteData(newName);
73
+ // Use atomic rename helpers instead of remove + add separately
74
+ renameLayout(oldName, newName);
75
+ renameSiteData(oldName, newName);
72
76
  }
73
77
 
74
78
  function removePage(pageName) {
75
79
  const camelName = toCamelCase(pageName);
76
- const OUTPUT_DIR = "out";
80
+
81
+ // Read the actual current output dir instead of hardcoding "out"
82
+ const OUTPUT_DIR = getCurrentOutputPath() || 'out';
77
83
 
78
84
  const filesToDelete = [
79
85
  `src/frontend/scss/pages/${camelName}.scss`,
80
86
  `src/frontend/js/pages/${camelName}.js`,
81
87
  `src/frontend/_routes/${pageName}.njk`,
82
- path.join(OUTPUT_DIR, "js/pages", `${camelName}.js`),
83
- path.join(OUTPUT_DIR, "css/pages", `${camelName}.css`),
88
+ path.join(OUTPUT_DIR, 'js/pages', `${camelName}.js`),
89
+ path.join(OUTPUT_DIR, 'css/pages', `${camelName}.css`),
84
90
  path.join(OUTPUT_DIR, `${pageName}.html`),
85
- path.join(OUTPUT_DIR, "pages", `${pageName}.html`),
91
+ path.join(OUTPUT_DIR, 'pages', `${pageName}.html`),
86
92
  ];
87
93
 
88
94
  const foldersToDelete = [
89
95
  path.join(OUTPUT_DIR, pageName),
90
- path.join(OUTPUT_DIR, "pages", pageName),
96
+ path.join(OUTPUT_DIR, 'pages', pageName),
91
97
  ];
92
98
 
93
99
  filesToDelete.forEach(f => {
@@ -0,0 +1,12 @@
1
+ // Shared utility functions used across modules
2
+
3
+ // Converts a kebab-case or snake_case string to camelCase.
4
+ // Uses slice(1) to remove the delimiter, avoiding the double-replace bug
5
+ // that occurs when using .replace('-', '') without the /g flag.
6
+ function toCamelCase(str) {
7
+ return str.toLowerCase().replace(/[-_][a-z0-9]/g, (group) =>
8
+ group.slice(1).toUpperCase()
9
+ );
10
+ }
11
+
12
+ module.exports = { toCamelCase };
@@ -1,36 +1,59 @@
1
- # To be finished...
2
-
3
1
  # Assistant CLI
4
2
 
5
- `assistant.js` is a CLI tool to manage pages and project configuration without editing files manually.
3
+ An interactive CLI to manage pages without touching files manually.
4
+
5
+ ```
6
+ npm run assistant
7
+ ```
8
+
9
+ ## Menu
6
10
 
7
- ## Run
8
- ```bash
9
- node assistant.js
11
+ ```
12
+ 1. Create page
13
+ 2. Remove page
14
+ 3. Rename page
15
+ 4. Configure output path
10
16
  ```
11
17
 
12
- ## Options
18
+ Use `CTRL/CMD + C` to exit.
13
19
 
14
- ### 1. Create a page
15
- Enter a page name in kebab-case (e.g. `contact-us`). The CLI generates:
20
+ ## Create page
21
+
22
+ Enter a page name in any format — the CLI converts it to kebab-case automatically.
23
+
24
+ For a page named `my-page`, the following files are created:
16
25
 
17
26
  | File | Purpose |
18
27
  |---|---|
19
- | `src/pages/contact-us.njk` | Page template |
20
- | `src/scss/pages/contactUs.scss` | Page styles |
21
- | `src/js/pages/contactUs.js` | Page scripts |
28
+ | `src/frontend/scss/pages/myPage.scss` | SCSS entry point |
29
+ | `src/frontend/js/pages/myPage.js` | JS entry point |
30
+ | `src/frontend/_routes/my-page.njk` | Nunjucks template |
31
+
32
+ It also adds an `elif` block in `includes.njk` and a stub entry in `site.json`:
33
+
34
+ ```json
35
+ "myPage": {
36
+ "seo": {
37
+ "title": "My Page",
38
+ "description": "description"
39
+ },
40
+ "cdn": {
41
+ "css": [],
42
+ "js": []
43
+ }
44
+ }
45
+ ```
46
+
47
+ ## Remove page
48
+
49
+ Deletes all source files for the page and cleans up the output directory, `includes.njk`, and `site.json`.
22
50
 
23
- It also registers the page automatically in:
24
- - `src/layouts/includes.njk` — adds an `elif` block for component routing
25
- - `src/data/site.json` — adds an SEO entry under `pages`
51
+ ## Rename page
26
52
 
27
- > Protected pages (`homepage`, `404`) cannot be created or removed.
53
+ Renames all three source files, updates the `elif` block in `includes.njk`, and renames the record in `site.json` while preserving all existing fields.
28
54
 
29
- ### 2. Remove a page
30
- Enter the page name to delete all related files and clean up registrations.
55
+ ## Configure output path
31
56
 
32
- ### 3. Configure output path
33
- Change the build output directory. Updates both `.eleventy.js` and `package.json` automatically.
57
+ Updates the output directory across `.eleventy.js` and all relevant `package.json` scripts in one shot. The old output folder is deleted automatically.
34
58
 
35
- ## Naming conventions
36
- Page names are always kebab-case for URLs and file names (`contact-us`), and camelCase for SCSS and JS (`contactUs`). The CLI handles the conversion automatically.
59
+ > ⚠️ `homepage` and `404` are protected — they cannot be created, removed, or renamed via the CLI.
@@ -0,0 +1,210 @@
1
+ # Backend
2
+
3
+ The backend is a PHP REST API located in `src/backend/`, copied to the output directory automatically at build time.
4
+
5
+ ## Structure
6
+
7
+ ```
8
+ src/backend/
9
+ ├── api/
10
+ │ ├── public/ # Endpoints accessible without an API key
11
+ │ └── protected/ # Endpoints requiring X-Api-Key header
12
+ ├── database/
13
+ │ ├── Database.php
14
+ │ ├── models/
15
+ │ └── migrations/
16
+ ├── config.php # Your local config — never commit this
17
+ └── config.example.php
18
+ ```
19
+
20
+ ## Configuration
21
+
22
+ `config.php` works like a `.env` file — it holds secrets and environment settings that stay local and out of version control.
23
+
24
+ Copy `config.example.php` to `config.php` and fill in your values:
25
+
26
+ ### config.php <small>(`src/backend/`)</small>
27
+ ```php
28
+ return [
29
+ 'APP_ENV' => 'development', // or 'production'
30
+ 'API_KEY' => 'your-default-key',
31
+
32
+ 'ENDPOINT_KEYS' => [
33
+ 'subfolder/example-protected' => 'specific-key',
34
+ ],
35
+
36
+ 'DB_HOST' => '127.0.0.1',
37
+ 'DB_NAME' => 'example_db',
38
+ 'DB_USER' => 'root',
39
+ 'DB_PASS' => '',
40
+ ];
41
+ ```
42
+
43
+ `API_KEY` is the fallback key for all protected endpoints. Use `ENDPOINT_KEYS` to assign a different key to a specific endpoint — for subfolder endpoints, use the relative path as the key.
44
+
45
+ ## How routing works
46
+
47
+ The file path inside `api/` maps directly to the URL. Extra URL segments become route parameters available as `$requestParams[]`.
48
+
49
+ Every endpoint file has access to:
50
+
51
+ | Variable | Description |
52
+ |---|---|
53
+ | `$method` | HTTP method (`GET`, `POST`, `PUT`, `PATCH`, `DELETE`) |
54
+ | `$requestParams` | Extra URL segments (e.g. `/api/posts/42` → `['42']`) |
55
+
56
+ ## Creating a public endpoint
57
+
58
+ Create a `.php` file anywhere inside `api/public/`
59
+
60
+ ### api/public/posts.php
61
+ ```php
62
+ <?php
63
+ declare(strict_types=1);
64
+
65
+ require_once CORE_PATH . '/modules/Response.php';
66
+
67
+ if ($method !== 'GET') {
68
+ Response::error('Method not allowed', 405);
69
+ }
70
+
71
+ $id = isset($requestParams[0]) ? (int)$requestParams[0] : null;
72
+
73
+ Response::success(['id' => $id]);
74
+ ```
75
+
76
+ Reachable at `/api/posts` or `/api/posts/42`
77
+
78
+ ## Creating a protected endpoint
79
+
80
+ Create a `.php` file inside `api/protected/`. The API key check happens automatically before your file runs.
81
+
82
+ ### api/protected/admin/stats.php
83
+ ```php
84
+ <?php
85
+ declare(strict_types=1);
86
+
87
+ require_once CORE_PATH . '/modules/Response.php';
88
+
89
+ if ($method !== 'GET') {
90
+ Response::error('Method not allowed', 405);
91
+ }
92
+
93
+ Response::success(['visits' => 1024]);
94
+ ```
95
+
96
+ To assign a dedicated key, add it to `config.php`:
97
+
98
+ ```php
99
+ 'ENDPOINT_KEYS' => [
100
+ 'admin/stats' => 'secret-stats-key',
101
+ ],
102
+ ```
103
+
104
+ ## The Response helper
105
+
106
+ ```php
107
+ Response::success($data, $code); // default 200
108
+ Response::error($message, $code, $details); // default 400
109
+ Response::noContent(); // 204
110
+ ```
111
+
112
+ ## Handling multiple methods
113
+
114
+ ```php
115
+ $id = isset($requestParams[0]) ? (int)$requestParams[0] : null;
116
+ $input = json_decode(file_get_contents('php://input'), true) ?? [];
117
+
118
+ switch ($method) {
119
+ case 'GET':
120
+ Response::success(['id' => $id]);
121
+ break;
122
+
123
+ case 'POST':
124
+ if (empty($input['title'])) Response::error('Missing title', 400);
125
+ Response::success(['message' => 'Created'], 201);
126
+ break;
127
+
128
+ case 'DELETE':
129
+ if (!$id) Response::error('ID required', 400);
130
+ Response::success(['message' => 'Deleted']);
131
+ break;
132
+
133
+ default:
134
+ Response::error('Method not allowed', 405);
135
+ }
136
+ ```
137
+
138
+ ## Using the database
139
+
140
+ ### database/models/Post.php
141
+ ```php
142
+ <?php
143
+ declare(strict_types=1);
144
+
145
+ require_once __DIR__ . '/../Database.php';
146
+
147
+ class Post {
148
+ private PDO $db;
149
+
150
+ public function __construct() {
151
+ $this->db = Database::getInstance();
152
+ }
153
+
154
+ public function getAll(): array {
155
+ return $this->db->query("SELECT * FROM posts")->fetchAll();
156
+ }
157
+
158
+ public function getById(int $id): ?array {
159
+ $stmt = $this->db->prepare("SELECT * FROM posts WHERE id = :id");
160
+ $stmt->execute(['id' => $id]);
161
+ return $stmt->fetch() ?: null;
162
+ }
163
+
164
+ public function create(string $title): int {
165
+ $stmt = $this->db->prepare("INSERT INTO posts (title) VALUES (:title)");
166
+ $stmt->execute(['title' => htmlspecialchars(strip_tags(trim($title)))]);
167
+ return (int)$this->db->lastInsertId();
168
+ }
169
+ }
170
+ ```
171
+
172
+ Then use it inside an endpoint:
173
+
174
+ ```php
175
+ require_once __DIR__ . '/../../database/models/Post.php';
176
+
177
+ $post = new Post();
178
+ Response::success($post->getAll());
179
+ ```
180
+
181
+ Migrations live in `database/migrations/` as plain SQL files — run them manually against your database.
182
+
183
+ ## Calling endpoints from the frontend
184
+
185
+ ```js
186
+ // Public
187
+ const res = await fetch('/api/posts/42');
188
+
189
+ // Protected
190
+ const res = await fetch('/api/admin/stats', {
191
+ headers: { 'X-Api-Key': 'secret-stats-key' }
192
+ });
193
+
194
+ // POST
195
+ const res = await fetch('/api/posts', {
196
+ method: 'POST',
197
+ headers: { 'Content-Type': 'application/json', 'X-Api-Key': 'your-key' },
198
+ body: JSON.stringify({ title: 'Hello world' })
199
+ });
200
+ ```
201
+
202
+ ## Pre-built endpoints
203
+
204
+ | Route | Auth | Methods | Description |
205
+ |---|---|---|---|
206
+ | `/api/example-public` | No | `GET` | Smoke test for public routing |
207
+ | `/api/subfolder/example-protected` | Yes | `GET` | Smoke test for protected routing |
208
+ | `/api/auth/register` | No | `POST` | Register a new user |
209
+ | `/api/auth/login` | No | `POST` | Login and retrieve user data |
210
+ | `/api/auth-system` | Yes | `GET POST PUT PATCH DELETE` | Full CRUD on users |
@@ -3,7 +3,7 @@
3
3
 
4
4
  This json holds global settings used across all pages in `base.njk` and other components:
5
5
 
6
- ### site.json <small>`src/frontend/data/`</small>
6
+ ### site.json <small>`(src/frontend/data/)`</small>
7
7
  ```json
8
8
  "site_name": "Site name",
9
9
  "title": "Site title",
@@ -25,7 +25,7 @@ Each page entry is keyed by its camelCase `title` from the front matter:
25
25
 
26
26
  If you don't want to use a particular cdn inserting it in `base.njk` for all pages, you can add extra specific cdn (css, js) by inserting the link in each page of site.json separating them with a `,` and setting them in ""
27
27
 
28
- ### site.json <small>`src/frontend/data/`</small>
28
+ ### site.json <small>`(src/frontend/data/)`</small>
29
29
  ```json
30
30
  "pages": {
31
31
  ...
@@ -1,14 +1,30 @@
1
- # To be finished...
2
-
3
1
  # JavaScript
4
2
 
5
- ## Page files
3
+ ## Page JS
4
+
5
+ Each page has its own JS entry point in `src/frontend/js/pages/`
6
6
 
7
- Each page has a JS entry point in `src/js/pages/`. It is bundled by esbuild and loaded automatically by `base.njk`. Import only what the page needs.
7
+ It is bundled and minified by esbuild and loaded automatically by `base.njk`
8
8
 
9
- ## Module categories
9
+ Import only what the page needs.
10
10
 
11
- **Call inside `DOMContentLoaded`** — these interact with the DOM and require the page to be fully loaded:
11
+ ### examplePage.js <small>(`src/frontend/js/pages/`)</small>
12
+ ```js
13
+ import { initLangSwitcher } from '../modules/langSwitcher.js';
14
+ import { showNotification } from '../modules/notification.js';
15
+
16
+ document.addEventListener("DOMContentLoaded", () => {
17
+ initLangSwitcher();
18
+ });
19
+
20
+ showNotification("Page loaded", "success", 3000);
21
+ ```
22
+
23
+ ## Modules
24
+
25
+ Modules live in `src/frontend/js/modules/`. Some must be called inside `DOMContentLoaded` as they interact with the DOM; others create elements dynamically and can be called anywhere.
26
+
27
+ ### Call inside `DOMContentLoaded`
12
28
 
13
29
  | Module | Function |
14
30
  |---|---|
@@ -17,26 +33,13 @@ Each page has a JS entry point in `src/js/pages/`. It is bundled by esbuild and
17
33
  | `modules/forms/textAreaAutoExpand.js` | `initTextAreaAutoExpand()` |
18
34
  | `modules/forms/normalizePhoneNumber.js` | `initNormalizePhoneNumber()` |
19
35
 
20
- **Call anywhere** — these create DOM elements dynamically and can be called at any point after the script loads:
36
+ ### Call anywhere
21
37
 
22
38
  | Module | Function |
23
39
  |---|---|
24
40
  | `modules/notification.js` | `showNotification(text, type, duration)` |
25
41
 
26
- ## Usage example
27
-
28
- ```js
29
- import { initLangSwitcher } from '../modules/langSwitcher.js';
30
- import { showNotification } from '../modules/notification.js';
31
-
32
- document.addEventListener("DOMContentLoaded", () => {
33
- initLangSwitcher();
34
- });
35
-
36
- showNotification("Page loaded", "success", 3000);
37
- ```
38
-
39
- ## `showNotification` reference
42
+ ### `showNotification` parameters
40
43
 
41
44
  | Parameter | Type | Default | Values |
42
45
  |---|---|---|---|
@@ -46,8 +49,21 @@ showNotification("Page loaded", "success", 3000);
46
49
 
47
50
  ## Adding a module
48
51
 
49
- Create a file in `src/js/modules/`, export your function, and import it in the pages that need it. Use ESM syntax (`import` / `export`) — this code is processed by esbuild.
52
+ Create a new `.js` file in `src/frontend/js/modules/`. You can organize them into subfolders freely.
53
+
54
+ Use ESM syntax — esbuild handles the bundling:
55
+
56
+ ```js
57
+ // _yourModule.js
58
+ export function yourFunction() {
59
+ // ...
60
+ }
61
+ ```
62
+
63
+ Then import it in the pages that need it:
50
64
 
51
- ## `assistant_utils/`
65
+ ```js
66
+ import { yourFunction } from '../modules/yourModule.js';
67
+ ```
52
68
 
53
- These scripts run directly in Node.js without a bundler. Use CommonJS syntax (`require` / `module.exports`) here, not ESM.
69
+ > ⚠️ Files inside `_tools/` run directly in Node.js without a bundler use CommonJS (`require` / `module.exports`) there, not ESM.
@@ -28,6 +28,58 @@ body {
28
28
  }
29
29
  ```
30
30
 
31
+ ## Global Variables
32
+
33
+ Instead of using `:root` in your custom modules or pages, the best thing to do is to centralize all your variables in a single file (that will be tree-shaken automatically by Sass)
34
+
35
+ ### _root.scss <small>(`src/frontend/scss/modules/`)</small>
36
+ ```scss
37
+ $header-height: 10vh;
38
+
39
+ // Usage example (in any other file):
40
+ header {
41
+ height: root.$header-height;
42
+ }
43
+ ```
44
+ ## Scss modules
45
+ You can create your custom css modules by creating a new `.scss` file in `src/frontend/scss/modules/` (the name of the file must start with `_`)
46
+
47
+ You can create subfolders if you want to refactor the structure, but be sure to update the relative paths in the pages that import them
48
+
49
+ ### _yourModule.scss <small>(`src/frontend/scss/modules/subfolder/`)</small>
50
+ ```scss
51
+ @use '../root' as root;
52
+
53
+ body {
54
+ background-color: root.$primary;
55
+ }
56
+ ```
57
+
58
+ ### examplePage.scss
59
+ ```scss
60
+ @import "../modules/subfolder/yourModule";
61
+
62
+ // This page will now inherit the body tag rules
63
+ // If the same property is declared in both, the last imported one wins
64
+ body {
65
+ color: root.$dark;
66
+ }
67
+ ```
68
+
69
+ ### Pre-existing modules
70
+
71
+ | File | Purpose |
72
+ |---|---|
73
+ | `_root.scss` | Global variables (colors, spacing) |
74
+ | `_global.scss` | Site-wide base rules and frameworks |
75
+ | `_typography.scss` | Font rules
76
+ | `_header.scss` | Header styles |
77
+ | `_footer.scss` | Footer styles |
78
+ | `_mobile.scss` | Media query rules |
79
+ | `_buttons.scss` | Style and hovers for buttons
80
+ | `_animations.scss` | Keyframe animations (`fade-in`, `spin`) |
81
+ | `_notification.scss` | Notification component style |
82
+
31
83
  ## CSS Framework
32
84
 
33
85
  Some of the most popular css frameworks that supports scss with modules are already installed in `node_modules`
@@ -85,56 +137,4 @@ To reduce the bundle size, open the corresponding framework file (`src/frontend/
85
137
  ```scss
86
138
  @import "bootstrap/scss/card"; // Cards
87
139
  @import "bootstrap/scss/carousel"; // Carousel
88
- ```
89
-
90
- ## Global Variables
91
-
92
- Instead of using `:root` in your custom modules or pages, the best thing to do is to centralize all your variables in a single file (that will be tree-shaken automatically by Sass)
93
-
94
- ### _root.scss <small>(`src/frontend/scss/modules/`)</small>
95
- ```scss
96
- $header-height: 10vh;
97
-
98
- // Usage example (in any other file):
99
- header {
100
- height: root.$header-height;
101
- }
102
- ```
103
- ## Scss modules
104
- You can create your custom css modules by creating a new `.scss` file in `src/frontend/scss/modules/` (the name of the file must start with `_`)
105
-
106
- You can create subfolders if you want to refactor the structure, but be sure to update the relative paths in the pages that import them
107
-
108
- ### _yourModule.scss <small>(`src/frontend/scss/modules/subfolder/`)</small>
109
- ```scss
110
- @use '../root' as root;
111
-
112
- body {
113
- background-color: root.$primary;
114
- }
115
- ```
116
-
117
- ### examplePage.scss
118
- ```scss
119
- @import "../modules/subfolder/yourModule";
120
-
121
- // This page will now inherit the body tag rules
122
- // If the same property is declared in both, the last imported one wins
123
- body {
124
- color: root.$dark;
125
- }
126
- ```
127
-
128
- ### Pre-existing modules
129
-
130
- | File | Purpose |
131
- |---|---|
132
- | `_root.scss` | Global variables (colors, spacing) |
133
- | `_global.scss` | Site-wide base rules and frameworks |
134
- | `_typography.scss` | Font rules
135
- | `_header.scss` | Header styles |
136
- | `_footer.scss` | Footer styles |
137
- | `_mobile.scss` | Media query rules |
138
- | `_buttons.scss` | Style and hovers for buttons
139
- | `_animations.scss` | Keyframe animations (`fade-in`, `spin`) |
140
- | `_notification.scss` | Notification component style |
140
+ ```