nibula 1.6.0 → 1.6.2

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.
Files changed (99) hide show
  1. package/.eleventy.js +82 -81
  2. package/CHANGELOG.md +395 -377
  3. package/LICENSE +169 -169
  4. package/NOTICE +4 -4
  5. package/README.md +110 -110
  6. package/bin/create.js +540 -505
  7. package/bin/delegate.js +58 -57
  8. package/bin/nibula.js +302 -302
  9. package/docs/Assistant CLI.md +65 -65
  10. package/docs/Backend.md +201 -201
  11. package/docs/Components.md +140 -140
  12. package/docs/Creating pages.md +69 -69
  13. package/docs/Deploy.md +210 -210
  14. package/docs/Head and SEO.md +139 -139
  15. package/docs/Javascript.md +126 -126
  16. package/docs/Styling with SCSS.md +141 -139
  17. package/nginx.conf +113 -113
  18. package/package.json +69 -69
  19. package/src/backend/.htaccess +6 -6
  20. package/src/backend/_core/composer.json +5 -5
  21. package/src/backend/_core/composer.lock +492 -492
  22. package/src/backend/_core/index.js +267 -267
  23. package/src/backend/_core/index.php +160 -160
  24. package/src/backend/_core/init.js +52 -52
  25. package/src/backend/_core/init.php +33 -33
  26. package/src/backend/_core/modules/RateLimiter.js +58 -58
  27. package/src/backend/_core/modules/RateLimiter.php +30 -30
  28. package/src/backend/_core/modules/Response.js +59 -59
  29. package/src/backend/_core/modules/Response.php +48 -48
  30. package/src/backend/api/protected/example-protected.js +23 -23
  31. package/src/backend/api/protected/example-protected.php +16 -16
  32. package/src/backend/api/public/example-public.js +24 -24
  33. package/src/backend/api/public/example-public.php +16 -16
  34. package/src/backend/database/Database.js +46 -46
  35. package/src/backend/database/Database.php +23 -23
  36. package/src/backend/example.config.js +37 -37
  37. package/src/backend/example.config.php +37 -37
  38. package/src/backend/package.json +17 -17
  39. package/src/backend/web.config +43 -43
  40. package/src/frontend/.htaccess +51 -51
  41. package/src/frontend/404.njk +17 -17
  42. package/src/frontend/assets/brand/favicon.svg +54 -54
  43. package/src/frontend/assets/brand/logo.svg +54 -54
  44. package/src/frontend/components/global/footer.njk +25 -25
  45. package/src/frontend/components/global/header.njk +6 -6
  46. package/src/frontend/components/welcome.njk +115 -115
  47. package/src/frontend/index.njk +8 -8
  48. package/src/frontend/js/global.js +10 -10
  49. package/src/frontend/js/modules/exampleModule.js +2 -2
  50. package/src/frontend/js/pages/404.js +8 -8
  51. package/src/frontend/js/pages/homepage.js +8 -8
  52. package/src/frontend/layouts/base.njk +135 -136
  53. package/src/frontend/layouts/page-components.njk +13 -13
  54. package/src/frontend/llms.njk +17 -17
  55. package/src/frontend/robots.njk +7 -7
  56. package/src/frontend/scss/_global.scss +46 -46
  57. package/src/frontend/scss/_root.scss +13 -13
  58. package/src/frontend/scss/modules/_animations.scss +24 -24
  59. package/src/frontend/scss/modules/_footer.scss +28 -28
  60. package/src/frontend/scss/modules/_header.scss +26 -26
  61. package/src/frontend/scss/modules/_mobile.scss +29 -29
  62. package/src/frontend/scss/modules/_typography.scss +18 -18
  63. package/src/frontend/scss/modules/frameworks/_bootstrap.scss +109 -109
  64. package/src/frontend/scss/modules/frameworks/_bulma.scss +108 -108
  65. package/src/frontend/scss/modules/frameworks/_foundation.scss +137 -137
  66. package/src/frontend/scss/modules/frameworks/_uikit.scss +109 -109
  67. package/src/frontend/scss/pages/404.scss +27 -27
  68. package/src/frontend/scss/pages/homepage.scss +22 -22
  69. package/src/frontend/sitemap.njk +17 -17
  70. package/src/frontend/ts/global.ts +11 -11
  71. package/src/frontend/ts/modules/exampleModule.ts +2 -2
  72. package/src/frontend/ts/pages/404.ts +8 -8
  73. package/src/frontend/ts/pages/homepage.ts +8 -8
  74. package/src/frontend/web.config +51 -51
  75. package/tools/assistant.js +122 -122
  76. package/tools/buildJs.js +39 -39
  77. package/tools/cleanOutput.js +23 -23
  78. package/tools/cli/prompt.js +40 -40
  79. package/tools/cli/ui.js +87 -87
  80. package/tools/config/messages.json +80 -80
  81. package/tools/config/settings.json +130 -130
  82. package/tools/lib/colors.js +17 -17
  83. package/tools/lib/files.js +75 -75
  84. package/tools/lib/logger.js +22 -22
  85. package/tools/lib/outputPath.js +141 -141
  86. package/tools/lib/pageActions.js +146 -146
  87. package/tools/lib/pageArtifacts.js +46 -46
  88. package/tools/lib/pageComponents.js +88 -88
  89. package/tools/lib/pagesData.js +87 -87
  90. package/tools/lib/paths.js +63 -63
  91. package/tools/lib/project.js +54 -54
  92. package/tools/lib/text.js +47 -47
  93. package/tools/lib/validation.js +40 -40
  94. package/tools/res/templates/template.js +7 -7
  95. package/tools/res/templates/template.njk +8 -8
  96. package/tools/res/templates/template.scss +22 -22
  97. package/tools/res/templates/template.ts +8 -8
  98. package/tsconfig.json +24 -24
  99. package/src/frontend/scss/pages/prova.scss +0 -23
