nibula 1.0.0

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 (81) hide show
  1. package/.eleventy.js +96 -0
  2. package/.eleventyignore +4 -0
  3. package/CHANGELOG.md +0 -0
  4. package/LICENSE +170 -0
  5. package/NOTICE +5 -0
  6. package/README.md +105 -0
  7. package/_tools/assistant.js +152 -0
  8. package/_tools/buildJs.js +37 -0
  9. package/_tools/cleanOutput.js +25 -0
  10. package/_tools/modules/constants.js +66 -0
  11. package/_tools/modules/pageComponents.js +77 -0
  12. package/_tools/modules/updateData.js +90 -0
  13. package/_tools/modules/updateOutputPath.js +112 -0
  14. package/_tools/modules/updatePage.js +162 -0
  15. package/_tools/modules/utils.js +27 -0
  16. package/_tools/modules/validation.js +30 -0
  17. package/_tools/res/templates/template.js +5 -0
  18. package/_tools/res/templates/template.njk +9 -0
  19. package/_tools/res/templates/template.scss +23 -0
  20. package/_tools/res/templates/template.ts +5 -0
  21. package/bin/create.js +407 -0
  22. package/bin/nibula.js +281 -0
  23. package/docs/Assistant CLI.md +66 -0
  24. package/docs/Backend.md +151 -0
  25. package/docs/Components.md +96 -0
  26. package/docs/Creating pages.md +65 -0
  27. package/docs/Deploy.md +101 -0
  28. package/docs/Head and SEO.md +117 -0
  29. package/docs/Javascript.md +53 -0
  30. package/docs/Styling with SCSS.md +136 -0
  31. package/nginx.conf +47 -0
  32. package/nibula-1.0.0.tgz +0 -0
  33. package/package.json +74 -0
  34. package/src/backend/.htaccess +7 -0
  35. package/src/backend/_core/composer.json +5 -0
  36. package/src/backend/_core/composer.lock +492 -0
  37. package/src/backend/_core/index.php +148 -0
  38. package/src/backend/_core/init.php +34 -0
  39. package/src/backend/_core/modules/RateLimiter.php +31 -0
  40. package/src/backend/_core/modules/Response.php +49 -0
  41. package/src/backend/api/protected/example-protected.php +17 -0
  42. package/src/backend/api/public/example-public.php +17 -0
  43. package/src/backend/database/Database.php +24 -0
  44. package/src/backend/database/migrations/create_example_db.sql +1 -0
  45. package/src/backend/example.config.php +28 -0
  46. package/src/backend/web.config +17 -0
  47. package/src/frontend/.htaccess +16 -0
  48. package/src/frontend/404.njk +17 -0
  49. package/src/frontend/assets/brand/favicon.svg +37 -0
  50. package/src/frontend/assets/brand/logo.svg +37 -0
  51. package/src/frontend/components/global/footer.njk +25 -0
  52. package/src/frontend/components/global/header.njk +7 -0
  53. package/src/frontend/components/welcome.njk +116 -0
  54. package/src/frontend/data/site.json +54 -0
  55. package/src/frontend/index.njk +9 -0
  56. package/src/frontend/js/modules/exampleModule.js +3 -0
  57. package/src/frontend/js/pages/404.js +7 -0
  58. package/src/frontend/js/pages/homepage.js +7 -0
  59. package/src/frontend/layouts/base.njk +142 -0
  60. package/src/frontend/layouts/page-components.njk +14 -0
  61. package/src/frontend/llms.njk +18 -0
  62. package/src/frontend/robots.njk +8 -0
  63. package/src/frontend/scss/modules/_animations.scss +25 -0
  64. package/src/frontend/scss/modules/_footer.scss +28 -0
  65. package/src/frontend/scss/modules/_global.scss +44 -0
  66. package/src/frontend/scss/modules/_header.scss +28 -0
  67. package/src/frontend/scss/modules/_mobile.scss +30 -0
  68. package/src/frontend/scss/modules/_root.scss +35 -0
  69. package/src/frontend/scss/modules/_typography.scss +15 -0
  70. package/src/frontend/scss/modules/frameworks/_bootstrap.scss +110 -0
  71. package/src/frontend/scss/modules/frameworks/_bulma.scss +109 -0
  72. package/src/frontend/scss/modules/frameworks/_foundation.scss +139 -0
  73. package/src/frontend/scss/modules/frameworks/_uikit.scss +110 -0
  74. package/src/frontend/scss/pages/404.scss +28 -0
  75. package/src/frontend/scss/pages/homepage.scss +23 -0
  76. package/src/frontend/sitemap.njk +18 -0
  77. package/src/frontend/ts/modules/exampleModule.ts +3 -0
  78. package/src/frontend/ts/pages/404.ts +7 -0
  79. package/src/frontend/ts/pages/homepage.ts +7 -0
  80. package/src/frontend/web.config +27 -0
  81. package/tsconfig.json +25 -0
