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 +35 -0
- package/docs/Components.md +76 -22
- package/docs/Creating pages.md +9 -36
- package/docs/Head and SEO.md +16 -1
- package/docs/Project structure.md +3 -1
- package/package.json +1 -1
- package/src/frontend/ts/global.ts +1 -2
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
|
package/docs/Components.md
CHANGED
|
@@ -14,7 +14,7 @@ src/frontend/components/
|
|
|
14
14
|
│ ├── header.njk
|
|
15
15
|
│ └── footer.njk
|
|
16
16
|
├── modals/
|
|
17
|
-
│ └── privacyModal.njk
|
|
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
|
-
```
|
|
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
|
-
```
|
|
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
|
-
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
-
"
|
|
81
|
-
"
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
"
|
|
86
|
-
|
|
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
|
-
```
|
|
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
|
-
```
|
|
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
|
-
```
|
|
188
|
+
```njk
|
|
135
189
|
{% for link in test.links %}
|
|
136
190
|
<a href="{{ link.url }}">{{ link.label }}</a>
|
|
137
191
|
{% endfor %}
|
package/docs/Creating pages.md
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
22
|
-
---
|
|
23
|
-
title: "myPage"
|
|
24
|
-
permalink: "/my-page/"
|
|
25
|
-
layout: base.njk
|
|
26
|
-
---
|
|
22
|
+
## URL and title
|
|
27
23
|
|
|
28
|
-
|
|
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
|
-
|
|
26
|
+
## Layout
|
|
32
27
|
|
|
33
|
-
|
|
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
|
-
|
|
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` |
|
package/docs/Head and SEO.md
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
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",
|