@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,384 @@
1
+ # Content
2
+
3
+ `site/content.md` is the editable page file for the homepage of a
4
+ `norna` site. Optional route pages use
5
+ `site/routes/<route-folder>/route-content.md` with the same page frontmatter and
6
+ Markdown section model.
7
+
8
+ Site-wide visual defaults may live in `site/theme.md`. Technical site
9
+ configuration lives in `site/config.mjs`.
10
+
11
+ ## Frontmatter Schema
12
+
13
+ The Astro content schema validates these top-level fields in page files:
14
+
15
+ - `title`: required string. Rendered as the document title.
16
+ - `description`: required string. Rendered as the meta description.
17
+ - `slug`: optional route URL slug. It is ignored on the homepage. If omitted on
18
+ a route page, the route folder name is used.
19
+ - `navigation`: optional page navigation metadata.
20
+ - `presentation`: optional page-level presentation overrides.
21
+ - `frame`: optional page-level frame color source.
22
+ - `sections`: required non-empty array. Defines section order, ids,
23
+ presentation overrides, and gallery rows.
24
+
25
+ `navigation` may contain:
26
+
27
+ - `include`: optional boolean. Defaults to `true`.
28
+ - `label`: optional string. Defaults to `title`.
29
+ - `order`: optional integer. Defaults to `0` for the homepage and `100` for
30
+ route pages.
31
+
32
+ Each `sections[]` item has:
33
+
34
+ - `id`: required string matching `^[a-z0-9-]+$`. Used for anchors, navigation,
35
+ image directories, and Markdown heading ids.
36
+ - `visible`: optional date window that controls whether the section is rendered.
37
+ - `presentation`: optional object with `backgroundColor`, `textColor`, and/or
38
+ `typography` overrides.
39
+ - `gallery`: optional array, defaulting to `[]`.
40
+
41
+ Each gallery row has:
42
+
43
+ - `image`: required filename matching `^[a-z0-9][a-z0-9.-]*\.(jpe?g|png)$`.
44
+ It must be a filename, not a path.
45
+ - `alt`: required string.
46
+ - `caption`: optional string.
47
+
48
+ ## Routes
49
+
50
+ The homepage is always `site/content.md` and builds to `/`.
51
+
52
+ Add a first-level route by creating:
53
+
54
+ ```text
55
+ site/routes/about/route-content.md
56
+ ```
57
+
58
+ Minimal route page:
59
+
60
+ ```md
61
+ ---
62
+ title: About
63
+ description: About this gallery.
64
+ navigation:
65
+ label: About
66
+ order: 20
67
+ sections:
68
+ - id: intro
69
+ ---
70
+
71
+ ## Intro {#intro}
72
+
73
+ Text...
74
+ ```
75
+
76
+ The example above builds to `/about/`. If `slug` is omitted, the route folder
77
+ name is used. If `slug` is set, it must use lowercase letters, numbers, and
78
+ hyphens. Keep the route folder and `slug` aligned unless you intentionally need
79
+ a different URL.
80
+
81
+ Route images live under the route:
82
+
83
+ ```text
84
+ site/routes/about/images/intro/image.jpg
85
+ ```
86
+
87
+ Image references in route frontmatter still use only the filename.
88
+
89
+ ### Navigation Scope
90
+
91
+ Current route navigation is intended for small sites. This guidance may change
92
+ as route support matures, but the present model is:
93
+
94
+ - A single-page site should normally use only page navigation between sections.
95
+ - A small multi-page site may use site navigation between routes plus page
96
+ navigation between sections on the current page.
97
+ - If a site needs many routes, deeply nested routes, or several navigation
98
+ levels, it has probably outgrown the current sticky-navigation model and may
99
+ need a different site structure or navigation system.
100
+
101
+ ## Site Theme
102
+
103
+ `site/theme.md` optionally defines site-wide visual defaults. It uses
104
+ frontmatter and does not need a Markdown body. If the file is missing, built-in
105
+ engine defaults are used.
106
+
107
+ Starter sites include a marked comment block such as
108
+ `norna:start theme-help` / `norna:end theme-help`. The block is
109
+ only explorable help text; YAML comments do not affect rendering. The active
110
+ configuration is the uncommented YAML below it.
111
+
112
+ ```yaml
113
+ ---
114
+ # norna:start theme-help
115
+ # Site-wide visual defaults. Remove a value to use the engine default.
116
+ # norna:end theme-help
117
+
118
+ presentation:
119
+ backgroundColor: "#000000"
120
+ textColor: "#f7f4ee"
121
+ inlineStyles:
122
+ highlight:
123
+ color: "#ffd84d"
124
+ typography:
125
+ preset: quiet-gallery
126
+ frame:
127
+ colors: presentation
128
+ ---
129
+ ```
130
+
131
+ `presentation.backgroundColor` and `presentation.textColor` are optional quoted
132
+ hex colors in `#rgb`, `#rrggbb`, or `#rrggbbaa` form.
133
+
134
+ `presentation.inlineStyles` defines named inline text styles that can be used
135
+ from Markdown. Inline style names must match `^[a-z][a-z0-9-]*$`. Each style
136
+ currently supports a required `color` field using the same quoted hex color
137
+ format as `textColor`.
138
+
139
+ `frame.colors` controls the sticky navigation and footer colors:
140
+
141
+ - `presentation`: use the resolved presentation colors for this level.
142
+ - `theme`: use the site theme frame colors. This is useful in page-level
143
+ frontmatter.
144
+ - explicit colors:
145
+
146
+ ```yaml
147
+ frame:
148
+ colors:
149
+ backgroundColor: "#111111"
150
+ textColor: "#eeeeee"
151
+ ```
152
+
153
+ ## Temporary Sections
154
+
155
+ Use `sections[].visible` for sections that should be rendered only during a
156
+ date window:
157
+
158
+ ```yaml
159
+ sections:
160
+ - id: exhibition
161
+ visible:
162
+ from: "2026-08-01"
163
+ until: "2026-09-16"
164
+ gallery: []
165
+ ```
166
+
167
+ `from` is inclusive. `until` is exclusive. With the example above, the section
168
+ is visible from 2026-08-01 through 2026-09-15 and hidden again on 2026-09-16.
169
+
170
+ Both `from` and `until` use `YYYY-MM-DD`. Either value may be omitted, but a
171
+ `visible` object must contain at least one of them.
172
+
173
+ Hidden sections are omitted from the rendered HTML and sticky navigation. They
174
+ remain in `content.md`, and `content:check` still validates their matching
175
+ Markdown headings and gallery image references.
176
+
177
+ The current date is evaluated at dev/build time. Set `NORNA_TODAY` to
178
+ preview or test a specific date:
179
+
180
+ ```sh
181
+ NORNA_TODAY=2026-08-15 npm run norna:build
182
+ ```
183
+
184
+ ## Presentation
185
+
186
+ Site-wide presentation belongs in `site/theme.md`. Page-level presentation in
187
+ `site/content.md` is always an override on top of the theme. Section-specific
188
+ presentation belongs under `sections[].presentation`.
189
+
190
+ If a page omits `presentation`, it uses the theme presentation unchanged. If a
191
+ section omits `presentation`, it uses the resolved page presentation.
192
+
193
+ ```yaml
194
+ presentation:
195
+ typography:
196
+ overrides:
197
+ body:
198
+ paragraphSpacing: 1em
199
+ ```
200
+
201
+ ### Typography Presets
202
+
203
+ Typographic presentation is configured through presets with optional overrides:
204
+
205
+ ```yaml
206
+ presentation:
207
+ typography:
208
+ preset: quiet-gallery
209
+ overrides:
210
+ body:
211
+ paragraphSpacing: 0.8em
212
+ ```
213
+
214
+ Available presets:
215
+
216
+ - `quiet-gallery`: the default for image-led art and portfolio sites. Text is
217
+ restrained and supports the images without dominating the page.
218
+ - `compact-gallery`: tighter typography for many sections, many images, or
219
+ short information blocks.
220
+ - `text-forward`: more generous body text for pages where longer text carries
221
+ more of the experience.
222
+ - `statement`: stronger type for introductions, first sections, and short
223
+ programmatic statements. Use it sparingly, usually as a section override.
224
+
225
+ The normal place to choose a site-wide preset is `site/theme.md`. If theme
226
+ typography is omitted, `quiet-gallery` is used. A page-level
227
+ `presentation.typography.preset` changes the typographic base for the page. A
228
+ section-level `sections[].presentation.typography.preset` changes the
229
+ typographic base for that section.
230
+
231
+ Use this command to inspect the exact preset values shipped with the installed
232
+ engine:
233
+
234
+ ```sh
235
+ norna typography:presets
236
+ ```
237
+
238
+ Use this command to inspect the effective values for the selected site after
239
+ presets and overrides have been applied:
240
+
241
+ ```sh
242
+ norna typography:show
243
+ ```
244
+
245
+ The typographic roles are:
246
+
247
+ - `heading`: section headings.
248
+ - `body`: Markdown body text inside sections.
249
+ - `caption`: gallery captions.
250
+
251
+ Allowed alignment values are `left`, `center`, and `right`. Alignment can be
252
+ responsive:
253
+
254
+ ```yaml
255
+ align:
256
+ desktop: left
257
+ mobile: center
258
+ ```
259
+
260
+ Allowed size values are `small`, `medium`, `large`, and `xlarge`.
261
+ `lineHeight` is a unitless number from `1` through `3`. `spacing` and
262
+ `paragraphSpacing` are CSS lengths such as `0`, `0.8em`, `1rem`, or `12px`.
263
+
264
+ Supported override fields:
265
+
266
+ - `heading.align`, `heading.size`, `heading.lineHeight`, `heading.spacing`
267
+ - `body.align`, `body.size`, `body.lineHeight`, `body.paragraphSpacing`
268
+ - `caption.align`, `caption.size`, `caption.lineHeight`, `caption.spacing`
269
+
270
+ Use `site/theme.md` `presentation.inlineStyles` for named inline text styles
271
+ that can be applied inside Markdown:
272
+
273
+ ```yaml
274
+ presentation:
275
+ inlineStyles:
276
+ highlight:
277
+ color: "#ffd84d"
278
+ ```
279
+
280
+ Apply an inline style in Markdown with `[text]{.style-name}`:
281
+
282
+ ```md
283
+ This sentence contains [highlighted text]{.highlight}.
284
+ ```
285
+
286
+ `content:check` fails if Markdown uses an inline style that is not defined in
287
+ `site/theme.md` `presentation.inlineStyles`.
288
+
289
+ `sections[].presentation` contains only section-specific differences:
290
+
291
+ ```yaml
292
+ sections:
293
+ - id: intro
294
+ presentation:
295
+ backgroundColor: "#161616"
296
+ textColor: "#ffffff"
297
+ typography:
298
+ preset: statement
299
+ overrides:
300
+ body:
301
+ paragraphSpacing: 0.7em
302
+ ```
303
+
304
+ If a section sets `typography.preset`, that section starts from that preset. If
305
+ a section only sets `typography.overrides`, it keeps the resolved page preset
306
+ and changes only the specified values.
307
+
308
+ Centered text uses narrower text widths. Left- or right-aligned heading and body
309
+ text use the calculated gallery layout width so text edges line up with gallery
310
+ images after layout gutters and gallery limits are applied.
311
+ Configured section backgrounds render as full-width horizontal bands while the
312
+ section content keeps the normal page and gallery widths. The top spacing
313
+ before the first heading, the spacing between sections, and the spacing after
314
+ the final section are part of the section background. The sticky section
315
+ navigation row and footer use the resolved frame colors, not section-specific
316
+ presentation.
317
+ Configured section text colors apply to section headings, Markdown text,
318
+ Markdown subheadings, and gallery captions. Links keep the global accent color.
319
+
320
+ ## Markdown Sections
321
+
322
+ Every frontmatter section must have a matching level 2 Markdown heading with an
323
+ explicit id:
324
+
325
+ ```md
326
+ ## Work {#work}
327
+
328
+ Introductory text.
329
+ ```
330
+
331
+ Keep these values aligned:
332
+
333
+ - the frontmatter `sections[].id`
334
+ - the Markdown heading id
335
+ - the source image directory `site/images/<section-id>/`
336
+
337
+ The visible navigation label comes from the Markdown heading text, not from the
338
+ frontmatter id.
339
+
340
+ ## Validation And Sync
341
+
342
+ Run:
343
+
344
+ ```sh
345
+ npm run norna:content:check
346
+ ```
347
+
348
+ This checks section order and heading ids, duplicate image names, missing image
349
+ files, misplaced referenced images, duplicate gallery references, invalid image
350
+ references, unreferenced images, undefined inline styles, and common
351
+ frontmatter indentation and structure mistakes.
352
+
353
+ Frontmatter uses YAML indentation. Use ordinary spaces, not tabs or
354
+ non-breaking spaces. `content:check` reports a focused error when indentation is
355
+ invalid, when a key is indented under a line that already has a value, or when
356
+ a known nested key such as `gallery` appears at the top level:
357
+
358
+ ```yaml
359
+ typography:
360
+ preset: quiet-gallery
361
+ overrides:
362
+ body:
363
+ paragraphSpacing: 0.8em
364
+ ```
365
+
366
+ Top-level page frontmatter may contain only `title`, `description`,
367
+ `presentation`, `frame`, and `sections`. A `gallery` key belongs under one
368
+ `sections[]` item:
369
+
370
+ ```yaml
371
+ sections:
372
+ - id: work
373
+ gallery: []
374
+ ```
375
+
376
+ Run:
377
+
378
+ ```sh
379
+ npm run norna:sync
380
+ ```
381
+
382
+ This rewrites Markdown sections into frontmatter order and moves referenced
383
+ image files into the directory matching their section. It prompts before
384
+ writing unless `--yes` is passed.
@@ -0,0 +1,164 @@
1
+ # Engine Development
2
+
3
+ This document is for work on the reusable `norna` package itself.
4
+
5
+ ## Main Areas
6
+
7
+ - `bin/norna.mjs`: public CLI dispatcher.
8
+ - `scripts/lib/site-paths.mjs`: engine/site path resolution.
9
+ - `scripts/lib/project-config.mjs`: `site/config.mjs` validation and defaults.
10
+ - `scripts/sync-content-sections.mjs`: content validation and sync behavior.
11
+ - `scripts/generate-images.mjs`: WebP image pipeline and manifest.
12
+ - `scripts/sync-site-public.mjs`: static public file sync.
13
+ - `scripts/deploy-site.mjs`: deploy and deploy:commit behavior.
14
+ - `scripts/watch-pages-deploy.mjs`: GitHub Pages workflow monitor.
15
+ - `src/content.config.ts`: Astro content schema.
16
+ - `src/components/` and `src/layouts/`: rendered page, navigation, gallery, and
17
+ layout.
18
+ - `tests/`: Playwright navigation diagnostics.
19
+ - `fixtures/basic/site/`: minimal site used for engine checks.
20
+ - `starters/basic/`: copyable site starter.
21
+
22
+ The repository-local `site/` directory is a dog-gallery demo. It is useful for
23
+ manual engine checks, but it is not a published site.
24
+
25
+ ## Common Checks
26
+
27
+ Run focused checks while developing:
28
+
29
+ ```sh
30
+ npm run test:content-check
31
+ npm run test:site-public
32
+ npm run test:fixture:build
33
+ npm run demo:build
34
+ npm run package:check
35
+ ```
36
+
37
+ `npm run test` runs the same set in sequence.
38
+
39
+ Navigation diagnostics are separate because they use Playwright:
40
+
41
+ ```sh
42
+ npm run test:navigation
43
+ npm run test:navigation:stress
44
+ npm run test:navigation:preview
45
+ ```
46
+
47
+ If Chromium is missing:
48
+
49
+ ```sh
50
+ npx playwright install chromium
51
+ ```
52
+
53
+ ## Package Check
54
+
55
+ `npm run package:check` packs this repository, extracts the package, copies the
56
+ packaged starter into a temporary site project, installs dependencies, runs
57
+ `norna doctor` from a site subdirectory, runs config/content checks,
58
+ builds the installed site, and verifies selected rendered output and validation
59
+ failures.
60
+
61
+ It needs network access when npm dependencies are not already cached.
62
+ The check uses a reusable npm cache at
63
+ `node_modules/.cache/norna-package-check-npm` and runs npm with
64
+ `--prefer-offline` so repeat runs do not redownload dependencies. Set
65
+ `NORNA_PACKAGE_CHECK_CACHE=/path/to/cache` to use another cache.
66
+
67
+ ## CI Lockfiles
68
+
69
+ Site repositories are developed on different operating systems but deployed on
70
+ GitHub Actions Linux. `engine:update` therefore normalizes `package-lock.json`
71
+ with npm 11.16.0 for Linux x64 glibc and verifies a clean install before it runs
72
+ the site checks. The starter workflow pins Node 24.18.0, which provides the
73
+ same npm version.
74
+
75
+ `npm run test:ci-lockfile` recreates and repairs the optional npm peer-dependency
76
+ case that previously caused GitHub Actions `npm ci` failures.
77
+
78
+ ## Test The Package In A Site Repository
79
+
80
+ > **Warning:** `npm link` is not supported for `norna`. Astro resolves
81
+ > renderer modules and runtime dependencies differently when the package is a
82
+ > symlink, so a linked site can fail to start even though the published package
83
+ > works. Do not use `npm link @janga/norna` to test a site.
84
+
85
+ Use `npm run package:check` to test the package before release. To test a
86
+ specific published engine version in a real site, update it with:
87
+
88
+ ```sh
89
+ npm run norna:engine:update -- <version>
90
+ ```
91
+
92
+ Commit the resulting `package.json` and `package-lock.json` changes in the
93
+ site repository after the site's normal checks pass.
94
+
95
+ ## npm Release
96
+
97
+ The npm package is published under the `@janga` scope. Choose the release type
98
+ when starting a release; the command requires a clean working tree, verifies npm
99
+ registry authentication for the same registry/cache used by `release:publish`,
100
+ runs `npm test`, requires a clean working tree after the checks, updates
101
+ `package.json` and `package-lock.json`, creates the release commit and Git tag,
102
+ publishes to npm, then pushes the commit and tag.
103
+
104
+ ```sh
105
+ npm run release:patch
106
+ ```
107
+
108
+ Use `patch` for backwards-compatible fixes and maintenance changes. Use `minor`
109
+ for backwards-compatible features and `major` for incompatible changes:
110
+
111
+ ```sh
112
+ npm run release:minor
113
+ npm run release:major
114
+ ```
115
+
116
+ The release command deliberately does not run GitHub Pages deployment monitoring.
117
+ If npm publication fails, it stops before pushing; the local version commit and
118
+ tag remain available for inspection or recovery.
119
+
120
+ If the npm authentication preflight fails, no version commit or tag has been
121
+ created yet. Run the printed login command:
122
+
123
+ ```sh
124
+ npm login --registry=https://registry.npmjs.org/ --auth-type=web --cache /private/tmp/norna-npm-cache
125
+ ```
126
+
127
+ Then start the release command again.
128
+
129
+ After publication, update site repositories to the exact published npm version
130
+ and commit their updated `package-lock.json`.
131
+
132
+ ## Rendering Notes
133
+
134
+ The renderer builds the homepage at `/` and optional first-level routes from
135
+ `site/routes/<route-folder>/route-content.md`.
136
+
137
+ Navigation has two separate levels:
138
+
139
+ - Site navigation moves between pages and routes. It uses normal page URLs and
140
+ browser history.
141
+ - Page navigation moves between sections on the current page. It uses real
142
+ `href="#section-id"` links so anchors work without JavaScript.
143
+
144
+ The current navigation model is deliberately scoped to single-page and small
145
+ multi-page sites. That scope may change as route support matures. For now,
146
+ single-page sites should rely on page navigation only; small route-based sites
147
+ may combine site navigation and page navigation; larger information
148
+ architectures should not be forced into the sticky-navigation model without a
149
+ separate design decision.
150
+
151
+ The JavaScript enhancement keeps the URL hash as the source of truth for active
152
+ page-navigation state. A section-link click pushes one hash entry into browser
153
+ history, back/forward moves between hash entries, and returning to the same page
154
+ without a hash restores the first section as active. The enhancement does not
155
+ derive active section state from free manual scrolling.
156
+
157
+ The sticky navigation updates root scroll offset variables so direct hash links
158
+ and clicked links land below the fixed header. Hash targets may be corrected
159
+ again after layout shifts such as image loading, but that correction must not
160
+ add history entries or change the active section independently of the URL hash.
161
+
162
+ The shared layout reads UI language and labels from `site/config.mjs`
163
+ `locale`. Keep editorial content in page Markdown and non-editorial engine UI
164
+ labels in config.
@@ -0,0 +1,96 @@
1
+ # Getting Started
2
+
3
+ This guide creates a new site repository from the included starter. It describes
4
+ the generic `norna` setup; site-specific content belongs in the new site
5
+ repository.
6
+
7
+ ## Requirements
8
+
9
+ - Node.js `>=22.12.0`
10
+ - ImageMagick for image generation
11
+ - GitHub CLI (`gh`) for deploy checks and deploy monitoring
12
+
13
+ Install Playwright Chromium only when you plan to run navigation diagnostics:
14
+
15
+ ```sh
16
+ npx playwright install chromium
17
+ ```
18
+
19
+ ## Create A Site Repository
20
+
21
+ Create the site from the starter first, then install dependencies inside the
22
+ new site directory:
23
+
24
+ ```sh
25
+ cd path/to/your/projects
26
+ npx @janga/norna@latest init my-gallery
27
+ cd my-gallery
28
+ npm install
29
+ npm run norna:dev
30
+ ```
31
+
32
+ A freshly created empty directory is not a `norna` site yet. It becomes a
33
+ site when `init` copies the starter files, including `package.json`. If you run
34
+ `npm install` in an empty directory, npm may walk up to a parent directory and
35
+ install dependencies for another project instead.
36
+
37
+ For normal site work, keep site repositories as siblings of the `norna`
38
+ engine repository. For example:
39
+
40
+ ```text
41
+ Projects/
42
+ norna/
43
+ my-gallery/
44
+ ```
45
+
46
+ The starter contains:
47
+
48
+ - `package.json` with npm scripts that call `norna`
49
+ - `.github/workflows/deploy.yml` for GitHub Pages
50
+ - `site/config.mjs`
51
+ - `site/theme.md`
52
+ - `site/content.md`
53
+ - `site/images/work/.gitkeep`
54
+ - `site/public/robots.txt`
55
+
56
+ Commit the generated `package-lock.json` after the first install.
57
+
58
+ ## First Edits
59
+
60
+ 1. Edit `site/config.mjs` for the site's URL, layout, font, language labels,
61
+ GitHub repository, footer, and deploy settings.
62
+ 2. Edit `site/theme.md` for site-wide colors, typography preset, inline styles,
63
+ and frame colors, or omit it to use engine defaults.
64
+ 3. Edit `site/content.md` for homepage title, description, sections, text,
65
+ gallery rows, page/section presentation overrides, alt text, and captions.
66
+ 4. Put source images under `site/images/<section-id>/`.
67
+ 5. Add optional route pages under
68
+ `site/routes/<route-folder>/route-content.md`.
69
+ 6. Put static files such as `robots.txt`, `CNAME`, and favicons under
70
+ `site/public/`.
71
+ 7. Run:
72
+
73
+ ```sh
74
+ npm run norna:check
75
+ npm run build
76
+ ```
77
+
78
+ Read [Site Structure](site-structure.md), [Content](content.md), and
79
+ [Configuration](configuration.md) before publishing a real site.
80
+
81
+ To change the site's maximum page width, set `layout.pageWidth` in
82
+ `site/config.mjs`. To change side margins, set `layout.gutter`. To change the
83
+ maximum gallery width inside that page area, set `gallery.width`. To keep
84
+ images within a comfortable viewport height, set
85
+ `gallery.maxAvailableHeightPercent`. See [Configuration](configuration.md) for
86
+ the accepted formats and default values.
87
+
88
+ To change the site's font, set `typography.fontFamily` in `site/config.mjs`.
89
+ See [`typography.fontFamily`](configuration.md#typographyfontfamily) for the
90
+ accepted format and default value.
91
+
92
+ To change the site-wide typography preset, edit `site/theme.md`. To change one
93
+ page or section, add a focused `presentation.typography` override in
94
+ `site/content.md`. Use `npm run norna:typography:presets` to inspect the
95
+ installed preset values and `npm run norna:typography:show` to inspect the
96
+ resolved typography for the selected site.