nibula 2.0.0 → 2.0.1

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.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,41 @@ All notable changes to Nibula are documented here.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [2.0.1] - 2026-08-08
9
+
10
+ ### Added
11
+ - `docs/Components.md` documents custom layouts. `base.njk` was presented as the
12
+ only layout there is, so a page that doesn't fit the standard shell — a landing
13
+ page with no header or footer, a full-screen login — had no documented way out.
14
+ Copying `base.njk` and pointing a route at the copy was already supported;
15
+ nothing said so.
16
+ - `docs/Head and SEO.md` shows where the tags are actually written, including the
17
+ `pageKey` lookup that ties a route's `title` to its record in `pages.json`, and
18
+ warns against removing the `url` filter from asset paths — it is what keeps a
19
+ site published under a subpath from 404ing every stylesheet and script.
20
+ - `docs/Creating pages.md` covers the `layout` front matter field, which the file
21
+ showed in every example without ever explaining.
22
+
23
+ ### Fixed
24
+ - `src/frontend/ts/global.ts` imported `exampleModule` for real while leaving the
25
+ call commented out, so a new TypeScript project started with an unused import
26
+ and a stale path in the comment above it. It now matches `global.js`, where the
27
+ import is commented too.
28
+ - The `site.json` snippet in `docs/Components.md` was not valid JSON: the opening
29
+ and two closing braces were missing and three `//` comments sat inside it, so
30
+ copying it into the file broke the build.
31
+ - `docs/Head and SEO.md` listed `favicon-48.png` among the favicon files. No tag
32
+ in `base.njk` loads it.
33
+
34
+ ### Changed
35
+ - `docs/Creating pages.md` no longer re-explains component includes and the
36
+ `pages.json` stub, which are documented in full elsewhere and had started to
37
+ drift.
38
+ - Nunjucks snippets in `docs/Components.md` are tagged as such instead of as
39
+ JavaScript.
40
+ - `docs/Project structure.md` points at `layouts/` for pages that need their own
41
+ skeleton.
42
+
8
43
  ## [2.0.0] - 2026-08-07
9
44
 
10
45
  ### Breaking
@@ -14,7 +14,7 @@ src/frontend/components/
14
14
  │ ├── header.njk
15
15
  │ └── footer.njk
16
16
  ├── modals/
17
- │ └── privacyModal.njk # You can move it to a modals/subfolder
17
+ │ └── privacyModal.njk
18
18
  ├── not-found.njk
19
19
  ├── welcome.njk
20
20
  ```
@@ -25,7 +25,7 @@ Components are included in the page that renders them. Open the page's file in `
25
25
 
26
26
  ### example-page.njk <small>(`src/frontend/routes/`)</small>
27
27
 
28
- ```js
28
+ ```njk
29
29
  ---
30
30
  title: "examplePage"
31
31
  permalink: "/example-page/"
@@ -49,7 +49,7 @@ Writing HTML directly in the route works too, and is perfectly fine for a short
49
49
  A component can include other components. This is useful for breaking complex sections into smaller, reusable pieces.
50
50
 
51
51
  ### exampleComponent.njk
52
- ```js
52
+ ```njk
53
53
  <section class="hero">
54
54
  {% include "hero/hero-title.njk" %}
55
55
  {% include "hero/hero-button.njk" %}
@@ -62,33 +62,87 @@ A component can include other components. This is useful for breaking complex se
62
62
 
63
63
  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
64
64
 
65
+ ## Custom layouts
66
+
67
+ `base.njk` is the default layout, but it isn't the only one you can have. Any
68
+ `.njk` file in `src/frontend/layouts/` is a layout, and each route picks its own.
69
+
70
+ The usual reason is a page that doesn't fit the standard shell: a landing page
71
+ with no header or footer, a full-screen login, a "coming soon" holding page.
72
+
73
+ Copy `base.njk`, rename it, and change only what you need — that way you keep the
74
+ head, the SEO tags and the CSS/JS wiring, which is the part you don't want to
75
+ rewrite:
76
+
77
+ ```
78
+ src/frontend/layouts/
79
+ ├── base.njk
80
+ └── custom.njk
81
+ ```
82
+
83
+ ### custom.njk <small>(`src/frontend/layouts/`)</small>
84
+
85
+ Same as `base.njk`, without the two global includes:
86
+
87
+ ```njk
88
+ <body>
89
+ <main>
90
+ {{ content | safe }}
91
+ </main>
92
+ </body>
93
+ ```
94
+
95
+ Then point the route at it:
96
+
97
+ ### landing.njk <small>(`src/frontend/routes/`)</small>
98
+
99
+ ```njk
100
+ ---
101
+ title: "landing"
102
+ permalink: "/landing/"
103
+ layout: custom.njk
104
+ ---
105
+ ```
106
+
107
+ Paths are resolved from `src/frontend/layouts/`, so you write `"custom.njk"`, not
108
+ `"layouts/custom.njk"` — the same rule as components.
109
+
110
+ > ⚠️ A layout you copy is a copy: a later fix to `base.njk` won't reach it. Keep
111
+ > the differences to the minimum, and check your custom layouts when you change
112
+ > something in the head.
113
+
65
114
  ## Site data in components
