@janga/norna 0.7.6 → 0.7.7

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 (137) hide show
  1. package/README.md +5 -5
  2. package/astro.config.mjs +76 -0
  3. package/bin/norna-cli.mjs +2 -2
  4. package/docs/README.md +3 -3
  5. package/docs/commands.md +6 -6
  6. package/docs/configuration.md +3 -2
  7. package/docs/content.md +152 -91
  8. package/docs/design/site-examples-structure.md +1 -1
  9. package/docs/engine-development.md +3 -3
  10. package/docs/getting-started.md +4 -3
  11. package/docs/images-and-metadata.md +112 -20
  12. package/docs/routes.md +9 -12
  13. package/docs/site-structure.md +4 -3
  14. package/docs/theme.md +8 -7
  15. package/docs/typography.md +5 -5
  16. package/examples/dog-gallery/site/content.md +41 -45
  17. package/examples/dog-gallery/site/routes/010-dog-care/route-content.md +1 -3
  18. package/fixtures/basic/site/content.md +2 -4
  19. package/fixtures/content-model-v2/.astro/collections/site.schema.json +776 -0
  20. package/fixtures/content-model-v2/.astro/collections/theme.schema.json +668 -0
  21. package/fixtures/content-model-v2/.astro/content-assets.mjs +4 -0
  22. package/fixtures/content-model-v2/.astro/content-modules.mjs +1 -0
  23. package/fixtures/content-model-v2/.astro/content.d.ts +175 -0
  24. package/fixtures/content-model-v2/.astro/data-store.json +1 -0
  25. package/fixtures/content-model-v2/.astro/dev-local.json +8 -0
  26. package/fixtures/content-model-v2/.astro/dev.json +13 -0
  27. package/fixtures/content-model-v2/.astro/dev.log +231 -0
  28. package/fixtures/content-model-v2/.astro/settings.json +5 -0
  29. package/fixtures/content-model-v2/.astro/types.d.ts +2 -0
  30. package/fixtures/content-model-v2/dist/_astro/GalleryGrid.astro_astro_type_script_index_0_lang.B0kxDyGz.js +1 -0
  31. package/fixtures/content-model-v2/dist/_astro/SitePage.BbfZub4g.css +1 -0
  32. package/fixtures/content-model-v2/dist/guide/index.html +3 -0
  33. package/fixtures/content-model-v2/dist/images/generated/intro/detail-27930b14-480.webp +0 -0
  34. package/fixtures/content-model-v2/dist/images/generated/intro/detail-27930b14-768.webp +0 -0
  35. package/fixtures/content-model-v2/dist/images/generated/intro/detail-27930b14-971.webp +0 -0
  36. package/fixtures/content-model-v2/dist/images/generated/intro/duplicate-49c98f6c-480.webp +0 -0
  37. package/fixtures/content-model-v2/dist/images/generated/intro/duplicate-49c98f6c-768.webp +0 -0
  38. package/fixtures/content-model-v2/dist/images/generated/intro/duplicate-49c98f6c-971.webp +0 -0
  39. package/fixtures/content-model-v2/dist/images/generated/intro/hero-6d588a83-480.webp +0 -0
  40. package/fixtures/content-model-v2/dist/images/generated/intro/hero-6d588a83-768.webp +0 -0
  41. package/fixtures/content-model-v2/dist/images/generated/intro/hero-6d588a83-971.webp +0 -0
  42. package/fixtures/content-model-v2/dist/images/generated/plain/image-3770d763-1080.webp +0 -0
  43. package/fixtures/content-model-v2/dist/images/generated/plain/image-3770d763-1440.webp +0 -0
  44. package/fixtures/content-model-v2/dist/images/generated/plain/image-3770d763-1600.webp +0 -0
  45. package/fixtures/content-model-v2/dist/images/generated/plain/image-3770d763-480.webp +0 -0
  46. package/fixtures/content-model-v2/dist/images/generated/plain/image-3770d763-768.webp +0 -0
  47. package/fixtures/content-model-v2/dist/images/generated/routes/010-guide/images/intro/duplicate-5cf614d2-480.webp +0 -0
  48. package/fixtures/content-model-v2/dist/images/generated/routes/010-guide/images/intro/duplicate-5cf614d2-768.webp +0 -0
  49. package/fixtures/content-model-v2/dist/images/generated/routes/010-guide/images/intro/duplicate-5cf614d2-960.webp +0 -0
  50. package/fixtures/content-model-v2/dist/images/generated/timed/slide-one-3770d763-1080.webp +0 -0
  51. package/fixtures/content-model-v2/dist/images/generated/timed/slide-one-3770d763-1440.webp +0 -0
  52. package/fixtures/content-model-v2/dist/images/generated/timed/slide-one-3770d763-1600.webp +0 -0
  53. package/fixtures/content-model-v2/dist/images/generated/timed/slide-one-3770d763-480.webp +0 -0
  54. package/fixtures/content-model-v2/dist/images/generated/timed/slide-one-3770d763-768.webp +0 -0
  55. package/fixtures/content-model-v2/dist/images/generated/timed/slide-three-5bdec65e-1080.webp +0 -0
  56. package/fixtures/content-model-v2/dist/images/generated/timed/slide-three-5bdec65e-1440.webp +0 -0
  57. package/fixtures/content-model-v2/dist/images/generated/timed/slide-three-5bdec65e-1600.webp +0 -0
  58. package/fixtures/content-model-v2/dist/images/generated/timed/slide-three-5bdec65e-480.webp +0 -0
  59. package/fixtures/content-model-v2/dist/images/generated/timed/slide-three-5bdec65e-768.webp +0 -0
  60. package/fixtures/content-model-v2/dist/images/generated/timed/slide-two-26c08aee-1080.webp +0 -0
  61. package/fixtures/content-model-v2/dist/images/generated/timed/slide-two-26c08aee-1440.webp +0 -0
  62. package/fixtures/content-model-v2/dist/images/generated/timed/slide-two-26c08aee-1600.webp +0 -0
  63. package/fixtures/content-model-v2/dist/images/generated/timed/slide-two-26c08aee-480.webp +0 -0
  64. package/fixtures/content-model-v2/dist/images/generated/timed/slide-two-26c08aee-768.webp +0 -0
  65. package/fixtures/content-model-v2/dist/index.html +8 -0
  66. package/fixtures/content-model-v2/node_modules/.vite/deps/_metadata.json +80 -0
  67. package/fixtures/content-model-v2/node_modules/.vite/deps/astro-D_GIpJIE.js +434 -0
  68. package/fixtures/content-model-v2/node_modules/.vite/deps/astro-D_GIpJIE.js.map +1 -0
  69. package/fixtures/content-model-v2/node_modules/.vite/deps/astro_n_aria-query.js +6390 -0
  70. package/fixtures/content-model-v2/node_modules/.vite/deps/astro_n_aria-query.js.map +1 -0
  71. package/fixtures/content-model-v2/node_modules/.vite/deps/astro_n_axobject-query.js +2702 -0
  72. package/fixtures/content-model-v2/node_modules/.vite/deps/astro_n_axobject-query.js.map +1 -0
  73. package/fixtures/content-model-v2/node_modules/.vite/deps/astro_n_html-escaper.js +65 -0
  74. package/fixtures/content-model-v2/node_modules/.vite/deps/astro_n_html-escaper.js.map +1 -0
  75. package/fixtures/content-model-v2/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +455 -0
  76. package/fixtures/content-model-v2/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +1 -0
  77. package/fixtures/content-model-v2/node_modules/.vite/deps/audit-DM9Hkl5B.js +1534 -0
  78. package/fixtures/content-model-v2/node_modules/.vite/deps/audit-DM9Hkl5B.js.map +1 -0
  79. package/fixtures/content-model-v2/node_modules/.vite/deps/embla-carousel.js +1389 -0
  80. package/fixtures/content-model-v2/node_modules/.vite/deps/embla-carousel.js.map +1 -0
  81. package/fixtures/content-model-v2/node_modules/.vite/deps/highlight-DnZ1ULOL.js +48 -0
  82. package/fixtures/content-model-v2/node_modules/.vite/deps/highlight-DnZ1ULOL.js.map +1 -0
  83. package/fixtures/content-model-v2/node_modules/.vite/deps/icons-RyebKi45.js +42 -0
  84. package/fixtures/content-model-v2/node_modules/.vite/deps/icons-RyebKi45.js.map +1 -0
  85. package/fixtures/content-model-v2/node_modules/.vite/deps/package.json +3 -0
  86. package/fixtures/content-model-v2/node_modules/.vite/deps/rolldown-runtime-BvCyGRYZ.js +4 -0
  87. package/fixtures/content-model-v2/node_modules/.vite/deps/settings-DaU-gNnK.js +190 -0
  88. package/fixtures/content-model-v2/node_modules/.vite/deps/settings-DaU-gNnK.js.map +1 -0
  89. package/fixtures/content-model-v2/node_modules/.vite/deps/toolbar-63PA619H.js +458 -0
  90. package/fixtures/content-model-v2/node_modules/.vite/deps/toolbar-63PA619H.js.map +1 -0
  91. package/fixtures/content-model-v2/node_modules/.vite/deps/ui-library--bkcNSYz.js +1016 -0
  92. package/fixtures/content-model-v2/node_modules/.vite/deps/ui-library--bkcNSYz.js.map +1 -0
  93. package/fixtures/content-model-v2/node_modules/.vite/deps/window-jPkpKVcy.js +32 -0
  94. package/fixtures/content-model-v2/node_modules/.vite/deps/window-jPkpKVcy.js.map +1 -0
  95. package/fixtures/content-model-v2/node_modules/.vite/deps/xray-BXCHpEJP.js +111 -0
  96. package/fixtures/content-model-v2/node_modules/.vite/deps/xray-BXCHpEJP.js.map +1 -0
  97. package/fixtures/content-model-v2/site/.norna/generated-images.json +210 -0
  98. package/fixtures/content-model-v2/site/config.mjs +11 -0
  99. package/fixtures/content-model-v2/site/content.md +84 -0
  100. package/fixtures/content-model-v2/site/images/card-section/adopt.svg +9 -0
  101. package/fixtures/content-model-v2/site/images/card-section/foster.svg +12 -0
  102. package/fixtures/content-model-v2/site/images/intro/detail.jpg +0 -0
  103. package/fixtures/content-model-v2/site/images/intro/duplicate.jpg +0 -0
  104. package/fixtures/content-model-v2/site/images/intro/hero.jpg +0 -0
  105. package/fixtures/content-model-v2/site/images/plain/image.jpg +0 -0
  106. package/fixtures/content-model-v2/site/images/timed/slide-one.jpg +0 -0
  107. package/fixtures/content-model-v2/site/images/timed/slide-three.jpg +0 -0
  108. package/fixtures/content-model-v2/site/images/timed/slide-two.jpg +0 -0
  109. package/fixtures/content-model-v2/site/routes/010-guide/images/intro/duplicate.jpg +0 -0
  110. package/fixtures/content-model-v2/site/routes/010-guide/route-content.md +17 -0
  111. package/fixtures/content-model-v2/site/theme.md +9 -0
  112. package/package.json +3 -2
  113. package/scripts/deploy-site.mjs +11 -4
  114. package/scripts/dev-local.mjs +6 -0
  115. package/scripts/generate-images.mjs +132 -56
  116. package/scripts/lib/image-dimensions.mjs +26 -0
  117. package/scripts/lib/norna-markdown-blocks.mjs +643 -0
  118. package/scripts/lib/site-content.mjs +31 -29
  119. package/scripts/show-typography.mjs +17 -9
  120. package/scripts/sync-content-sections.mjs +280 -411
  121. package/scripts/test-content-check.mjs +59 -45
  122. package/scripts/test-content-model-v2.mjs +998 -0
  123. package/scripts/test-engine-commands.mjs +1 -1
  124. package/scripts/test-package-check.mjs +36 -8
  125. package/scripts/test-temporary-visibility.mjs +3 -6
  126. package/src/components/CardList.astro +77 -0
  127. package/src/components/GalleryGrid.astro +22 -12
  128. package/src/components/SiteNavigation.astro +5 -4
  129. package/src/components/SitePage.astro +9 -2
  130. package/src/components/SiteSection.astro +35 -13
  131. package/src/content.config.ts +4 -17
  132. package/src/lib/generatedImages.ts +50 -10
  133. package/src/lib/sectionContent.ts +130 -35
  134. package/src/styles/global.css +187 -0
  135. package/starters/basic/site/content.md +10 -6
  136. package/starters/project/site/content.md +0 -9
  137. package/starters/project/site/routes/010-guide/route-content.md +0 -5