@@ -0,0 +1,66 @@
1
+ # Assistant CLI
2
+
3
+ > Examples use JavaScript, but everything applies equally to TypeScript. The only difference is the file extension (`.ts` instead of `.js`), that imports do **not** include the extension, and that paths use `src/frontend/ts/` instead of `src/frontend/js/`.
4
+
5
+ An interactive CLI to manage pages without touching files manually.
6
+
7
+ Run it from anywhere inside a project:
8
+
9
+ ```
10
+ nib cli
11
+ ```
12
+
13
+ ## Menu
14
+
15
+ ```
16
+ 1. Create page
17
+ 2. Remove page
18
+ 3. Rename page
19
+ 4. Configure output path
20
+ ```
21
+
22
+ Use `CTRL/CMD + C` (or `CTRL + D`) to exit.
23
+
24
+ ## Create page
25
+
26
+ Enter a page name in any format — the CLI converts it to kebab-case automatically.
27
+
28
+ For a page named `my-page`, the following files are created:
29
+
30
+ | File | Purpose |
31
+ |---|---|
32
+ | `src/frontend/scss/pages/myPage.scss` | SCSS entry point |
33
+ | `src/frontend/js/pages/myPage.js` | JS entry point |
34
+ | `src/frontend/_routes/my-page.njk` | Nunjucks template |
35
+
36
+ It also adds an `elif` block in `page-components.njk` and a stub entry in `site.json`:
37
+
38
+ ```json
39
+ "myPage": {
40
+ "seo": {
41
+ "title": "My page",
42
+ "description": "Description",
43
+ "keywords": "",
44
+ "noindex": false,
45
+ "canonical": ""
46
+ },
47
+ "cdn": {
48
+ "css": [],
49
+ "js": []
50
+ }
51
+ }
52
+ ```
53
+
54
+ ## Remove page
55
+
56
+ Deletes all source files for the page and cleans up the output directory, `page-components.njk`, and `site.json`.
57
+
58
+ ## Rename page
59
+
60
+ Renames all three source files, updates the `elif` block in `page-components.njk`, and renames the record in `site.json` while preserving all existing fields.
61
+
62
+ ## Configure output path
63
+
64
+ Updates the output directory across `.eleventy.js` and all relevant `package.json` scripts in one shot. The old output folder is deleted automatically.
65
+
66
+ > ⚠️ `homepage` and `404` are protected — they cannot be created, removed, or renamed via the CLI.
@@ -0,0 +1,151 @@
1
+ # Backend
2
+
3
+ > **PHP requirement** — the backend runs only on servers with PHP support:
4
+ > - **Apache** (shared hosting, VPS) ✅
5
+ > - **Nginx** + PHP-FPM (VPS) ✅
6
+ > - **IIS** + FastCGI (Windows Server) ✅
7
+ > - **Static hosting** (Netlify, Vercel, GitHub Pages, Cloudflare Pages) ❌
8
+
9
+ The backend is a PHP REST API located in `src/backend/`, copied to the output directory automatically at build time.
10
+
11
+ ## Structure
12
+
13
+ ```
14
+ src/backend/
15
+ ├── api/
16
+ │ └── protected/ # Endpoints requiring X-Api-Key header
17
+ │ ├── public/ # Endpoints accessible without an API key
18
+ ├── _core/ # Framework internals (routing, modules) — do not edit
19
+ │ └── modules/ # Response, RateLimiter, ...
20
+ ├── database/
21
+ │ ├── Database.php
22
+ │ └── migrations/
23
+ ├── example.config.php # Template — versioned, safe to commit
24
+ └── config.php # Your local config, generated on setup — never commit this
25
+ ```
26
+
27
+ > A scaffolded project contains **both** files. `config.php` is generated automatically by `nib new` (a copy of the example) and is git-ignored, so your secrets stay local and are never pushed. `example.config.php` is kept and versioned: it's the file that ends up on GitHub, acting as a secret-free reference so anyone cloning the project knows which keys to set. If `config.php` is ever missing (e.g. after a fresh clone), just copy `example.config.php` to `config.php`.
28
+
29
+ > Note: in the boilerplate repo itself, only `example.config.php` exists — `config.php` is created at scaffold time, not shipped.
30
+
31
+ ## Configuration
32
+
33
+ `config.php` works like a `.env` file — it holds secrets and environment settings that stay local and out of version control.
34
+
35
+ Fill in your values:
36
+
37
+ ### config.php <small>(`src/backend/`)</small>
38
+ ```php
39
+ // Default key for protected endpoints that don't have a specific key in CUSTOM_ENDPOINT_KEYS
40
+ 'GENERAL_API_KEY' => 'DEFAULT_KEY',
41
+
42
+ // If you want restrict access to protected endpoints to specific clients, you can define custom keys for each endpoint
43
+ // For subfolder endpoints, use the relative path ('subfolder/endpoint')
44
+ 'CUSTOM_ENDPOINT_KEYS' => [
45
+ 'subfolder/endpoint' => 'custom-key',
46
+ ],
47
+
48
+ 'GENERAL_ALLOWED_ORIGINS' => [
49
+ '*',
50
+ // 'https://example.com',
51
+ ],
52
+
53
+ 'CUSTOM_ENDPOINT_ORIGINS' => [
54
+ 'subfolder/endpoint' => ['https://app.example.com'],
55
+ ],
56
+
57
+ // Database configuration
58
+ 'DB_HOST' => '127.0.0.1',
59
+ 'DB_NAME' => 'example_db',
60
+ 'DB_USER' => 'root',
61
+ 'DB_PASS' => '',
62
+ ```
63
+
64
+ ### API keys
65
+
66
+ `GENERAL_API_KEY` is the fallback key for all protected endpoints. Use `CUSTOM_ENDPOINT_KEYS` to assign a different key to a specific endpoint — for subfolder endpoints, use the relative path as the key.
67
+
68
+ > ⚠️ The API key travels in the `X-Api-Key` header on every request. Use it only for server-to-server calls over HTTPS. Never embed it in frontend code, where it would be publicly visible.
69
+
70
+ ### CORS (allowed origins)
71
+
72
+ Cross-origin access mirrors the API-key model:
73
+
74
+ - `GENERAL_ALLOWED_ORIGINS` — the default list of origins allowed to call any endpoint from a browser.
75
+ - `CUSTOM_ENDPOINT_ORIGINS` — overrides the default for a specific endpoint, keyed by the same relative path used in `CUSTOM_ENDPOINT_KEYS`.
76
+
77
+ Origins must be exact (scheme + host, no trailing slash), e.g. `https://example.com`. When a request's `Origin` is in the resolved list, it is reflected back in `Access-Control-Allow-Origin` (with `Vary: Origin`). An empty list sends no CORS header — the most restrictive setting; same-origin requests still work. Use `'*'` as the only element to allow any origin (not recommended for protected endpoints).
78
+
79
+ Resolution order for a given endpoint: `CUSTOM_ENDPOINT_ORIGINS[path]` if present, otherwise `GENERAL_ALLOWED_ORIGINS`.
80
+
81
+ ## How routing works
82
+
83
+ The file path inside `api/` maps directly to the URL. Extra URL segments become route parameters available as `$requestParams[]`.
84
+
85
+ Every endpoint file has access to:
86
+
87
+ | Variable | Description |
88
+ |---|---|
89
+ | `$method` | HTTP method (`GET`, `POST`, `PUT`, `PATCH`, `DELETE`) |
90
+ | `$requestParams` | Extra URL segments (e.g. `/api/posts/42` → `['42']`) |
91
+
92
+ ## Creating a public endpoint
93
+
94
+ Create a `.php` file anywhere inside `api/public/`
95
+
96
+ ### api/public/example.php
97
+ ```php
98
+ <?php
99
+ declare(strict_types=1);
100
+
101
+ require_once CORE_PATH . '/modules/Response.php';
102
+
103
+ if ($method !== 'GET') {
104
+ Response::error('Method not allowed', 405);
105
+ }
106
+
107
+ $id = isset($requestParams[0]) ? (int)$requestParams[0] : null;
108
+
109
+ Response::success(['id' => $id]);
110
+ ```
111
+
112
+ ## Creating a protected endpoint
113
+
114
+ Create a `.php` file inside `api/protected/`. The API key check happens automatically before your file runs.
115
+
116
+ ### api/protected/example.php
117
+ ```php
118
+ <?php
119
+ declare(strict_types=1);
120
+
121
+ require_once CORE_PATH . '/modules/Response.php';
122
+
123
+ if ($method !== 'GET') {
124
+ Response::error('Method not allowed', 405);
125
+ }
126
+
127
+ Response::success(['visits' => 1024]);
128
+ ```
129
+
130
+ To assign a dedicated key, add it to `config.php`:
131
+
132
+ ```php
133
+ 'CUSTOM_ENDPOINT_KEYS' => [
134
+ 'endpoint' => 'custom-key',
135
+ ],
136
+ ```
137
+
138
+ ## The Response helper
139
+
140
+ ```php
141
+ Response::success($data, $code); // default 200
142
+ Response::error($message, $code, $details); // default 400
143
+ Response::noContent(); // 204
144
+ ```
145
+
146
+ ## Pre-built endpoints
147
+
148
+ | Route | Method | Description |
149
+ |---|---|---|
150
+ | `/api/public/example-public` | `GET` | Example endpoint that doesn't require any key |
151
+ | `/api/protected/example-protected` | `GET` | Example endpoint that requires X-API-KEY |
@@ -0,0 +1,96 @@
1
+ # Nunjucks (HTML) Components
2
+
3
+ ## What is Nunjucks
4
+
5
+ Nunjucks (`.njk`) is an HTML file that supports logic like variables, `if` statements, and `for` loops. It can extend a base layout and include other `.njk` components
6
+
7
+ ## Create a component
8
+
9
+ Create a new `.njk` file anywhere inside `src/frontend/components/`. You can organize them into subfolders freely
10
+
11
+ ```
12
+ src/frontend/components/
13
+ ├── global/
14
+ ├── layouts/
15
+ ├── modals/
16
+ │ └── privacyModal.njk # You can move it to a modals/subfolder
17
+ ├── welcome.njk
18
+ ```
19
+
20
+ ## Include a component
21
+
22
+ To render a component inside a page, navigate to `src/frontend/layouts/` and edit `page-components.njk`
23
+
24
+ ### page-components.njk <small>(`src/frontend/layouts/`)</small>
25
+
26
+ ```js
27
+ {% if title == "homepage" %}
28
+ {% include "welcome.njk" %}
29
+
30
+ {% elif title == "examplePage" %}
31
+ {% include "exampleComponent1.njk" %}
32
+ {% include "subfolder/exampleComponent2.njk" %}
33
+
34
+ {% else %}
35
+ {% include "404/_404.njk" %}
36
+ {{ content | safe }}
37
+ {% endif %}
38
+ ```
39
+
40
+ Add a new `{% elif %}` block for each page, listing its components in order. If a component lives in a subfolder, specify the relative path accordingly
41
+
42
+ > ⚠️ A new `elif` block is automatically added when you create a page via the Assistant CLI
43
+
44
+ > ⚠️ If you move or delete a component, always update `page-components.njk` or the site will break
45
+
46
+ ### Using Markdown files in components
47
+
48
+ You can also render a `.md` file directly inside any `.njk` component using the `mdFile` shortcode:
49
+
50
+ ```njk
51
+ {% mdFile "docs/your-file.md" %}
52
+ ```
53
+
54
+ The path is relative to the project root (where `.eleventy.js` lives).
55
+
56
+ > ⚠️ The file is read at build time — changes to the `.md` file trigger a rebuild in watch mode.
57
+
58
+ ## Nest components
59
+
60
+ A component can include other components. This is useful for breaking complex sections into smaller, reusable pieces.
61
+
62
+ ### exampleComponent.njk
63
+ ```js
64
+ <section class="hero">
65
+ {% include "ui/heroTitle.njk" %}
66
+ {% include "ui/heroButton.njk" %}
67
+ </section>
68
+ ```
69
+
70
+ > The same path rules apply: if the included component is in a subfolder, specify the full relative path.
71
+
72
+ ## Global components
73
+
74
+ Header and footer live in `src/frontend/components/global/` and are automatically included in every page via `base.njk`. Edit them to change the site-wide layout
75
+
76
+ ## Site data in components
77
+
78
+ All values defined in `src/frontend/data/site.json` are globally available in every component via `{{ site.* }}`
79
+
80
+ ### site.json <small>(`src/data/`)</small>
81
+ ```json
82
+ {
83
+ "title": "My Site",
84
+ "logo": "/img/logo.png",
85
+ "legal": {
86
+ "privacy": "/privacy"
87
+ }
88
+ }
89
+ ```
90
+
91
+ ### Usage in any `.njk` file
92
+ ```js
93
+ <p>{{ site.title }}</p>
94
+ <a href="{{ site.legal.privacy }}">Privacy Policy</a>
95
+ <img src="{{ site.logo }}" alt="{{ site.title }}">
96
+ ```
@@ -0,0 +1,65 @@
1
+ # Creating Pages
2
+
3
+ > Examples use JavaScript, but everything applies equally to TypeScript. The only difference is the file extension (`.ts` instead of `.js`), that imports do **not** include the extension, and that paths use `src/frontend/ts/` instead of `src/frontend/js/`.
4
+
5
+ The recommended way is via the **Assistant CLI**
6
+
7
+ ## What gets created
8
+ For a page named `my-page`:
9
+
10
+ | File | Purpose |
11
+ |---|---|
12
+ | `src/frontend/_routes_/my-page.njk` | Template with front matter |
13
+ | `src/frontend/scss/pages/myPage.scss` | Imports framework + modules |
14
+ | `src/frontend/js/pages/myPage.js` | Imports JS modules |
15
+
16
+ ## Adding content
17
+
18
+ 1. Create a component in `src/frontend/components/` (e.g. `_myPage.njk`)
19
+ 2. Include it in `src/frontend/layouts/page-components.njk` inside the generated `elif` block:
20
+
21
+ ```njk
22
+ {% elif title == "myPage" %}
23
+ {% include "_myPage.njk" %}
24
+ ```
25
+
26
+ See **Components** DOC file for more info
27
+
28
+ ## URL and title
29
+
30
+ The URL is the kebab-case name (`/my-page/`). The `title` in the front matter is camelCase (`myPage`) and is used internally to load the correct CSS and JS files — do not change it.
31
+
32
+ ## Subpages (nested URLs)
33
+
34
+ To create a URL like `domain.it/about/team`, edit the `permalink` in `src/frontend/_routes/team.njk` and add the parent segment before the final slash:
35
+
36
+ ```njk
37
+ ---
38
+ title: "team"
39
+ permalink: "about/team/"
40
+ layout: page-components.njk
41
+ ---
42
+ ```
43
+
44
+ The parent path (`about`) does **not** need to exist as a real page — it's just a URL prefix. Only the last segment (`team`) must match the filename and the `title` in the front matter.
45
+
46
+ | Goal URL | permalink value | File |
47
+ |---|---|---|
48
+ | `/team/` | `/team/` | `_routes/team.njk` |
49
+ | `/about/team/` | `/about/team/` | `_routes/team.njk` |
50
+ | `/company/about/team/` | `/company/about/team/` | `_routes/team.njk` |
51
+
52
+ ## SEO
53
+
54
+ The CLI creates a stub entry in `src/frontend/data/site.json`. Fill it in:
55
+
56
+ ```json
57
+ "myPage": {
58
+ "seo": {
59
+ "title": "My Page | Site Name",
60
+ "description": "Page description"
61
+ }
62
+ }
63
+ ```
64
+
65
+ See **Head & SEO** for all available options.
package/docs/Deploy.md ADDED
@@ -0,0 +1,101 @@
1
+ # Server Configuration
2
+
3
+ Nibula builds a static site into your output folder (`out`). To put it
4
+ online you upload that folder to a web server. Which server config you need
5
+ depends on **where** you publish.
6
+
7
+ ## Where to publish
8
+
9
+ ### Shared hosting (Apache — e.g. Aruba, most cPanel providers)
10
+
11
+ The most common and beginner-friendly option. You rent space on a server that's
12
+ already set up, and you just upload your files.
13
+
14
+ In short: take the **contents of the `out` folder** and upload them to the
15
+ hosting's public web root (often called `htdocs`, `public_html`, or `www`).
16
+ That's it — the site is live.
17
+
18
+ On this kind of hosting the server usually runs **Apache** (or **IIS** on Windows
19
+ hosts). This is where `.htaccess` (Apache) or `web.config` (IIS) come in: they're
20
+ already inside your `out` folder and get picked up automatically, with no extra
21
+ setup. They handle security and routing for you (see below).
22
+
23
+ > You don't need to understand the details to publish — upload `out`, and the
24
+ > config files do their job. The sections below explain what they protect.
25
+
26
+ ### Your own server (Nginx — e.g. an Ubuntu VPS)
27
+
28
+ If you rent a bare server (a VPS) and install everything yourself, it typically
29
+ runs **Nginx** on **Ubuntu Server**. Here nothing is pre-configured: you set up
30
+ Nginx, PHP, and the security rules by hand. This is more powerful but requires
31
+ knowing what you're doing — if this is your path, read up on Nginx server
32
+ administration first. The Nginx section below gives you the essential rules to
33
+ start from.
34
+
35
+ ---
36
+
37
+ ## Apache (`.htaccess`)
38
+
39
+ `.htaccess` files at `src/frontend/` and `src/backend/` are automatically copied
40
+ into the build output by Eleventy. No setup required — upload `out` and Apache
41
+ reads them on its own.
42
+
43
+ They cover:
44
+ - Directory listing disabled
45
+ - 403 / 404 → `/404.html`
46
+ - Sensitive files blocked (`web.config`, dotfiles, etc.)
47
+ - Backend source directory sealed — all access funnels through the front controller
48
+ - `/api/*` → `backend/_core/index.php`
49
+ - HTTPS redirect
50
+
51
+ This is the config that makes shared hosting like Aruba work out of the box.
52
+
53
+ ## IIS (`web.config`)
54
+
55
+ For Windows-based hosting. `web.config` files at `src/frontend/` and
56
+ `src/backend/` are automatically copied into the build output by Eleventy, and
57
+ IIS reads them automatically — same idea as `.htaccess`, different server.
58
+
59
+ Covers the same rules as the Apache configuration above.
60
+
61
+ ## Nginx
62
+
63
+ Used when you run your **own Ubuntu Server** (a VPS). Unlike Apache and IIS,
64
+ Nginx does **not** read per-directory config files, so there's nothing automatic
65
+ here.
66
+
67
+ **`nginx.conf` in the project root is not a ready-to-use file.** It is a set of
68
+ directives that you must take and adapt into a complete Nginx `server { }`
69
+ block — the one that defines your domain (`server_name`), ports (`listen`), and
70
+ SSL certificates. Paste these rules inside your own server block and adjust
71
+ `root` to point to your `out` folder.
72
+
73
+ These directives are the **minimum Nginx equivalent of `.htaccess` and
74
+ `web.config`**: their job is to not expose dangerous files (server config files,
75
+ the backend source directory) and to route the API correctly. Without them, an
76
+ Nginx server would serve your site but leave those files reachable.
77
+
78
+ They provide:
79
+ - `server_tokens off` — hides the Nginx version
80
+ - `autoindex off` — disables directory listing
81
+ - 403 / 404 → `/404.html`
82
+ - Backend source directory (`/backend/`) sealed with `return 404`
83
+ - Server config files (`.htaccess`, `web.config`) blocked anywhere in the tree
84
+ - `/api/*` routed to the PHP front controller
85
+ - Security headers on static responses
86
+
87
+ > Setting up Nginx correctly (SSL, HTTPS redirect, PHP-FPM) goes beyond these
88
+ > rules. If you plan to publish with Nginx, read up on Nginx + PHP-FPM on Ubuntu
89
+ > Server first — these directives are the security starting point, not a full
90
+ > server setup.
91
+
92
+ ### PHP-FPM socket
93
+
94
+ The socket path in the `/api/` rule targets RHEL / Fedora by default. On Ubuntu
95
+ the socket name includes the PHP version — check with `ls /run/php/` and adjust:
96
+
97
+ | Distro | Path |
98
+ |---|---|
99
+ | RHEL / Fedora | `unix:/run/php-fpm/php-fpm.sock` |
100
+ | Debian / Ubuntu | `unix:/run/php/php8.3-fpm.sock` |
101
+ | TCP fallback | `127.0.0.1:9000` |
@@ -0,0 +1,117 @@
1
+ # Head & SEO
2
+
3
+ Global settings live in `src/frontend/data/site.json` and are available everywhere via `{{ site.* }}`.
4
+
5
+ ## Global fields
6
+
7
+ ```json
8
+ {
9
+ "site_name": "Site name",
10
+ "title": "Site title",
11
+ "description": "Site description",
12
+ "keywords": "keyword1, keyword2, keyword3",
13
+ "domain": "yoursite.com",
14
+ "url": "https://yoursite.com",
15
+ "lang": "en",
16
+ "author": "Name and surname",
17
+ "data_bs_theme": "dark",
18
+ "theme_color": {
19
+ "light": "#ffffff",
20
+ "dark": "#0d1117"
21
+ },
22
+ "favicon": "/assets/brand/favicon.svg",
23
+ "logo": "/assets/brand/logo.svg",
24
+ "legal": {
25
+ "privacy": "",
26
+ "cookie": "",
27
+ "cookieControls": "",
28
+ "terms": "",
29
+ "copyright": {
30
+ "year": "2026",
31
+ "text": "Copyright text"
32
+ }
33
+ }
34
+ }
35
+ ```
36
+
37
+ | Field | Purpose |
38
+ |---|---|
39
+ | `site_name` | Brand name (used in meta tags and JSON-LD) |
40
+ | `title` | Default page title — fallback when a page has no `seo.title` |
41
+ | `description` | Default meta description — fallback for pages |
42
+ | `keywords` | Default meta keywords — fallback for pages |
43
+ | `domain` / `url` | Canonical URLs, `og:url`, JSON-LD |
44
+ | `lang` | HTML `lang` attribute |
45
+ | `author` | Meta author and JSON-LD author |
46
+ | `data_bs_theme` | Bootstrap color scheme (`light` / `dark`) |
47
+ | `theme_color` | Browser UI / PWA color, per scheme (`light` / `dark`) |
48
+ | `favicon` | Path to the favicon |
49
+ | `logo` | Path to the logo, also used as social image |
50
+ | `legal.privacy` / `cookie` / `cookieControls` / `terms` | Legal page URLs |
51
+ | `legal.copyright.year` / `text` | Footer copyright |
52
+
53
+ ## Per-page SEO and CDN
54
+
55
+ Each page is keyed by its camelCase `title` from the front matter:
56
+
57
+ ```json
58
+ "pages": {
59
+ "examplePage": {
60
+ "seo": {
61
+ "title": "Example Page",
62
+ "description": "Description",
63
+ "keywords": "",
64
+ "noindex": false,
65
+ "canonical": ""
66
+ },
67
+ "cdn": {
68
+ "css": ["https://example.com/lib.min.css"],
69
+ "js": ["https://example.com/lib.min.js"]
70
+ }
71
+ }
72
+ }
73
+ ```
74
+
75
+ | Field | Purpose | If empty / absent |
76
+ |---|---|---|
77
+ | `seo.title` | Page title | Falls back to global `title` |
78
+ | `seo.description` | Meta description | Falls back to global `description` |
79
+ | `seo.keywords` | Meta keywords | Falls back to global `keywords` |
80
+ | `seo.noindex` | If `true`, emits `<meta name="robots" content="noindex, follow">` and drops the page from the sitemap | Page is indexable |
81
+ | `seo.canonical` | Absolute canonical URL override | Computed as `url + page.url` |
82
+ | `cdn.css` / `cdn.js` | Extra per-page CDN links | No extra links loaded |
83
+
84
+ ## Fallback logic
85
+
86
+ Global values are defaults, not duplicates. A page value is used when present; otherwise the global one applies:
87
+
88
+ ```njk
89
+ {{ pageData.seo.title or title or site.title }}
90
+ {{ pageData.seo.description or site.description }}
91
+ {{ pageData.seo.keywords or site.keywords }}
92
+ ```
93
+
94
+ `noindex` and `canonical` have no global default: `noindex` defaults to indexable behavior, `canonical` is computed from the URL. They exist per-page only.
95
+
96
+ ## theme_color
97
+
98
+ Two tags are emitted so the browser bar follows the user's system scheme:
99
+
100
+ ```njk
101
+ <meta name="theme-color" content="{{ site.theme_color.light }}" media="(prefers-color-scheme: light)">
102
+ <meta name="theme-color" content="{{ site.theme_color.dark }}" media="(prefers-color-scheme: dark)">
103
+ ```
104
+
105
+ This is independent from `data_bs_theme`, which controls the Bootstrap theme, not the browser UI.
106
+
107
+ ## AI & SEO bots
108
+
109
+ `llms.txt`, `robots.txt` and `sitemap.xml` are generated from `.njk` templates and are publicly reachable (they must not be blocked by the server config).
110
+
111
+ | File | Purpose | Reachable at |
112
+ |---|---|---|
113
+ | `llms.njk` | Tells AI models what the site is about | `yoursite.com/llms.txt` |
114
+ | `robots.njk` | Controls search-engine crawling | `yoursite.com/robots.txt` |
115
+ | `sitemap.njk` | Lists indexable pages (skips drafts, 404, and `noindex`) | `yoursite.com/sitemap.xml` |
116
+
117
+ To customize, edit `src/frontend/llms.njk` or `src/frontend/robots.njk` directly.
@@ -0,0 +1,53 @@
1
+ # JavaScript
2
+
3
+ > Examples use JavaScript, but everything applies equally to TypeScript. The only difference is the file extension (`.ts` instead of `.js`), that imports do **not** include the extension, and that paths use `src/frontend/ts/` instead of `src/frontend/js/`.
4
+
5
+ ## Page JS
6
+
7
+ Each page has its own JS entry point in `src/frontend/js/pages/`, bundled and minified by esbuild and loaded automatically by `base.njk`.
8
+
9
+ Import only what the page needs.
10
+
11
+ ### examplePage.js <small>(`src/frontend/js/pages/`)</small>
12
+
13
+ ```js
14
+ // import { initExampleModule } from '../modules/exampleModule.js';
15
+
16
+ document.addEventListener("DOMContentLoaded", () => {
17
+ // initExampleModule();
18
+ });
19
+
20
+ // Page logic here
21
+ ```
22
+
23
+ ## Modules
24
+
25
+ Modules live in `src/frontend/js/modules/`. Modules that interact with the DOM must be called inside `DOMContentLoaded`; others can be called anywhere.
26
+
27
+ ## Adding a module
28
+
29
+ Create a new `.js` file in `src/frontend/js/modules/`. Subfolders are allowed.
30
+
31
+ Use ESM syntax — esbuild handles the bundling:
32
+
33
+ ### exampleModule.js <small>(`src/frontend/js/modules/`)</small>
34
+
35
+ ```js
36
+ export function exampleModule() {
37
+ // Module logic here
38
+ }
39
+ ```
40
+
41
+ Then import it in the pages that need it:
42
+
43
+ ```js
44
+ import { exampleModule } from '../modules/exampleModule.js';
45
+ ```
46
+
47
+ In TypeScript, omit the extension:
48
+
49
+ ```ts
50
+ import { exampleModule } from '../modules/exampleModule';
51
+ ```
52
+
53
+ > ⚠️ Files inside `_tools/` run directly in Node.js without a bundler — use CommonJS (`require` / `module.exports`) there, not ESM.