@janga/norna 0.7.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 (77) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +109 -0
  3. package/astro.config.mjs +17 -0
  4. package/bin/norna.mjs +170 -0
  5. package/docs/README.md +48 -0
  6. package/docs/command-organization.md +402 -0
  7. package/docs/commands.md +152 -0
  8. package/docs/configuration.md +376 -0
  9. package/docs/content.md +384 -0
  10. package/docs/engine-development.md +164 -0
  11. package/docs/getting-started.md +96 -0
  12. package/docs/images-and-metadata.md +88 -0
  13. package/docs/local-development.md +61 -0
  14. package/docs/publishing.md +81 -0
  15. package/docs/site-examples-structure-note.md +105 -0
  16. package/docs/site-structure.md +81 -0
  17. package/fixtures/basic/site/.norna/generated-images.json +1 -0
  18. package/fixtures/basic/site/config.mjs +59 -0
  19. package/fixtures/basic/site/content.md +17 -0
  20. package/fixtures/basic/site/images/work/.gitkeep +1 -0
  21. package/fixtures/basic/site/public/robots.txt +2 -0
  22. package/fixtures/basic/site/theme.md +7 -0
  23. package/package.json +90 -0
  24. package/scripts/build-site.mjs +16 -0
  25. package/scripts/check-config.mjs +37 -0
  26. package/scripts/deploy-site.mjs +389 -0
  27. package/scripts/dev-local.mjs +313 -0
  28. package/scripts/doctor.mjs +38 -0
  29. package/scripts/engine-version.mjs +137 -0
  30. package/scripts/generate-images.mjs +369 -0
  31. package/scripts/init-site.mjs +249 -0
  32. package/scripts/lib/astro-command.mjs +34 -0
  33. package/scripts/lib/ci-lockfile.mjs +34 -0
  34. package/scripts/lib/image-dimensions.mjs +78 -0
  35. package/scripts/lib/presentation.mjs +72 -0
  36. package/scripts/lib/project-config.mjs +322 -0
  37. package/scripts/lib/run-command.mjs +21 -0
  38. package/scripts/lib/site-content.mjs +392 -0
  39. package/scripts/lib/site-paths.mjs +127 -0
  40. package/scripts/lib/typography.mjs +166 -0
  41. package/scripts/release.mjs +77 -0
  42. package/scripts/show-typography.mjs +210 -0
  43. package/scripts/sync-content-sections.mjs +610 -0
  44. package/scripts/sync-site-public.mjs +42 -0
  45. package/scripts/test-ci-lockfile.mjs +65 -0
  46. package/scripts/test-content-check.mjs +364 -0
  47. package/scripts/test-engine-commands.mjs +128 -0
  48. package/scripts/test-navigation-preview.mjs +108 -0
  49. package/scripts/test-navigation.mjs +116 -0
  50. package/scripts/test-package-check.mjs +394 -0
  51. package/scripts/test-site-public.mjs +85 -0
  52. package/scripts/test-temporary-visibility.mjs +99 -0
  53. package/scripts/update-engine.mjs +127 -0
  54. package/scripts/watch-pages-deploy.mjs +430 -0
  55. package/src/components/GalleryGrid.astro +221 -0
  56. package/src/components/SiteNavigation.astro +410 -0
  57. package/src/components/SitePage.astro +69 -0
  58. package/src/components/SiteSection.astro +174 -0
  59. package/src/content.config.ts +171 -0
  60. package/src/layouts/BaseLayout.astro +90 -0
  61. package/src/lib/generatedImages.ts +63 -0
  62. package/src/lib/sectionContent.ts +125 -0
  63. package/src/lib/sitePages.ts +80 -0
  64. package/src/lib/sitePublicAssets.ts +39 -0
  65. package/src/lib/visibility.ts +35 -0
  66. package/src/pages/[slug].astro +31 -0
  67. package/src/pages/index.astro +16 -0
  68. package/src/styles/global.css +872 -0
  69. package/starters/basic/.github/workflows/deploy.yml +65 -0
  70. package/starters/basic/README.md +55 -0
  71. package/starters/basic/package.json +35 -0
  72. package/starters/basic/site/config.mjs +60 -0
  73. package/starters/basic/site/content.md +21 -0
  74. package/starters/basic/site/images/work/.gitkeep +1 -0
  75. package/starters/basic/site/public/robots.txt +2 -0
  76. package/starters/basic/site/theme.md +53 -0
  77. package/tsconfig.json +5 -0