@@ -1,26 +1,78 @@
1
1
  # Images And Metadata
2
2
 
3
- This document describes the generic image pipeline. Site repositories own their
3
+ This document describes Norna's image pipeline. Site repositories own their
4
4
  source images and any copyright or licensing policy for those images.
5
5
 
6
- ## Source Images
6
+ ## Managed Source Images
7
7
 
8
- Source images live under `site/images/<section-id>/` and are referenced from
9
- `site/content.md` by filename only.
8
+ Norna-managed local images are referenced from Markdown with
9
+ `norna-image-stack` or `norna-image-carousel` blocks. See
10
+ [Content](content.md#image-blocks) for the block syntax.
11
+
12
+ Homepage source images live under:
13
+
14
+ ```text
15
+ site/images/<section-id>/
16
+ ```
17
+
18
+ Route source images live under:
19
+
20
+ ```text
21
+ site/routes/<NNN-route-id>/images/<section-id>/
22
+ ```
10
23
 
11
24
  Supported source extensions:
12
25
 
13
26
  - `.jpg`
14
27
  - `.jpeg`
15
28
  - `.png`
29
+ - `.svg`
30
+
31
+ Image block references use only the filename:
32
+
33
+ ````md
34
+ ```norna-image-stack
35
+ - image: portrait.jpg
36
+ alt: Optional alt text.
37
+ caption: Optional caption.
38
+ ```
39
+ ````
16
40
 
17
- Image filenames must be globally unique under `site/images/`. The content and
18
- image scripts reject duplicate filenames because image rows identify images by
19
- filename only.
41
+ `alt` and `caption` are optional. If `alt` is omitted, Norna renders an empty
42
+ alt attribute.
43
+
44
+ For generated or edited images, keep provenance near the image in a source-only
45
+ comment. Norna removes these comments from rendered HTML:
46
+
47
+ ```md
48
+ <!-- norna-image-provenance:
49
+ image: portrait.jpg
50
+ source: generated
51
+ prompt: Short prompt or editing note.
52
+ -->
53
+ ```
20
54
 
21
- ## Generated Variants
55
+ Filenames do not have to be globally unique for the site to be valid.
56
+ Automatic sync only moves files when the filename identifies exactly one source
57
+ candidate within the current page or route.
22
58
 
23
- `npm run norna:images` and `npm run norna:build` generate WebP files in:
59
+ ## Markdown Images
60
+
61
+ Use ordinary Markdown images for external images or static public assets:
62
+
63
+ ```md
64
+ ![External image](https://example.com/image.jpg)
65
+ ![Public asset](/favicon.svg)
66
+ ```
67
+
68
+ Relative local Markdown images such as `![Portrait](portrait.jpg)` are not
69
+ managed by Norna. `content:check` warns about them because Norna cannot
70
+ validate, process, or sync those files through the image pipeline.
71
+
72
+ ## Generated Variants And Static SVG
73
+
74
+ For raster images, `npm run norna:images` and `npm run norna:build` generate
75
+ WebP files in:
24
76
 
25
77
  ```text
26
78
  site/.norna/public/images/generated/
@@ -46,6 +98,19 @@ example-work-1a2b3c4d-1440.webp
46
98
  When a source image changes, the generated URL changes too. This avoids stale
47
99
  browser, CDN, and GitHub Actions cache entries at the old URL.
48
100
 
101
+ SVG files are managed by the same Markdown image blocks, validation and sync
102
+ model, but they are not rasterized and do not get WebP variants. Norna copies
103
+ the SVG source to:
104
+
105
+ ```text
106
+ site/.norna/public/images/original/
107
+ ```
108
+
109
+ The copied SVG filename includes the source hash. If the SVG has a `viewBox` or
110
+ numeric `width` and `height`, Norna stores that ratio in the image manifest so
111
+ the existing image layout can size it like other images. If no intrinsic aspect
112
+ ratio can be read, the SVG is still rendered directly.
113
+
49
114
  ## Manifest
50
115
 
51
116
  The generated image manifest is:
@@ -54,13 +119,15 @@ The generated image manifest is:
54
119
  site/.norna/generated-images.json
55
120
  ```
56
121
 
57
- It is versioned site state. It stores source hashes, original dimensions,
58
- output version, and generated variant paths. The image pipeline reuses generated
59
- files only when the manifest entry matches the current source hash and output
60
- version, and all expected variant files exist.
122
+ It is versioned site state. For raster images, it stores source hashes,
123
+ original dimensions, output version, and generated variant paths. For static
124
+ SVG images, it stores the source hash, copied public path, output version, and
125
+ intrinsic dimensions when they can be read. The image pipeline reuses generated
126
+ or copied files only when the manifest entry matches the current source hash
127
+ and output version, and all expected output files exist.
61
128
 
62
- Generated files under `site/.norna/public/` are build-preparation output
63
- and should not be versioned.
129
+ Generated files under `site/.norna/public/` are build-preparation output and
130
+ should not be versioned.
64
131
 
65
132
  ## Metadata Behavior
66
133
 
@@ -70,10 +137,35 @@ copyright metadata.
70
137
  Generated WebP files are created with ImageMagick using `-strip`, so embedded
71
138
  metadata is not a publication mechanism for generated variants. Keep licensing,
72
139
  credits, copyright notices, alt text, and captions in site-owned files such as
73
- `site/content.md`, `COPYRIGHT.md`, or other site documentation.
140
+ `site/content.md`, route content files, `COPYRIGHT.md`, or other site
141
+ documentation.
74
142
 
75
143
  If a site wants embedded metadata in original source files, that process is
76
- outside the current `norna` command surface.
144
+ outside the current Norna command surface.
145
+
146
+ ## AI-Generated Images
147
+
148
+ For AI-generated images, keep provenance and the generation prompt in a
149
+ Markdown comment near the image block. This makes the image maintainable as
150
+ content: future editors can understand where it came from and regenerate or
151
+ revise it without reverse-engineering the asset.
152
+
153
+ ````md
154
+ <!--
155
+ AI image prompt for workflow.png:
156
+ A clean editorial illustration of ordinary project files becoming a small
157
+ static website, restrained colors, readable composition, no text in the image.
158
+ -->
159
+
160
+ ```norna-image-stack
161
+ - image: workflow.png
162
+ alt: Abstract illustration of project files becoming a website.
163
+ caption: Generated illustration.
164
+ ```
165
+ ````
166
+
167
+ This is a maintenance rule for real editorial images. It is not necessary for
168
+ throwaway test fixtures or examples where the prompt has no practical value.
77
169
 
78
170
  ## GitHub Actions Cache
79
171
 
@@ -83,6 +175,6 @@ The starter workflow caches:
83
175
  site/.norna/public/images/generated
84
176
  ```
85
177
 
86
- The cache key should include `site/.norna/generated-images.json` so
87
- unchanged generated variants can be restored during deploy. With a cache miss or
88
- a changed source hash, variants are rebuilt from source images.
178
+ The cache key should include `site/.norna/generated-images.json` so unchanged
179
+ generated variants can be restored during deploy. With a cache miss or a
180
+ changed source hash, variants are rebuilt from source images.
package/docs/routes.md CHANGED
@@ -4,9 +4,9 @@
4
4
  under `site/routes/<NNN-route-id>/route-content.md` and build to first-level
5
5
  URLs.
6
6
 
7
- Routes use the same page frontmatter, section frontmatter, image rows, and
8
- Markdown section model as the homepage. See [Content](content.md) for the page
9
- and section model.
7
+ Routes use the same page frontmatter, optional section metadata, Norna image
8
+ blocks, and Markdown section model as the homepage. See [Content](content.md)
9
+ for the page and section model.
10
10
 
11
11
  ## Route Directory Format
12
12
 
@@ -61,8 +61,6 @@ title: About
61
61
  description: About this site.
62
62
  navigation:
63
63
  label: About
64
- sections:
65
- - id: intro
66
64
  ---
67
65
 
68
66
  ## Intro {#intro}
@@ -99,14 +97,13 @@ Route images live under the physical route directory:
99
97
  site/routes/010-about/images/intro/image.jpg
100
98
  ```
101
99
 
102
- Image references in route frontmatter still use only the filename:
100
+ Image references in route content still use only the filename:
103
101
 
104
- ```yaml
105
- sections:
106
- - id: intro
107
- gallery:
108
- - image: image.jpg
109
- alt: "Intro image."
102
+ ````md
103
+ ```norna-image-stack
104
+ - image: image.jpg
105
+ alt: Intro image.
110
106
  ```
107
+ ````
111
108
 
112
109
  The image directory segment after `images/` should match the section id.
@@ -41,8 +41,9 @@ Version these files in a site repository:
41
41
  image sizing, font, typography defaults, colors, inline styles, and frame
42
42
  colors.
43
43
  See [Theme](theme.md).
44
- - `site/content.md`: homepage page file with editable content, section
45
- definitions, image rows, alt text, and captions. See [Content](content.md).
44
+ - `site/content.md`: homepage page file with editable content, Markdown
45
+ sections, optional section metadata, image blocks, alt text, and captions.
46
+ See [Content](content.md).
46
47
  - `site/images/<section-id>/`: original source images.
47
48
  - `site/routes/<NNN-route-id>/route-content.md`: optional route page files.
48
49
  See [Routes](routes.md).
@@ -50,7 +51,7 @@ Version these files in a site repository:
50
51
  for that route page.
51
52
  - `site/public/`: site-specific static public files.
52
53
  - `site/.norna/generated-images.json`: generated image manifest used to
53
- decide whether WebP variants can be reused.
54
+ decide whether WebP variants and copied managed SVG files can be reused.
54
55
  - `.github/workflows/deploy.yml`: site-owned GitHub Pages workflow.
55
56
  - `package.json` and `package-lock.json`: scripts and pinned engine dependency.
56
57
 
package/docs/theme.md CHANGED
@@ -73,9 +73,9 @@ navigation:
73
73
  `desktop` / `mobile` values.
74
74
  - `density`: default structural spacing profile. Allowed values are `compact`,
75
75
  `normal`, and `airy`.
76
- - `spacing`: optional structural spacing overrides for sections and image
77
- rows. Every spacing value accepts either one CSS length for all viewports or
78
- `desktop` / `mobile` values.
76
+ - `spacing`: optional structural spacing overrides for sections and Norna
77
+ image blocks. Every spacing value accepts either one CSS length for all
78
+ viewports or `desktop` / `mobile` values.
79
79
 
80
80
  Example:
81
81
 
@@ -104,8 +104,8 @@ Spacing keys:
104
104
  - `firstSectionTop`: space above the first section heading.
105
105
  - `sectionGap`: space above each following section.
106
106
  - `finalSectionBottom`: space below the final section.
107
- - `bodyToImages`: space from section body text to image rows.
108
- - `imageGap`: space between image rows.
107
+ - `bodyToImages`: space from section body text to Norna image blocks.
108
+ - `imageGap`: space between stacked images or carousel blocks.
109
109
 
110
110
  Text-near spacing, such as spacing after headings, spacing before Markdown
111
111
  subheadings, paragraph spacing, and caption spacing, belongs to
@@ -206,11 +206,12 @@ presentation:
206
206
  paragraphSpacing: 1em
207
207
  ```
208
208
 
209
- Section-specific presentation belongs under `sections[].presentation`:
209
+ Section-specific presentation belongs under
210
+ `sections.<section-id>.presentation`:
210
211
 
211
212
  ```yaml
212
213
  sections:
213
- - id: intro
214
+ intro:
214
215
  presentation:
215
216
  backgroundColor: "#161616"
216
217
  textColor: "#ffffff"
@@ -132,10 +132,10 @@ typography:
132
132
  ```
133
133
 
134
134
  A page-level `presentation.typography.preset` changes the typographic character
135
- for that page. A section-level `sections[].presentation.typography.preset`
136
- changes the typographic character for that section. `rhythm` is inherited
137
- separately, so changing preset does not change spacing unless `rhythm` is also
138
- set.
135
+ for that page. A section-level
136
+ `sections.<section-id>.presentation.typography.preset` changes the typographic
137
+ character for that section. `rhythm` is inherited separately, so changing
138
+ preset does not change spacing unless `rhythm` is also set.
139
139
 
140
140
  If a section sets `typography.rhythm`, that section changes text-near spacing.
141
141
  If a section only sets `typography.overrides`, it keeps the resolved page
@@ -145,7 +145,7 @@ Example section override:
145
145
 
146
146
  ```yaml
147
147
  sections:
148
- - id: intro
148
+ intro:
149
149
  presentation:
150
150
  typography:
151
151
  preset: statement
@@ -4,7 +4,7 @@ description: A small norna example with free dog images.
4
4
  navigation:
5
5
  label: Dog site!
6
6
  sections:
7
- - id: black-dogs
7
+ black-dogs:
8
8
  presentation:
9
9
  typography:
10
10
  preset: statement
@@ -12,16 +12,7 @@ sections:
12
12
  headings:
13
13
  h2:
14
14
  size: xlarge
15
- gallery:
16
- - image: photo-of-a-black-dog.jpg
17
- alt: "A black dog sitting outdoors and looking at the camera."
18
- caption:
19
- "Photo of a dog. ContaDeletada2906, CC0 1.0, via Wikimedia Commons."
20
- - image: black-puppy-meadow.png
21
- alt:
22
- "A black puppy standing in a grassy meadow and looking at the camera."
23
- caption: "AI-generated image."
24
- - id: summer-dogs
15
+ summer-dogs:
25
16
  visible:
26
17
  from: "2026-08-01"
27
18
  until: "2026-09-01"
@@ -29,21 +20,11 @@ sections:
29
20
  backgroundColor: "#181818"
30
21
  typography:
31
22
  preset: compact-gallery
32
- gallery: []
33
- - id: brown-dogs
23
+ brown-dogs:
34
24
  presentation:
35
25
  typography:
36
26
  preset: text-forward
37
- gallery:
38
- - image: brown-dog.jpg
39
- alt: "A brown dog looking directly at the camera."
40
- caption: "DogBrown. Frank-3, CC0 1.0, via Wikimedia Commons."
41
- - image: dog-accompanies-master.jpg
42
- alt: "A man walks outdoors with a dog at his side."
43
- caption:
44
- "Dog accompanies his master. Steve Hillebrand, U.S. Fish and Wildlife
45
- Service, public domain."
46
- - id: golden-dogs
27
+ golden-dogs:
47
28
  presentation:
48
29
  typography:
49
30
  overrides:
@@ -55,33 +36,12 @@ sections:
55
36
  body:
56
37
  lineHeight: 1.58
57
38
  paragraphSpacing: 1em
58
- gallery:
59
- - image: golden-retriever.jpg
60
- alt: "A golden retriever standing outdoors."
61
- caption: "Golden Retriever. Ribo, public domain, via Wikimedia Commons."
62
- - image: toller-puppy.jpg
63
- alt:
64
- "A young Nova Scotia Duck Tolling Retriever puppy sitting on grass."
65
- caption:
66
- "Nova Scotia Duck Tolling Retriever puppy. RM, public domain, via
67
- Wikimedia Commons."
68
- - id: white-dogs
39
+ white-dogs:
69
40
  presentation:
70
41
  typography:
71
42
  overrides:
72
43
  caption:
73
44
  spacingBefore: 0.35em
74
- gallery:
75
- - carousel:
76
- - image: white-cute-dog.jpg
77
- alt: "A white dog looking toward the camera."
78
- caption:
79
- "White cute dog. Neal Ziring, public domain, via Wikimedia
80
- Commons."
81
- - image: white-puppy-garden.png
82
- alt: "A fluffy white puppy standing on grass in a garden."
83
- caption: "AI-generated image."
84
- - id: sources
85
45
  ---
86
46
 
87
47
  ## Black Dogs {#black-dogs}
@@ -104,6 +64,15 @@ This `####` heading should follow `typography.headings.h4`.
104
64
  Curabitur blandit tempus porttitor. Donec id elit non mi porta gravida at eget
105
65
  metus. Praesent commodo cursus magna, vel scelerisque nisl consectetur et.
106
66
 
67
+ ```norna-image-stack
68
+ - image: photo-of-a-black-dog.jpg
69
+ alt: A black dog sitting outdoors and looking at the camera.
70
+ caption: Photo of a dog. ContaDeletada2906, CC0 1.0, via Wikimedia Commons.
71
+ - image: black-puppy-meadow.png
72
+ alt: A black puppy standing in a grassy meadow and looking at the camera.
73
+ caption: AI-generated image.
74
+ ```
75
+
107
76
  ## Summer Dogs {#summer-dogs}
108
77
 
109
78
  This temporary section is visible during August 2026. It is here to demonstrate
@@ -126,6 +95,15 @@ Maecenas sed diam eget risus varius blandit sit amet non magna. Morbi leo risus,
126
95
  porta ac consectetur ac, vestibulum at eros. Sed posuere consectetur est at
127
96
  lobortis.
128
97
 
98
+ ```norna-image-stack
99
+ - image: brown-dog.jpg
100
+ alt: A brown dog looking directly at the camera.
101
+ caption: DogBrown. Frank-3, CC0 1.0, via Wikimedia Commons.
102
+ - image: dog-accompanies-master.jpg
103
+ alt: A man walks outdoors with a dog at his side.
104
+ caption: Dog accompanies his master. Steve Hillebrand, U.S. Fish and Wildlife Service, public domain.
105
+ ```
106
+
129
107
  ## Golden Dogs {#golden-dogs}
130
108
 
131
109
  Golden-coated retrievers add a third image section and more scrolling depth.
@@ -141,6 +119,15 @@ Nulla vitae elit libero, a pharetra augue. Integer posuere erat a ante venenatis
141
119
  dapibus posuere velit aliquet. Aenean eu leo quam. Pellentesque ornare sem
142
120
  lacinia quam venenatis vestibulum.
143
121
 
122
+ ```norna-image-stack
123
+ - image: golden-retriever.jpg
124
+ alt: A golden retriever standing outdoors.
125
+ caption: Golden Retriever. Ribo, public domain, via Wikimedia Commons.
126
+ - image: toller-puppy.jpg
127
+ alt: A young Nova Scotia Duck Tolling Retriever puppy sitting on grass.
128
+ caption: Nova Scotia Duck Tolling Retriever puppy. RM, public domain, via Wikimedia Commons.
129
+ ```
130
+
144
131
  ## White Dogs {#white-dogs}
145
132
 
146
133
  A white dog completes the colour-based image sequence.
@@ -152,6 +139,15 @@ Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Lorem ipsum
152
139
  dolor sit amet, consectetur adipiscing elit. Donec sed odio dui. Vestibulum id
153
140
  ligula porta felis euismod semper.
154
141
 
142
+ ```norna-image-stack
143
+ - image: white-cute-dog.jpg
144
+ alt: A white dog looking toward the camera.
145
+ caption: White cute dog. Neal Ziring, public domain, via Wikimedia Commons.
146
+ - image: white-puppy-garden.png
147
+ alt: A fluffy white puppy standing on grass in a garden.
148
+ caption: AI-generated image.
149
+ ```
150
+
155
151
  ## Sources {#sources}
156
152
 
157
153
  Most images are sourced from Wikimedia Commons pages that list CC0 or
@@ -4,15 +4,13 @@ description: A small route for testing multi-page norna navigation.
4
4
  navigation:
5
5
  label: Dog care
6
6
  sections:
7
- - id: daily-rhythm
7
+ daily-rhythm:
8
8
  presentation:
9
9
  typography:
10
10
  overrides:
11
11
  headings:
12
12
  h2:
13
13
  size: large
14
- - id: calm-training
15
- - id: media-notes
16
14
  ---
17
15
 
18
16
  ## Daily rhythm {#daily-rhythm}
@@ -2,10 +2,8 @@
2
2
  title: Fixture Site
3
3
  description: Minimal cli-gallery fixture site.
4
4
  sections:
5
- - id: intro
6
- gallery: []
7
- - id: work
8
- gallery: []
5
+ intro: {}
6
+ work: {}
9
7
 
10
8
  ---
11
9
  ## Intro {#intro}