@@ -1,116 +1,116 @@
1
- <div class="layout-container layout-my-3">
2
- <h1>Welcome to <span class="boilerplate-name">Nibula</span></h1>
3
- <div class="slogan">The boilerplate you need, simplified</div>
4
-
5
- <div id="content-welcome" class="tab-content active">
6
- <div style="display: flex; gap: 1rem; flex-wrap: wrap;">
7
- <a href="https://github.com/rhaastrake/nibula" class="card" target="_blank" rel="noopener noreferrer" style="flex: 1; min-width: 250px;">
8
- <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">
9
- <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>
10
- </svg>
11
- <h3>GitHub repository</h3>
12
- <p>Explore the project and suggest improvements</p>
13
- <span class="card-link">Open the repository
14
- <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;">
15
- <path stroke-linecap="round" stroke-linejoin="round" d="M14 5l7 7m0 0l-7 7m7-7H3"></path>
16
- </svg>
17
- </span>
18
- </a>
19
- <a href="https://github.com/rhaastrake/nibula/tree/main/docs" class="card" target="_blank" rel="noopener noreferrer" style="flex: 1; min-width: 250px;">
20
- <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">
21
- <path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"></path>
22
- <path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"></path>
23
- </svg>
24
- <h3>Documentation</h3>
25
- <p>Everything you need to get started</p>
26
- <span class="card-link">Go to documentation
27
- <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;">
28
- <path stroke-linecap="round" stroke-linejoin="round" d="M14 5l7 7m0 0l-7 7m7-7H3"></path>
29
- </svg>
30
- </span>
31
- </a>
32
- </div>
33
- </div>
34
- <div class="prova">{{ test.prova }}</div>
35
- <style>
36
- .layout-container {
37
- width: 100%;
38
- max-width: 1140px;
39
- margin-right: auto;
40
- margin-left: auto;
41
- padding-right: 15px;
42
- padding-left: 15px;
43
- }
44
- .layout-my-3 {
45
- margin-top: 1rem;
46
- margin-bottom: 1rem;
47
- }
48
-
49
- h1 {
50
- text-align: center;
51
- font-weight: 400;
52
- .boilerplate-name {
53
- color: #42b883;
54
- font-weight: 600;
55
- }
56
- }
57
-
58
- .slogan {
59
- text-align: center;
60
- color: #4b71a0;
61
- font-size: 1.5rem;
62
- margin-bottom: 1rem;
63
- }
64
-
65
- .card {
66
- background: #11474b;
67
- border: 0.8px solid #1e2d4a;
68
- border-radius: 10px;
69
- padding: 18px;
70
- display: flex;
71
- flex-direction: column;
72
- gap: 0.75rem;
73
- margin-top: 1rem;
74
- margin-bottom: 1rem;
75
- transition: border-color 0.2s, transform 0.15s;
76
- text-decoration: none;
77
- h3 {
78
- font-size: 1.5rem;
79
- font-weight: 600;
80
- color: #cbd5e1;
81
- letter-spacing: 0.01em;
82
- margin: 0;
83
- }
84
- p {
85
- font-size: 1rem;
86
- color: #5d8dc7;
87
- line-height: 1.6;
88
- margin: 0;
89
- }
90
- }
91
-
92
- .card:hover {
93
- border-color: #42b883;
94
- transform: translateY(-2px);
95
- .card-link {
96
- opacity: 1;
97
- }
98
- }
99
-
100
- .card-icon {
101
- width: 2rem;
102
- height: 2rem;
103
- color: #42b883;
104
- }
105
-
106
- .card-link {
107
- display: flex;
108
- align-items: center;
109
- gap: 4px;
110
- font-size: 1rem;
111
- color: #42b883;
112
- opacity: 0.8;
113
- transition: opacity 0.15s;
114
- margin-top: auto;
115
- }
1
+ <div class="layout-container layout-my-3">
2
+ <h1>Welcome to <span class="boilerplate-name">Nibula</span></h1>
3
+ <div class="slogan">The boilerplate you need, simplified</div>
4
+
5
+ <div id="content-welcome" class="tab-content active">
6
+ <div style="display: flex; gap: 1rem; flex-wrap: wrap;">
7
+ <a href="https://github.com/rhaastrake/nibula" class="card" target="_blank" rel="noopener noreferrer" style="flex: 1; min-width: 250px;">
8
+ <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">
9
+ <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>
10
+ </svg>
11
+ <h3>GitHub repository</h3>
12
+ <p>Explore the project and suggest improvements</p>
13
+ <span class="card-link">Open the repository
14
+ <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;">
15
+ <path stroke-linecap="round" stroke-linejoin="round" d="M14 5l7 7m0 0l-7 7m7-7H3"></path>
16
+ </svg>
17
+ </span>
18
+ </a>
19
+ <a href="https://github.com/rhaastrake/nibula/tree/main/docs" class="card" target="_blank" rel="noopener noreferrer" style="flex: 1; min-width: 250px;">
20
+ <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">
21
+ <path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"></path>
22
+ <path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"></path>
23
+ </svg>
24
+ <h3>Documentation</h3>
25
+ <p>Everything you need to get started</p>
26
+ <span class="card-link">Go to documentation
27
+ <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;">
28
+ <path stroke-linecap="round" stroke-linejoin="round" d="M14 5l7 7m0 0l-7 7m7-7H3"></path>
29
+ </svg>
30
+ </span>
31
+ </a>
32
+ </div>
33
+ </div>
34
+ <div class="prova">{{ test.prova }}</div>
35
+ <style>
36
+ .layout-container {
37
+ width: 100%;
38
+ max-width: 1140px;
39
+ margin-right: auto;
40
+ margin-left: auto;
41
+ padding-right: 15px;
42
+ padding-left: 15px;
43
+ }
44
+ .layout-my-3 {
45
+ margin-top: 1rem;
46
+ margin-bottom: 1rem;
47
+ }
48
+
49
+ h1 {
50
+ text-align: center;
51
+ font-weight: 400;
52
+ .boilerplate-name {
53
+ color: #42b883;
54
+ font-weight: 600;
55
+ }
56
+ }
57
+
58
+ .slogan {
59
+ text-align: center;
60
+ color: #4b71a0;
61
+ font-size: 1.5rem;
62
+ margin-bottom: 1rem;
63
+ }
64
+
65
+ .card {
66
+ background: #11474b;
67
+ border: 0.8px solid #1e2d4a;
68
+ border-radius: 10px;
69
+ padding: 18px;
70
+ display: flex;
71
+ flex-direction: column;
72
+ gap: 0.75rem;
73
+ margin-top: 1rem;
74
+ margin-bottom: 1rem;
75
+ transition: border-color 0.2s, transform 0.15s;
76
+ text-decoration: none;
77
+ h3 {
78
+ font-size: 1.5rem;
79
+ font-weight: 600;
80
+ color: #cbd5e1;
81
+ letter-spacing: 0.01em;
82
+ margin: 0;
83
+ }
84
+ p {
85
+ font-size: 1rem;
86
+ color: #5d8dc7;
87
+ line-height: 1.6;
88
+ margin: 0;
89
+ }
90
+ }
91
+
92
+ .card:hover {
93
+ border-color: #42b883;
94
+ transform: translateY(-2px);
95
+ .card-link {
96
+ opacity: 1;
97
+ }
98
+ }
99
+
100
+ .card-icon {
101
+ width: 2rem;
102
+ height: 2rem;
103
+ color: #42b883;
104
+ }
105
+
106
+ .card-link {
107
+ display: flex;
108
+ align-items: center;
109
+ gap: 4px;
110
+ font-size: 1rem;
111
+ color: #42b883;
112
+ opacity: 0.8;
113
+ transition: opacity 0.15s;
114
+ margin-top: auto;
115
+ }
116
116
  </style>
