create-berna-stencil 2.0.4 → 2.0.5

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.
@@ -5,7 +5,7 @@
5
5
  // Call anywhere
6
6
  import { showNotification } from '../modules/notification.js';
7
7
 
8
- // Uncomment to enable optional modules (call inside DOMContentLoaded)
8
+ // Example of pre-existing modules
9
9
  // import { initTextAreaAutoExpand } from '../modules/forms/textAreaAutoExpand.js';
10
10
  // import { initNormalizePhoneNumber } from '../modules/forms/normalizePhoneNumber.js';
11
11
 
@@ -14,6 +14,8 @@ import { showNotification } from '../modules/notification.js';
14
14
  //==========================
15
15
 
16
16
  document.addEventListener("DOMContentLoaded", () => {
17
+ // initTextAreaAutoExpand();
18
+ // initNormalizePhoneNumber();
17
19
  });
18
20
 
19
21
  showNotification("Example notification", "success", 3000);
package/bin/create.js CHANGED
@@ -15,7 +15,7 @@ const COPY_TARGETS = [
15
15
 
16
16
  const PROJECT_PACKAGE = {
17
17
  name: path.basename(targetDir),
18
- version: '2.0.4',
18
+ version: '2.0.5',
19
19
  private: true,
20
20
  scripts: {
21
21
  "build:css": "sass src/frontend/scss:out/css --no-source-map --style=compressed --quiet --load-path=node_modules",
package/docs/Backend.md CHANGED
@@ -104,9 +104,9 @@ To assign a dedicated key, add it to `config.php`:
104
104
  ## The Response helper
105
105
 
106
106
  ```php
107
- Response::success($data, $code); // default 200
107
+ Response::success($data, $code); // default 200
108
108
  Response::error($message, $code, $details); // default 400
109
- Response::noContent(); // 204
109
+ Response::noContent(); // 204
110
110
  ```
111
111
 
112
112
  ## Handling multiple methods
@@ -10,11 +10,12 @@ Import only what the page needs.
10
10
 
11
11
  ### examplePage.js <small>(`src/frontend/js/pages/`)</small>
12
12
  ```js
13
- import { initLangSwitcher } from '../modules/langSwitcher.js';
14
13
  import { showNotification } from '../modules/notification.js';
15
14
 
15
+ import { initNormalizePhoneNumber } from '../modules/forms/normalizePhoneNumber.js';
16
+
16
17
  document.addEventListener("DOMContentLoaded", () => {
17
- initLangSwitcher();
18
+ initNormalizePhoneNumber();
18
19
  });
19
20
 
20
21
  showNotification("Page loaded", "success", 3000);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-berna-stencil",
3
- "version": "2.0.4",
3
+ "version": "2.0.5",
4
4
  "description": "Eleventy boilerplate with per-page SCSS/JS pipeline, esbuild bundling, multi-framework CSS support and a built-in page management CLI",
5
5
  "keywords": [],
6
6
  "author": "Michele Garofalo",
@@ -7,7 +7,7 @@ layout: base.njk
7
7
  <!-- !IMPORTANT -->
8
8
  <!-- This is the only page that you need to modify statically -->
9
9
 
10
- <div class="fade-in d-flex flex-column justify-content-center align-items-center" style="min-height: 60vh">
10
+ <div class="fade-in center">
11
11
  <h1>Oops! Page not found</h1>
12
12
  <a href="/">Return to homepage</a>
13
13
  </div>
@@ -1,4 +1,4 @@
1
- <div class="container my-3">
1
+ <div class="layout-container layout-my-3">
2
2
  <h1>Welcome to <span class="berna-stencil">Berna-Stencil</span></h1>
3
3
  <div class="slogan">The boilerplate you need, simplified</div>
4
4
 
@@ -42,16 +42,29 @@
42
42
  <div id="content-welcome" class="tab-content active">
43
43
  <div class="grid" style="display: flex; gap: 1rem; flex-wrap: wrap;">
44
44
  <a href="https://bernastencil.com" class="card" target="_blank" rel="noopener noreferrer" style="flex: 1; min-width: 250px;">
45
- <i class="bi bi-book card-icon" aria-hidden="true"></i>
45
+ <svg class="card-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
46
+ <path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"></path>
47
+ <path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"></path>
48
+ </svg>
46
49
  <h3>Documentation</h3>
47
50
  <p>Everything you need to get started, from setup to advanced topics and customizations</p>
48
- <span class="card-link">Go to documentation <span class="bi bi-arrow-right"></span></span>
51
+ <span class="card-link">Go to documentation
52
+ <svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" style="margin-left:4px;">
53
+ <path stroke-linecap="round" stroke-linejoin="round" d="M14 5l7 7m0 0l-7 7m7-7H3"></path>
54
+ </svg>
55
+ </span>
49
56
  </a>
50
57
  <a href="https://github.com/rhaastrake/berna-stencil" class="card" target="_blank" rel="noopener noreferrer" style="flex: 1; min-width: 250px;">
51
- <i class="bi bi-github card-icon" aria-hidden="true"></i>
58
+ <svg class="card-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
59
+ <path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"></path>
60
+ </svg>
52
61
  <h3>Github repository</h3>
53
62
  <p>Community-driven. Contributions, issues and PRs are welcome.</p>
54
- <span class="card-link">Open the repository <span class="bi bi-arrow-right"></span></span>
63
+ <span class="card-link">Open the repository
64
+ <svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" style="margin-left:4px;">
65
+ <path stroke-linecap="round" stroke-linejoin="round" d="M14 5l7 7m0 0l-7 7m7-7H3"></path>
66
+ </svg>
67
+ </span>
55
68
  </a>
56
69
  </div>
57
70
  </div>
@@ -215,23 +228,27 @@ body {
215
228
  <div id="content-javascript" class="tab-content">
216
229
  <div class="markdown-body">
217
230
  <h2>JavaScript</h2>
231
+
218
232
  <h3>Page JS</h3>
219
233
  <p>Each page has its own JS entry point in <code>src/frontend/js/pages/</code></p>
220
234
  <p>It is bundled and minified by esbuild and loaded automatically by <code>base.njk</code></p>
221
235
  <p>Import only what the page needs.</p>
236
+
222
237
  <h4>examplePage.js <small>(<code>src/frontend/js/pages/</code>)</small></h4>
223
- <pre><code>
224
- import { initLangSwitcher } from '../modules/langSwitcher.js';
225
- import { showNotification } from '../modules/notification.js';
238
+ <pre><code class="language-js">import { showNotification } from '../modules/notification.js';
226
239
 
227
- document.addEventListener("DOMContentLoaded", () => {
228
- initLangSwitcher();
229
- });
240
+ import { initNormalizePhoneNumber } from '../modules/forms/normalizePhoneNumber.js';
241
+
242
+ document.addEventListener("DOMContentLoaded", () => {
243
+ initNormalizePhoneNumber();
244
+ });
245
+
246
+ showNotification("Page loaded", "success", 3000);</code></pre>
230
247
 
231
- showNotification("Page loaded", "success", 3000);</code></pre>
232
- <h3>Modules</h3>
248
+ <h2>Modules</h2>
233
249
  <p>Modules live in <code>src/frontend/js/modules/</code>. Some must be called inside <code>DOMContentLoaded</code> as they interact with the DOM; others create elements dynamically and can be called anywhere.</p>
234
- <h4>Call inside <code>DOMContentLoaded</code></h4>
250
+
251
+ <h3>Call inside <code>DOMContentLoaded</code></h3>
235
252
  <table>
236
253
  <thead>
237
254
  <tr><th>Module</th><th>Function</th></tr>
@@ -243,7 +260,8 @@ showNotification("Page loaded", "success", 3000);</code></pre>
243
260
  <tr><td><code>modules/forms/normalizePhoneNumber.js</code></td><td><code>initNormalizePhoneNumber()</code></td></tr>
244
261
  </tbody>
245
262
  </table>
246
- <h4>Call anywhere</h4>
263
+
264
+ <h3>Call anywhere</h3>
247
265
  <table>
248
266
  <thead>
249
267
  <tr><th>Module</th><th>Function</th></tr>
@@ -252,7 +270,8 @@ showNotification("Page loaded", "success", 3000);</code></pre>
252
270
  <tr><td><code>modules/notification.js</code></td><td><code>showNotification(text, type, duration)</code></td></tr>
253
271
  </tbody>
254
272
  </table>
255
- <h4><code>showNotification</code> parameters</h4>
273
+
274
+ <h3><code>showNotification</code> parameters</h3>
256
275
  <table>
257
276
  <thead>
258
277
  <tr><th>Parameter</th><th>Type</th><th>Default</th><th>Values</th></tr>
@@ -263,18 +282,24 @@ showNotification("Page loaded", "success", 3000);</code></pre>
263
282
  <tr><td><code>duration</code></td><td>number</td><td><code>5000</code></td><td>ms, or <code>-1</code> for persistent with spinner</td></tr>
264
283
  </tbody>
265
284
  </table>
266
- <h3>Adding a module</h3>
285
+
286
+ <h2>Adding a module</h2>
267
287
  <p>Create a new <code>.js</code> file in <code>src/frontend/js/modules/</code>. You can organize them into subfolders freely.</p>
268
288
  <p>Use ESM syntax — esbuild handles the bundling:</p>
269
- <pre><code>// _yourModule.js
270
- export function yourFunction() {
271
- // ...
272
- }</code></pre>
289
+
290
+ <pre><code class="language-js">// _yourModule.js
291
+ export function yourFunction() {
292
+ // ...
293
+ }</code></pre>
294
+
273
295
  <p>Then import it in the pages that need it:</p>
274
- <pre><code>import { yourFunction } from '../modules/yourModule.js';</code></pre>
296
+
297
+ <pre><code class="language-js">import { yourFunction } from '../modules/yourModule.js';</code></pre>
298
+
275
299
  <blockquote>⚠️ Files inside <code>_tools/</code> run directly in Node.js without a bundler — use CommonJS (<code>require</code> / <code>module.exports</code>) there, not ESM.</blockquote>
276
300
  </div>
277
301
  </div>
302
+ </div>
278
303
 
279
304
  <div id="content-creating-pages" class="tab-content">
280
305
  <div class="markdown-body">
@@ -375,39 +400,45 @@ export function yourFunction() {
375
400
  <div class="markdown-body">
376
401
  <h2>Head & SEO</h2>
377
402
  <p>This json holds global settings used across all pages in <code>base.njk</code> and other components:</p>
403
+
378
404
  <h3>site.json <small>(<code>src/frontend/data/</code>)</small></h3>
379
- <pre><code>{
380
- "site_name": "Site name",
381
- "title": "Site title",
382
- "description": "Site description",
383
- "keywords": "keyword1, keyword2, keyword3",
384
- "domain": "yoursite.com",
385
- "url": "https://yoursite.com",
386
- "lang": "en",
387
- "author": "Name and surname",
388
- "data_bs_theme": "dark",
389
- "favicon": "/assets/brand/favicon.svg",
390
- "logo": "/assets/brand/logo.svg"
391
- }</code></pre>
405
+ <pre><code>"site_name": "Site name",
406
+ "title": "Site title",
407
+ "description": "Site description",
408
+ "keywords": "keyword1, keyword2, keyword3",
409
+ "domain": "yoursite.com",
410
+ "url": "https://yoursite.com",
411
+ "lang": "en",
412
+ "author": "Name and surname",
413
+ "data_bs_theme": "dark",
414
+ "favicon": "/assets/brand/favicon.svg",
415
+ "logo": "/assets/brand/logo.svg",
416
+ ...</code></pre>
417
+
392
418
  <h2>Per-page SEO and CDN</h2>
393
- <p>Each page entry is keyed by its camelCase <code>title</code> from the front matter.</p>
419
+ <p>Each page entry is keyed by its camelCase <code>title</code> from the front matter:</p>
394
420
  <p>If you don't want to use a particular CDN inserting it in <code>base.njk</code> for all pages, you can add extra specific CDN (CSS, JS) by inserting the link in each page of <code>site.json</code> separating them with a <code>,</code> and setting them in <code>""</code>.</p>
421
+
395
422
  <h3>site.json <small>(<code>src/frontend/data/</code>)</small></h3>
396
423
  <pre><code>"pages": {
397
- "examplePage": {
398
- "seo": {
399
- "title": "Example Page",
400
- "description": "description"
401
- },
402
- "cdn": {
403
- // You can leave the [] empty
404
- "css": ["https://example1.com/lib.min.css", "https://example2.com/lib.min.css"],
405
- "js": ["https://example1.com/lib.min.js", "https://example2.com/lib.min.js"]
406
- }
407
- }
408
- }</code></pre>
424
+ ...
425
+ "examplePage": {
426
+ "seo": {
427
+ "title": "Example Page",
428
+ "description": "description"
429
+ },
430
+ "cdn": {
431
+ // You can leave the [] empty
432
+ "css": ["https://example1.com/lib.min.css", "https://example2.com/lib.min.css"],
433
+ "js": ["https://example1.com/lib.min.js", "https://example2.com/lib.min.js"]
434
+ }
435
+ }
436
+ ...
437
+ }</code></pre>
438
+
409
439
  <h2>AI & SEO bots</h2>
410
440
  <p><code>llms.txt</code> and <code>robots.txt</code> are generated automatically from <code>site.json</code> via their respective <code>.njk</code> files — no manual editing needed.</p>
441
+
411
442
  <table>
412
443
  <thead>
413
444
  <tr><th>File</th><th>Purpose</th><th>Reachable at</th></tr>
@@ -417,7 +448,29 @@ export function yourFunction() {
417
448
  <tr><td><code>robots.njk</code></td><td>Controls search engine crawling</td><td><code>yoursite.com/robots.txt</code></td></tr>
418
449
  </tbody>
419
450
  </table>
451
+
420
452
  <p>To customize them, edit <code>src/llms.njk</code> or <code>src/robots.njk</code> directly.</p>
453
+
454
+ <h3>Customizing llms.txt</h3>
455
+ <p><code>src/llms.njk</code> ships with a base template — <strong>replace the placeholders with your own content</strong>:</p>
456
+
457
+ <pre><code># Site name
458
+
459
+ > Site description
460
+
461
+ Built by Name and surname — https://yoursite.com
462
+
463
+ ## Pages
464
+
465
+ - https://yoursite.com: Homepage
466
+
467
+ ## Notes
468
+
469
+ - Language: en
470
+ - All content may be used for AI indexing unless otherwise stated</code></pre>
471
+
472
+ <blockquote>The more accurate and detailed your <code>llms.txt</code>, the better AI models will understand and reference your site.</blockquote>
473
+
421
474
  <h2>Configuration field description</h2>
422
475
  <table>
423
476
  <thead>
@@ -516,7 +569,7 @@ Response::success(['visits' => 1024]);</code></pre>
516
569
  'admin/stats' => 'secret-stats-key',
517
570
  ],</code></pre>
518
571
  <h3>The Response helper</h3>
519
- <pre><code>Response::success($data, $code); // default 200
572
+ <pre><code>Response::success($data, $code); // default 200
520
573
  Response::error($message, $code, $details); // default 400
521
574
  Response::noContent(); // 204</code></pre>
522
575
  <h3>Handling multiple methods</h3>
@@ -611,6 +664,20 @@ const res = await fetch('/api/posts', {
611
664
  </div>
612
665
 
613
666
  <style>
667
+ /* Custom Replacement Classes for Bootstrap */
668
+ .layout-container {
669
+ width: 100%;
670
+ max-width: 1140px;
671
+ margin-right: auto;
672
+ margin-left: auto;
673
+ padding-right: 15px;
674
+ padding-left: 15px;
675
+ }
676
+ .layout-my-3 {
677
+ margin-top: 1rem;
678
+ margin-bottom: 1rem;
679
+ }
680
+
614
681
  h1 {
615
682
  text-align: center;
616
683
  font-weight: 400;
@@ -659,7 +726,8 @@ const res = await fetch('/api/posts', {
659
726
  transform: translateY(-2px);
660
727
  }
661
728
  .card-icon {
662
- font-size: 2rem;
729
+ width: 2rem;
730
+ height: 2rem;
663
731
  color: #42b883;
664
732
  }
665
733
  .card h3 {
@@ -824,6 +892,7 @@ const res = await fetch('/api/posts', {
824
892
  }
825
893
  });
826
894
  });
895
+
827
896
  document.querySelector('.nav-to-assistant').addEventListener('click', (e) => {
828
897
  e.preventDefault();
829
898
 
@@ -834,7 +903,8 @@ const res = await fetch('/api/posts', {
834
903
  assistantRadio.dispatchEvent(new Event('change'));
835
904
  }
836
905
  });
837
- document.querySelector('.nav-to-head-seo').addEventListener('click', (e) => {
906
+
907
+ document.querySelector('.nav-to-head-seo').addEventListener('click', (e) => {
838
908
  e.preventDefault();
839
909
 
840
910
  const headAndSeoRadio = document.querySelector('input[name="guide-filter"][value="head-seo"]');
@@ -18,6 +18,11 @@
18
18
 
19
19
  // Add any custom rule specific to this page below
20
20
  // These rules override the framework and module styles
21
- // body {
22
- // background-color: root.$primary;
23
- // }
21
+ .center {
22
+ display: flex;
23
+ flex-direction: column;
24
+ justify-content: center;
25
+ align-items: center;
26
+ min-height: 60vh;
27
+ text-align: center;
28
+ }