66
115
 
67
116
  All values defined in `src/frontend/data/site.json` are globally available in every component via `{{ site.* }}`
68
117
 
69
118
  ### site.json <small>(`src/frontend/data/`)</small>
70
119
  ```json
71
- "site_name": "Site name",
72
- "title": "Site title",
73
- "description": "Site description",
74
- "keywords": "keyword1, keyword2, keyword3",
75
- "domain": "yoursite.com",
76
- "url": "https://yoursite.com",
77
- "lang": "en",
78
- "author": "Name and surname",
79
- "logo": "/assets/brand/logo.svg",
80
- "legal": {
81
- "privacy": "", // This is supposed to contain a link
82
- "cookie": "", // This is supposed to contain a link
83
- "terms": "", // This is supposed to contain a link
84
- "copyright": {
85
- "year": "2026",
86
- "text": "All rights reserved."
120
+ {
121
+ "site_name": "Site name",
122
+ "title": "Site title",
123
+ "description": "Site description",
124
+ "keywords": "keyword1, keyword2, keyword3",
125
+ "domain": "yoursite.com",
126
+ "url": "https://yoursite.com",
127
+ "lang": "en",
128
+ "author": "Name and surname",
129
+ "logo": "/assets/brand/logo.svg",
130
+ "legal": {
131
+ "privacy": "",
132
+ "cookie": "",
133
+ "terms": "",
134
+ "copyright": {
135
+ "year": "2026",
136
+ "text": "All rights reserved."
137
+ }
87
138
  }
139
+ }
88
140
  ```
89
141
 
142
+ `legal.privacy`, `legal.cookie` and `legal.terms` hold the URLs of your legal pages — the footer links to them. See **Head & SEO** DOC file for what every field does
143
+
90
144
  ### Usage in any `.njk` file
91
- ```js
145
+ ```njk
92
146
  <p>{{ site.title }}</p>
93
147
  <a href="{{ site.legal.privacy }}">Privacy Policy</a>
94
148
  <img src="{{ site.logo }}" alt="{{ site.title }}">
@@ -108,7 +162,7 @@ Create `test.json`, put anything you want inside it, and every key is reachable
108
162
  ```
109
163
 
110
164
  ### Usage in any `.njk` file
111
- ```js
165
+ ```njk
112
166
  <p>{{ test.testMessage }}</p>
113
167
  ```
114
168
 
@@ -131,7 +185,7 @@ Values can be anything JSON allows, so a list lets you loop instead of copying m
131
185
  }
132
186
  ```
133
187
 
134
- ```js
188
+ ```njk
135
189
  {% for link in test.links %}
136
190
  <a href="{{ link.url }}">{{ link.label }}</a>
137
191
  {% endfor %}
@@ -13,26 +13,21 @@ For a page named `my-page`:
13
13
  | `src/frontend/scss/pages/myPage.scss` | Imports framework + modules |
14
14
  | `src/frontend/js/pages/myPage.js` | Imports JS/TS modules |
15
15
 
16
+ It also adds a stub entry in `src/frontend/data/pages.json` for the page's SEO and CDN links — fill it in as described in the **Head & SEO** DOC file.
17
+
16
18
  ## Adding content
17
19
 
18
- 1. Create a component in `src/frontend/components/` (e.g. `my-component.njk`)
19
- 2. Include it in the page's own file, `src/frontend/routes/my-page.njk`:
20
+ The route is created empty. Write your HTML in its body, or include components — see the **Components** DOC file.
20
21
 
21
- ```njk
22
- ---
23
- title: "myPage"
24
- permalink: "/my-page/"
25
- layout: base.njk
26
- ---
22
+ ## URL and title
27
23
 