@@ -0,0 +1,376 @@
1
+ # Configuration
2
+
3
+ Technical site configuration lives in the selected site's `config.mjs`; by
4
+ default that is `site/config.mjs`. The file must default-export an object.
5
+
6
+ This document describes the generic configuration interface. It does not list
7
+ the current values for any one site.
8
+
9
+ ## Site
10
+
11
+ ### `site.url`
12
+
13
+ - Purpose: canonical public URL used in the page `<link rel="canonical">`,
14
+ deploy monitoring output, and site-specific documentation.
15
+ - Type: string.
16
+ - Required: yes.
17
+ - Default: none.
18
+ - Validation: non-empty absolute URL accepted by `new URL()`.
19
+ - Consequence: wrong values render a wrong canonical URL and make deploy output
20
+ point at the wrong site.
21
+
22
+ Example:
23
+
24
+ ```js
25
+ site: {
26
+ url: 'https://example.com/',
27
+ }
28
+ ```
29
+
30
+ ## Layout
31
+
32
+ ### `layout.pageWidth`
33
+
34
+ - Purpose: maximum width of the main page content area, including section
35
+ headings, section text, galleries, sticky-navigation alignment, and footer.
36
+ - Type: string containing a simple positive CSS length.
37
+ - Required: no.
38
+ - Default: `1180px`.
39
+ - Validation: if set, it must be a positive number followed by one of `px`,
40
+ `rem`, `em`, `vw`, `vh`, `vmin`, `vmax`, `ch`, or `%`.
41
+ - Consequence: the value overrides the global `--page-width` CSS variable.
42
+ It is the outer width limit for content that is centered on the page.
43
+ `gallery.width` cannot render wider than this page width or the available
44
+ viewport width after responsive gutters.
45
+
46
+ Example:
47
+
48
+ ```js
49
+ layout: {
50
+ pageWidth: '1180px',
51
+ }
52
+ ```
53
+
54
+ ### `layout.gutter`
55
+
56
+ - Purpose: side margin removed from the viewport before the available content
57
+ width is calculated.
58
+ - Type: either a CSS length string used on all viewports, or an object with
59
+ `desktop` and `mobile` CSS length strings.
60
+ - Required: no.
61
+ - Default: desktop `clamp(1.25rem, 4vw, 3rem)`, mobile `1rem`.
62
+ - Validation: if set, each value must be a positive CSS length such as `16px`,
63
+ `3rem`, or `4vw`. `clamp()` with those simple lengths is also accepted.
64
+ - Consequence: wider gutters leave more side margin and reduce the maximum
65
+ available width for galleries and aligned text.
66
+
67
+ Example:
68
+
69
+ ```js
70
+ layout: {
71
+ gutter: {
72
+ desktop: '48px',
73
+ mobile: '16px',
74
+ },
75
+ }
76
+ ```
77
+
78
+ ## Gallery
79
+
80
+ ### `gallery.width`
81
+
82
+ - Purpose: maximum rendered width for gallery images, gallery captions, and
83
+ left- or right-aligned section text that is intended to line up with gallery
84
+ edges.
85
+ - Type: string containing a simple positive CSS length.
86
+ - Required: no.
87
+ - Default: `900px`.
88
+ - Validation: if set, it must be a positive number followed by one of `px`,
89
+ `rem`, `em`, `vw`, `vh`, `vmin`, `vmax`, `ch`, or `%`.
90
+ - Consequence: the value overrides the global `--gallery-width` CSS variable.
91
+ Images and aligned text cannot render wider than this value, but the
92
+ effective width is also limited by `layout.pageWidth`, `layout.gutter`, and
93
+ `gallery.maxAvailableWidthPercent`.
94
+
95
+ Example:
96
+
97
+ ```js
98
+ gallery: {
99
+ width: '900px',
100
+ }
101
+ ```
102
+
103
+ ### `gallery.maxAvailableWidthPercent`
104
+
105
+ - Purpose: maximum share of the available width, after gutters, that gallery
106
+ images, gallery captions, and aligned section text may use.
107
+ - Type: either a number used on all viewports, or an object with `desktop` and
108
+ `mobile` numbers.
109
+ - Required: no.
110
+ - Default: desktop `100`, mobile `100`.
111
+ - Validation: each value must be greater than `0` and less than or equal to
112
+ `100`.
113
+ - Consequence: `100` allows the gallery to use the full available width. A
114
+ smaller value keeps galleries narrower without changing the page width or
115
+ gutters.
116
+
117
+ Example:
118
+
119
+ ```js
120
+ gallery: {
121
+ maxAvailableWidthPercent: {
122
+ desktop: 100,
123
+ mobile: 100,
124
+ },
125
+ }
126
+ ```
127
+
128
+ ### `gallery.maxAvailableHeightPercent`
129
+
130
+ - Purpose: maximum share of viewport height that gallery images may use.
131
+ - Type: either a number used on all viewports, or an object with `desktop` and
132
+ `mobile` numbers.
133
+ - Required: no.
134
+ - Default: desktop `74`, mobile `68`.
135
+ - Validation: each value must be greater than `0` and less than or equal to
136
+ `100`.
137
+ - Consequence: images keep their proportions and are scaled down when filling
138
+ the available width would make them taller than this limit. This applies to
139
+ both landscape and portrait images.
140
+
141
+ Example:
142
+
143
+ ```js
144
+ gallery: {
145
+ maxAvailableHeightPercent: {
146
+ desktop: 74,
147
+ mobile: 68,
148
+ },
149
+ }
150
+ ```
151
+
152
+ ## Typography
153
+
154
+ ### `typography.fontFamily`
155
+
156
+ - Purpose: global CSS `font-family` stack used by site text and sticky
157
+ navigation.
158
+ - Type: string containing a CSS font-family value.
159
+ - Required: no.
160
+ - Default: `Arial, 'Helvetica Neue', Helvetica, sans-serif`, matching the
161
+ engine's existing sticky-navigation font stack.
162
+ - Validation: if set, it must be a non-empty string and must not contain
163
+ semicolons, braces, or line breaks.
164
+ - Consequence: the value overrides the global `--font-sans` CSS variable for
165
+ the whole page. Section headings, body text, gallery captions, footer text,
166
+ and sticky navigation all inherit from that variable unless engine CSS gives a
167
+ more specific rule.
168
+
169
+ Example:
170
+
171
+ ```js
172
+ typography: {
173
+ fontFamily: "system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
174
+ }
175
+ ```
176
+
177
+ ## Navigation
178
+
179
+ ### `navigation.smoothScroll`
180
+
181
+ - Purpose: controls enhanced same-page anchor navigation and native
182
+ `scroll-behavior`.
183
+ - Type: object.
184
+ - Required: no; omitted fields use defaults.
185
+
186
+ Fields:
187
+
188
+ - `navigation.smoothScroll.enabled`: boolean, default `true`.
189
+ - `navigation.smoothScroll.minimumDurationMs`: positive integer, default
190
+ `2000`.
191
+ - `navigation.smoothScroll.maximumDurationMs`: positive integer, default
192
+ `4000`; must be greater than or equal to `minimumDurationMs`.
193
+ - `navigation.smoothScroll.durationPerPixelMs`: positive number, default
194
+ `0.22`.
195
+
196
+ When `enabled` is `false`, section links jump directly to anchors without the
197
+ controlled animation. The no-JavaScript fallback keeps real `href="#section-id"`
198
+ links.
199
+
200
+ Example:
201
+
202
+ ```js
203
+ navigation: {
204
+ smoothScroll: {
205
+ enabled: true,
206
+ minimumDurationMs: 600,
207
+ maximumDurationMs: 1_200,
208
+ durationPerPixelMs: 0.2,
209
+ },
210
+ }
211
+ ```
212
+
213
+ ## Locale And UI Labels
214
+
215
+ ### `locale.lang`
216
+
217
+ - Purpose: language tag rendered on the root `<html lang="...">` element.
218
+ - Type: string language tag such as `en` or `sv`.
219
+ - Required: no.
220
+ - Default: `en`.
221
+ - Validation: two or three letters, optionally followed by `-` separated
222
+ subtags.
223
+ - Consequence: screen readers, browsers, and search engines use this to
224
+ interpret the page language.
225
+
226
+ ### `locale.labels`
227
+
228
+ - Purpose: site-wide UI labels rendered by the engine for non-editorial
229
+ interface text.
230
+ - Type: object.
231
+ - Required: no; omitted labels use English defaults.
232
+
233
+ Fields:
234
+
235
+ - `locale.labels.skipToContent`: skip-link text, default `Skip to content`.
236
+ - `locale.labels.siteNavigation`: site-level navigation ARIA label and mobile
237
+ menu group heading, default `Pages`.
238
+ - `locale.labels.pageNavigation`: current-page section navigation ARIA label and
239
+ mobile menu group heading, default `On this page`.
240
+ - `locale.labels.sectionNavigation`: legacy section navigation label, default
241
+ `Sections`.
242
+ - `locale.labels.menu`: mobile menu summary text, default `Menu`.
243
+ - `locale.labels.closeMenu`: reserved close-menu label, default `Close menu`.
244
+ - `locale.labels.gallery`: gallery ARIA label prefix, default `Gallery`.
245
+
246
+ Example:
247
+
248
+ ```js
249
+ locale: {
250
+ lang: 'sv',
251
+ labels: {
252
+ skipToContent: 'Hoppa till innehåll',
253
+ siteNavigation: 'Sidor',
254
+ pageNavigation: 'På denna sida',
255
+ sectionNavigation: 'Sektioner',
256
+ menu: 'Meny',
257
+ gallery: 'Galleri',
258
+ },
259
+ }
260
+ ```
261
+
262
+ ## Footer
263
+
264
+ ### `footer.copyrightMessage`
265
+
266
+ - Purpose: optional footer sentence.
267
+ - Type: string.
268
+ - Required: no.
269
+ - Default: hidden.
270
+ - Validation: if set, it must be a non-empty string. `undefined`, `null`, and
271
+ `''` are treated as absent.
272
+ - Consequence: footer rendering is enabled when this or enabled build info is
273
+ present.
274
+
275
+ ### `footer.buildInfo`
276
+
277
+ - Purpose: optional footer build timestamp.
278
+ - Type: object, `false`, `null`, or omitted.
279
+ - Required: no.
280
+ - Default: hidden.
281
+ - Validation: object fields are validated when the object is present. `false`,
282
+ `null`, or omission hides build info.
283
+
284
+ Fields:
285
+
286
+ - `footer.buildInfo.enabled`: boolean, default `true`.
287
+ - `footer.buildInfo.text`: required non-empty string.
288
+ - `footer.buildInfo.dateTimeFormat`: required object.
289
+ - `footer.buildInfo.dateTimeFormat.locale`: required string.
290
+ - `footer.buildInfo.dateTimeFormat.timeZone`: required string.
291
+ - `footer.buildInfo.dateTimeFormat.dateStyle`: required string.
292
+ - `footer.buildInfo.dateTimeFormat.timeStyle`: required string.
293
+
294
+ The date/time object must be accepted by `Intl.DateTimeFormat`.
295
+
296
+ Example:
297
+
298
+ ```js
299
+ footer: {
300
+ copyrightMessage: '(c) Example Artist.',
301
+ buildInfo: {
302
+ enabled: true,
303
+ text: 'Built',
304
+ dateTimeFormat: {
305
+ locale: 'en-GB',
306
+ timeZone: 'UTC',
307
+ dateStyle: 'short',
308
+ timeStyle: 'short',
309
+ },
310
+ },
311
+ }
312
+ ```
313
+
314
+ If both `footer.copyrightMessage` and enabled `footer.buildInfo` are absent, the
315
+ footer is not rendered.
316
+
317
+ ## GitHub
318
+
319
+ ### `github.repo`
320
+
321
+ - Purpose: repository used by deploy checks and deploy monitoring.
322
+ - Type: string in `owner/name` form.
323
+ - Required: yes.
324
+ - Default: none.
325
+ - Validation: non-empty string.
326
+
327
+ ### `github.branch`
328
+
329
+ - Purpose: deploy branch required by `norna deploy` and monitored by
330
+ `deploy:watch`.
331
+ - Type: string.
332
+ - Required: yes.
333
+ - Default: none.
334
+ - Validation: non-empty string.
335
+
336
+ ### `github.pagesWorkflow`
337
+
338
+ - Purpose: GitHub Actions workflow name used by deploy checks and
339
+ `deploy:watch`.
340
+ - Type: string.
341
+ - Required: yes.
342
+ - Default: none.
343
+ - Validation: non-empty string.
344
+
345
+ ## Deploy Watch
346
+
347
+ ### `deploy.watch`
348
+
349
+ - Purpose: defaults for `norna deploy:watch`.
350
+ - Type: object.
351
+ - Required: no; omitted fields use defaults.
352
+
353
+ Fields:
354
+
355
+ - `deploy.watch.intervalMs`: positive integer, default `10000`.
356
+ - `deploy.watch.timeoutMs`: positive integer, default `900000`.
357
+ - `deploy.watch.runLimit`: positive integer, default `10`.
358
+
359
+ Command-line options such as `--interval`, `--timeout`, and `--limit` can
360
+ override these values for one run.
361
+
362
+ ## Site Directory Selection
363
+
364
+ The site directory is not configured in `config.mjs`.
365
+
366
+ Use one of:
367
+
368
+ ```sh
369
+ NORNA_SITE_DIR=my-site npm run norna:build
370
+ norna --site-dir my-site build
371
+ ```
372
+
373
+ If `NORNA_SITE_DIR` is set to an empty value, commands fail. Relative site
374
+ directories are resolved by walking upward from the invocation root until the
375
+ selected directory contains `config.mjs` and `content.md`. Absolute site
376
+ directories are accepted and make their parent the site project root.