@@ -1,9 +1,9 @@
1
- ---
2
- title: homepage
3
- permalink: /
4
- layout: page-components.njk
5
- ---
6
-
7
- <!-- !IMPORTANT -->
8
- <!-- DO NOT ADD ANYTHING HERE -->
1
+ ---
2
+ title: homepage
3
+ permalink: /
4
+ layout: page-components.njk
5
+ ---
6
+
7
+ <!-- !IMPORTANT -->
8
+ <!-- DO NOT ADD ANYTHING HERE -->
9
9
  <!-- You should create a new component.njk into src/components and include that in layouts/page-components.njk -->
@@ -1,11 +1,11 @@
1
- // import { initExampleModule } from "./modules/exampleModule.js";
2
-
3
- function initGlobal() {
4
- // initExampleModule();
5
- }
6
-
7
- // Global logic here
8
-
9
- // Do not touch
10
- // This instruction starts global.js itself
1
+ // import { initExampleModule } from "./modules/exampleModule.js";
2
+
3
+ function initGlobal() {
4
+ // initExampleModule();
5
+ }
6
+
7
+ // Global logic here
8
+
9
+ // Do not touch
10
+ // This instruction starts global.js itself
11
11
  document.addEventListener('DOMContentLoaded', initGlobal);
@@ -1,3 +1,3 @@
1
- export function initExampleModule() {
2
- // Module logic here
1
+ export function initExampleModule() {
2
+ // Module logic here
3
3
  }
@@ -1,9 +1,9 @@
1
- import '../global.js';
2
-
3
- // import { initExampleModule } from '../modules/exampleModule';
4
-
5
- document.addEventListener("DOMContentLoaded", () => {
6
- // initExampleModule();
7
- });
8
-
1
+ import '../global.js';
2
+
3
+ // import { initExampleModule } from '../modules/exampleModule';
4
+
5
+ document.addEventListener("DOMContentLoaded", () => {
6
+ // initExampleModule();
7
+ });
8
+
9
9
  // Page logic here
@@ -1,9 +1,9 @@
1
- import '../global.js';
2
-
3
- // import { initExampleModule } from '../modules/exampleModule';
4
-
5
- document.addEventListener("DOMContentLoaded", () => {
6
- // initExampleModule();
7
- });
8
-
1
+ import '../global.js';
2
+
3
+ // import { initExampleModule } from '../modules/exampleModule';
4
+
5
+ document.addEventListener("DOMContentLoaded", () => {
6
+ // initExampleModule();
7
+ });
8
+
9
9
  // Page logic here
@@ -1,137 +1,136 @@
1
- <!-- This is the base of every page that you will create -->
2
-
3
- <!DOCTYPE html>
4
- <html lang="{{ site.lang or 'en' }}" data-bs-theme="{{ site.theme or 'light' }}">
5
-
6
- <head>
7
- <meta charset="UTF-8">
8
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
9
- <meta name="color-scheme" content="{{ site.theme or 'light' }}">
10
-
11
- {#╔══════════════════╗
12
- ║ PAGE DATA ║
13
- ╚══════════════════╝#}
14
-
15
- {% set pageKey = title %}
16
- {% set pageData = pages[pageKey] %}
17
-
18
- {#╔══════════════════╗
19
- ║ SEO ║
20
- ╚══════════════════╝#}
21
-
22
- <link rel="canonical" href="{{ pageData.seo.canonical or (site.url + page.url) }}"/>
23
- <title>{{ pageData.seo.title or title or site.title }}</title>
24
- <meta name="description" content="{{ pageData.seo.description or site.description }}">
25
- <meta name="keywords" content="{{ pageData.seo.keywords or site.keywords }}">
26
- <meta name="author" content="{{ site.author }}">
27
- <meta name="theme-color" content="{{ site.theme }}">
28
-
29
- {% if pageData.seo.noindex %}
30
- <meta name="robots" content="noindex, follow">
31
- {% endif %}
32
-
33
- {# Open Graph #}
34
- <meta property="og:title" content="{{ pageData.seo.title }}">
35
- <meta property="og:description" content="{{ pageData.seo.description }}">
36
- <meta property="og:type" content="website">
37
- <meta property="og:url" content="{{ site.url }}{{ page.url }}">
38
- <meta property="og:image" content="{{ site.url }}{{ site.logo }}">
39
- <meta property="og:site_name" content="{{ site.site_name }}">
40
-
41
- {# Twitter Card #}
42
- <meta name="twitter:card" content="summary_large_image">
43
- <meta name="twitter:title" content="{{ pageData.seo.title }}">
44
- <meta name="twitter:description" content="{{ pageData.seo.description }}">
45
- <meta name="twitter:image" content="{{ site.url }}{{ site.logo }}">
46
-
47
- {#╔══════════════════════════════════════════╗
48
- ║ JSON-LD — Structured data for SEO/AI ║
49
- ╚══════════════════════════════════════════╝#}
50
-
51
- <script type="application/ld+json">
52
- {
53
- "@context": "https://schema.org",
54
- "@type": "WebPage",
55
- "name": {{ (pageData.seo.title or title or site.title) | dump }},
56
- "description": {{ (pageData.seo.description or site.description) | dump }},
57
- "url": {{ (site.url + page.url) | dump }},
58
- "author": {
59
- "@type": "Person",
60
- "name": {{ site.author | dump }}
61
- },
62
- "publisher": {
63
- "@type": "Organization",
64
- "name": {{ site.site_name | dump }},
65
- "logo": {
66
- "@type": "ImageObject",
67
- "url": {{ (site.url + site.logo) | dump }}
68
- }
69
- }
70
- }
71
- </script>
72
-
73
- {#╔══════════════════╗
74
- ║ FAVICON ║
75
- ╚══════════════════╝#}
76
-
77
- <link rel="icon" type="image/svg+xml" href="{{ '/assets/brand/favicon.svg' | url }}">
78
- <link rel="icon" type="image/png" sizes="32x32" href="{{ '/assets/brand/favicon-32.png' | url }}">
79
- <link rel="apple-touch-icon" sizes="180x180" href="{{ '/assets/brand/apple-touch-icon.png' | url }}">
80
-
81
- {#╔══════════════════╗
82
- ║ CSS ║
83
- ╚══════════════════╝#}
84
-
85
- {# Page CSS #}
86
- <link rel="stylesheet" href="{{ ('/css/pages/' + title + '.css') | url }}"/>
87
-
88
- {# Per-page CDN CSS #}
89
- {% if pageData and pageData.cdn and pageData.cdn.css %}
90
- {% for link in pageData.cdn.css %}
91
- <link rel="stylesheet" href="{{ link }}">
92
- {% endfor %}
93
- {% endif %}
94
-
95
- </head>
96
-
97
- <body>
98
-
99
- {% include "global/header.njk" %}
100
-
101
- <main>
102
- {{ content | safe }}
103
- </main>
104
-
105
- {% include "global/footer.njk" %}
106
-
107
- {#╔══════════════════╗
108
- ║ SCRIPTS ║
109
- ╚══════════════════╝#}
110
-
111
- {# Framework — uncomment the one you are using #}
112
-
113
- {# Bootstrap JS #}
114
- <script src="{{ '/js/bootstrap.bundle.min.js' | url }}" defer></script>
115
-
116
- {# Foundation JS #}
117
- {# <script src="{{ '/js/foundation.min.js' | url }}" defer></script> #}
118
-
119
- {# UIkit JS #}
120
- {# <script src="{{ '/js/uikit.min.js' | url }}" defer></script> #}
121
- {# <script src="{{ '/js/uikit-icons.min.js' | url }}" defer></script> #}
122
-
123
- {# Bulma — no JS needed #}
124
-
125
- {# Per-page CDN JS #}
126
- {% if pageData and pageData.cdn and pageData.cdn.js %}
127
- {% for script in pageData.cdn.js %}
128
- <script src="{{ script }}" defer></script>
129
- {% endfor %}
130
- {% endif %}
131
-
132
- {# Page JS #}
133
- <script src="{{ ('/js/pages/' + title + '.js') | url }}"></script>
134
-
135
- </body>
136
-
1
+ <!-- This is the base of every page that you will create -->
2
+
3
+ <!DOCTYPE html>
4
+ <html lang="{{ site.lang or 'en' }}" data-theme="{{ site.theme or 'light' }}" data-bs-theme="{{ site.theme or 'light' }}">
5
+
6
+ <head>
7
+ <meta charset="UTF-8">
8
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
9
+ <meta name="color-scheme" content="{{ site.theme or 'light' }}">
10
+
11
+ {#╔══════════════════╗
12
+ ║ PAGE DATA ║
13
+ ╚══════════════════╝#}
14
+
15
+ {% set pageKey = title %}
16
+ {% set pageData = pages[pageKey] %}
17
+
18
+ {#╔══════════════════╗
19
+ ║ SEO ║
20
+ ╚══════════════════╝#}
21
+
22
+ <link rel="canonical" href="{{ pageData.seo.canonical or (site.url + page.url) }}"/>
23
+ <title>{{ pageData.seo.title or title or site.title }}</title>
24
+ <meta name="description" content="{{ pageData.seo.description or site.description }}">
25
+ <meta name="keywords" content="{{ pageData.seo.keywords or site.keywords }}">
26
+ <meta name="author" content="{{ site.author }}">
27
+ <meta name="theme-color" content="{{ site.theme }}">
28
+
29
+ {% if pageData.seo.noindex %}
30
+ <meta name="robots" content="noindex, follow">
31
+ {% endif %}
32
+
33
+ {# Open Graph #}
34
+ <meta property="og:title" content="{{ pageData.seo.title or title or site.title }}">
35
+ <meta property="og:description" content="{{ pageData.seo.description or site.description }}">
36
+ <meta property="og:type" content="website">
37
+ <meta property="og:url" content="{{ site.url }}{{ page.url }}">
38
+ <meta property="og:image" content="{{ site.url }}{{ site.logo }}">
39
+ <meta property="og:site_name" content="{{ site.site_name }}">
40
+
41
+ {# Twitter Card #}
42
+ <meta name="twitter:card" content="summary_large_image">
43
+ <meta name="twitter:title" content="{{ pageData.seo.title or title or site.title }}">
44
+ <meta name="twitter:description" content="{{ pageData.seo.description or site.description }}">
45
+ <meta name="twitter:image" content="{{ site.url }}{{ site.logo }}">
46
+
47
+ {#╔══════════════════════════════════════════╗
48
+ ║ JSON-LD — Structured data for SEO/AI ║
49
+ ╚══════════════════════════════════════════╝#}
50
+
51
+ <script type="application/ld+json">
52
+ {
53
+ "@context": "https://schema.org",
54
+ "@type": "WebPage",
55
+ "name": {{ (pageData.seo.title or title or site.title) | dump }},
56
+ "description": {{ (pageData.seo.description or site.description) | dump }},
57
+ "url": {{ (site.url + page.url) | dump }},
58
+ "author": {
59
+ "@type": "Person",
60
+ "name": {{ site.author | dump }}
61
+ },
62
+ "publisher": {
63
+ "@type": "Organization",
64
+ "name": {{ site.site_name | dump }},
65
+ "logo": {
66
+ "@type": "ImageObject",
67
+ "url": {{ (site.url + site.logo) | dump }}
68
+ }
69
+ }
70
+ }
71
+ </script>
72
+
73
+ {#╔══════════════════╗
74
+ ║ FAVICON ║
75
+ ╚══════════════════╝#}
76
+
77
+ <link rel="icon" type="image/svg+xml" href="{{ '/assets/brand/favicon.svg' | url }}">
78
+ <link rel="icon" type="image/png" sizes="32x32" href="{{ '/assets/brand/favicon-32.png' | url }}">
79
+ <link rel="apple-touch-icon" sizes="180x180" href="{{ '/assets/brand/apple-touch-icon.png' | url }}">
80
+
81
+ {#╔══════════════════╗
82
+ ║ CSS ║
83
+ ╚══════════════════╝#}
84
+
85
+ {# Page CSS #}
86
+ <link rel="stylesheet" href="{{ ('/css/pages/' + title + '.css') | url }}"/>
87
+
88
+ {# Per-page CDN CSS #}
89
+ {% if pageData and pageData.cdn and pageData.cdn.css %}
90
+ {% for link in pageData.cdn.css %}
91
+ <link rel="stylesheet" href="{{ link }}">
92
+ {% endfor %}
93
+ {% endif %}
94
+
95
+ </head>
96
+
97
+ <body>
98
+
99
+ {% include "global/header.njk" %}
100
+
101
+ <main>
102
+ {{ content | safe }}
103
+ </main>
104
+
105
+ {% include "global/footer.njk" %}
106
+
107
+ {#╔══════════════════╗
108
+ ║ SCRIPTS ║
109
+ ╚══════════════════╝#}
110
+
111
+ {# Framework — uncomment the one you are using #}
112
+ {# Bootstrap JS #}
113
+ <script src="{{ '/js/bootstrap.bundle.min.js' | url }}" defer></script>
114
+
115
+ {# Foundation JS #}
116
+ {# <script src="{{ '/js/foundation.min.js' | url }}" defer></script> #}
117
+
118
+ {# UIkit JS #}
119
+ {# <script src="{{ '/js/uikit.min.js' | url }}" defer></script> #}
120
+ {# <script src="{{ '/js/uikit-icons.min.js' | url }}" defer></script> #}
121
+
122
+ {# Bulma — no JS needed #}
123
+
124
+ {# Per-page CDN JS #}
125
+ {% if pageData and pageData.cdn and pageData.cdn.js %}
126
+ {% for script in pageData.cdn.js %}
127
+ <script src="{{ script }}" defer></script>
128
+ {% endfor %}
129
+ {% endif %}
130
+
131
+ {# Page JS #}
132
+ <script src="{{ ('/js/pages/' + title + '.js') | url }}"></script>
133
+
134
+ </body>
135
+
137
136
  </html>
@@ -1,14 +1,14 @@
1
- ---
2
- layout: base.njk
3
- ---
4
-
5
- <!-- NJK (HTML) components per page -->
6
-
7
- {% if title == "homepage" %}
8
- {% include "welcome.njk" ignore missing %}
9
-
10
- {% else %}
11
- <!-- You should not touch this else statement -->
12
- {% include "404/_404.njk" %}
13
- {{ content | safe }}
1
+ ---
2
+ layout: base.njk
3
+ ---
4
+
5
+ <!-- NJK (HTML) components per page -->
6
+
7
+ {% if title == "homepage" %}
8
+ {% include "welcome.njk" ignore missing %}
9
+
10
+ {% else %}
11
+ <!-- You should not touch this else statement -->
12
+ {% include "404/_404.njk" %}
13
+ {{ content | safe }}
14
14
  {% endif %}