28
- {% include "my-component.njk" %}
29
- ```
24
+ 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, and to look up the page's record in `pages.json` — do not change it.
30
25
 
31
- See **Components** DOC file for more info
26
+ ## Layout
32
27
 
33
- ## URL and title
28
+ `layout` decides the HTML skeleton the page is rendered into. New pages get `base.njk`, which brings the head, the SEO tags, the header and the footer.
34
29
 
35
- 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.
30
+ A page that needs a different shell — a landing page with no header, for instance — points at a layout of its own. See **Components** DOC file, *Custom layouts*.
36
31
 
37
32
  ## Subpages (nested URLs)
38
33
 
@@ -52,26 +47,4 @@ The parent path (`about`) does **not** need to exist as a real page — it's jus
52
47
  |---|---|---|
53
48
  | `/team/` | `/team/` | `routes/team.njk` |
54
49
  | `/about/team/` | `/about/team/` | `routes/team.njk` |
55
- | `/company/about/team/` | `/company/about/team/` | `routes/team.njk` |
56
-
57
- ## SEO
58
-
59
- The CLI creates a stub entry in `src/frontend/data/pages.json`. Fill it in:
60
-
61
- ```json
62
- "myPage": {
63
- "seo": {
64
- "title": "My Page",
65
- "description": "Description",
66
- "keywords": "",
67
- "noindex": false,
68
- "canonical": ""
69
- },
70
- "cdn": {
71
- "css": [],
72
- "js": []
73
- }
74
- }
75
- ```
76
-
77
- The `seo.title` and `seo.description` you write here are also what the page shows up as in `llms.txt`, so a page left with the placeholder description will advertise itself that way to AI crawlers.
50
+ | `/company/about/team/` | `/company/about/team/` | `routes/team.njk` |
@@ -15,6 +15,7 @@ Global settings live in `src/frontend/data/site.json` and are available everywhe
15
15
  "lang": "en",
16
16
  "author": "Name and surname",
17
17
  "theme": "dark",
18
+ "theme_color": "#2a2150",
18
19
  "logo": "/assets/brand/logo.svg",
19
20
  "legal": {
20
21
  "privacy": "",
@@ -74,6 +75,8 @@ Per-page settings live in `src/frontend/data/pages.json`, available via `{{ page
74
75
  | `seo.canonical` | Absolute canonical URL override | Computed as `url + page.url` |
75
76
  | `cdn.css` / `cdn.js` | Extra per-page CDN links | No extra links loaded |
76
77
 
78
+ The assistant creates the stub entry when you add a page; a page left with the placeholder description will advertise itself that way to AI crawlers.
79
+
77
80
  ## Fallback logic
78
81
 
79
82
  Global values are defaults, not duplicates. A page value is used when present; otherwise the global one applies:
@@ -120,12 +123,24 @@ The three favicon tags are hardcoded in `base.njk`. To change the icons, replace
120
123
  | File | Purpose |
121
124
  |---|---|
122
125
  | `favicon.svg` | Modern browsers, scales to any size |
123
- | `favicon-48.png` | Fallback for older browsers and some crawlers |
124
126
  | `favicon-32.png` | Fallback for older browsers and some crawlers |
125
127
  | `apple-touch-icon.png` | iOS home screen icon — 180×180, must be opaque (iOS renders transparency as black) |
126
128
 
127
129
  > ⚠️ Without `apple-touch-icon.png`, iOS uses a screenshot of the page as the home screen icon.
128
130
 
131
+ ## Where the tags are written
132
+
133
+ All of the above is rendered by `src/frontend/layouts/base.njk`, which reads the page's record with:
134
+
135
+ ```njk
136
+ {% set pageKey = title %}
137
+ {% set pageData = pages[pageKey] %}
138
+ ```
139
+
140
+ Edit the template when you need a tag the boilerplate doesn't cover — a font `<link>`, an analytics snippet. For everything listed here, change the values in `site.json` and `pages.json` instead.
141
+
142
+ > ⚠️ Asset paths in `base.njk` go through the `url` filter. It makes them work when the site is published under a subpath — GitHub Pages being the usual case. Removing it 404s every stylesheet and script there.
143
+
129
144
  ## AI & SEO bots
130
145
 
131
146
  `llms.txt`, `robots.txt` and `sitemap.xml` are generated from `.njk` templates in `src/frontend/indexing/` and are publicly reachable (they must not be blocked by the server config).
@@ -16,7 +16,7 @@ your-project/
16
16
  ├── backend/ # REST API — see docs/Backend.md
17
17
  └── frontend/
18
18
  ├── assets/ # Images, fonts, brand files
19
- └── brand/ # Favicons, logo etc
19
+ │ └── brand/ # Favicons, logo etc
20
20
  ├── components/ # Reusable .njk fragments
21
21
  │ └── global/ # Header and footer, included in every page
22
22
  ├── data/ # site.json and pages.json
@@ -44,6 +44,7 @@ your-project/
44
44
  | Change the site name, URL, author | `data/site.json` |
45
45
  | Change a page's SEO | `data/pages.json` |
46
46
  | Edit the HTML `<head>` | `layouts/base.njk` |
47
+ | Give one page a different skeleton | `layouts/` — add your own, see docs/Components.md |
47
48
  | Add an API endpoint | `backend/api/` |
48
49
 
49
50
  ## Two folders worth a note
@@ -58,6 +59,7 @@ Subfolders are allowed wherever content is yours to organize:
58
59
 
59
60
  - `components/` — nest as deep as you like; includes take the path relative to `components/`
60
61
  - `scss/modules/` and `js/modules/` — same idea, remember to update the relative import paths
62
+ - `layouts/` — extra layouts alongside `base.njk`; routes pick one by filename
61
63
  - `backend/api/public/` and `protected/` — subfolders become part of the URL
62
64
 
63
65
  The folders in the tree above are the part to leave alone: paths are wired into `.eleventy.js`, the build scripts and the assistant, so renaming one breaks the build.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nibula",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "A beginner-friendly static site generator built on Eleventy that stays close to vanilla HTML, CSS and JS — with a page-management CLI, an optional PHP backend, and SEO files generated for you.",
5
5
  "keywords": [
6
6
  "11ty",
@@ -1,5 +1,4 @@
1
- // import { initExampleModule } from "/exampleModule";
2
- import { initExampleModule } from "./modules/exampleModule"
1
+ // import { initExampleModule } from "./modules/exampleModule"
3
2
 
4
3
  function initGlobal() {
5
4
  // initExampleModule();