@janga/norna 0.7.11 → 0.7.12

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 (240) hide show
  1. package/README.md +120 -108
  2. package/bin/norna-cli.mjs +1 -1
  3. package/docs/README.md +107 -75
  4. package/docs/assets/norna-dog-shelter.jpg +0 -0
  5. package/docs/commands.md +7 -4
  6. package/docs/configuration.md +86 -185
  7. package/docs/design/command-organization.md +1 -1
  8. package/docs/design/norna-diagram-design.md +2 -1
  9. package/docs/design/site-examples-structure.md +4 -1
  10. package/docs/engine-development.md +15 -8
  11. package/docs/getting-started.md +126 -144
  12. package/docs/how-to/embedded-site.md +35 -0
  13. package/docs/publishing.md +47 -46
  14. package/docs/requirements.md +54 -0
  15. package/docs/routes.md +1 -1
  16. package/docs/site-structure.md +4 -4
  17. package/docs/sitewide-content.md +2 -1
  18. package/docs/theme.md +18 -0
  19. package/examples/README.md +26 -24
  20. package/examples/complete-sites/dog-shelter-multi-page/.astro/content-assets.mjs +4 -1
  21. package/examples/complete-sites/dog-shelter-multi-page/.astro/content.d.ts +1 -1
  22. package/examples/complete-sites/dog-shelter-multi-page/.astro/data-store.json +1 -1
  23. package/examples/complete-sites/dog-shelter-multi-page/.astro/dev.json +6 -8
  24. package/examples/complete-sites/dog-shelter-multi-page/.astro/dev.log +26 -53
  25. package/examples/complete-sites/dog-shelter-multi-page/README.md +7 -0
  26. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/_metadata.json +29 -35
  27. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/{astro-D_GIpJIE.js → astro-B3JHJyiv.js} +4 -4
  28. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/astro-B3JHJyiv.js.map +1 -0
  29. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/astro_n_aria-query.js +151 -151
  30. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/astro_n_aria-query.js.map +1 -1
  31. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/astro_n_axobject-query.js +131 -131
  32. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/astro_n_axobject-query.js.map +1 -1
  33. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/astro_n_html-escaper.js +1 -1
  34. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/astro_n_html-escaper.js.map +1 -1
  35. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +10 -10
  36. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +1 -1
  37. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/{audit-DM9Hkl5B.js → audit-C3SVr7dT.js} +10 -10
  38. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/audit-C3SVr7dT.js.map +1 -0
  39. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/{highlight-DnZ1ULOL.js → highlight-BLtvqtzO.js} +1 -1
  40. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/highlight-BLtvqtzO.js.map +1 -0
  41. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/{icons-RyebKi45.js → icons-C92i61-2.js} +1 -1
  42. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/icons-C92i61-2.js.map +1 -0
  43. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/{settings-DaU-gNnK.js → settings-v5GgKks_.js} +2 -2
  44. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/settings-v5GgKks_.js.map +1 -0
  45. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/{toolbar-63PA619H.js → toolbar-BFvJVp26.js} +2 -2
  46. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/toolbar-BFvJVp26.js.map +1 -0
  47. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/{ui-library--bkcNSYz.js → ui-library-Cd35oAvo.js} +10 -10
  48. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/ui-library-Cd35oAvo.js.map +1 -0
  49. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/{window-jPkpKVcy.js → window-vo3A6TMn.js} +1 -1
  50. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/window-vo3A6TMn.js.map +1 -0
  51. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/{xray-BXCHpEJP.js → xray-DwhzGckb.js} +3 -3
  52. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/xray-DwhzGckb.js.map +1 -0
  53. package/examples/complete-sites/dog-shelter-multi-page/site/.norna/generated-images.json +92 -12
  54. package/examples/complete-sites/dog-shelter-multi-page/site/config.md +3 -0
  55. package/examples/complete-sites/dog-shelter-multi-page/site/content.md +12 -0
  56. package/examples/complete-sites/dog-shelter-multi-page/site/images/intro/shelter-dog.jpg +0 -0
  57. package/examples/complete-sites/dog-shelter-multi-page/site/public/logo.svg +9 -0
  58. package/examples/complete-sites/dog-shelter-multi-page/site/routes/010-dogs/images/dogs/rex.jpg +0 -0
  59. package/examples/complete-sites/dog-shelter-multi-page/site/routes/010-dogs/images/dogs/rover.jpg +0 -0
  60. package/examples/complete-sites/dog-shelter-multi-page/site/routes/010-dogs/route-content.md +11 -19
  61. package/examples/complete-sites/dog-shelter-multi-page/site/routes/020-adopt/images/adopt/adoption-dog.jpg +0 -0
  62. package/examples/complete-sites/dog-shelter-multi-page/site/routes/020-adopt/route-content.md +11 -0
  63. package/examples/complete-sites/dog-shelter-multi-page/site/sitewide-content.md +12 -2
  64. package/examples/complete-sites/dog-shelter-multi-page/site/theme.md +1 -23
  65. package/examples/complete-sites/dog-shelter-single-page/.astro/collections/site.schema.json +59 -0
  66. package/examples/complete-sites/dog-shelter-single-page/.astro/collections/sitewide.schema.json +138 -0
  67. package/examples/complete-sites/dog-shelter-single-page/.astro/collections/theme.schema.json +755 -0
  68. package/examples/complete-sites/dog-shelter-single-page/.astro/content-assets.mjs +4 -0
  69. package/examples/complete-sites/dog-shelter-single-page/.astro/content-modules.mjs +1 -0
  70. package/examples/complete-sites/dog-shelter-single-page/.astro/content.d.ts +183 -0
  71. package/examples/complete-sites/dog-shelter-single-page/.astro/data-store.json +1 -0
  72. package/examples/complete-sites/dog-shelter-single-page/.astro/dev.json +13 -0
  73. package/examples/complete-sites/dog-shelter-single-page/.astro/dev.log +14 -0
  74. package/examples/complete-sites/dog-shelter-single-page/.astro/settings.json +5 -0
  75. package/examples/complete-sites/dog-shelter-single-page/.astro/types.d.ts +2 -0
  76. package/examples/complete-sites/dog-shelter-single-page/README.md +7 -0
  77. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/_metadata.json +74 -0
  78. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/astro-B3JHJyiv.js +434 -0
  79. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/astro-B3JHJyiv.js.map +1 -0
  80. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/astro_n_aria-query.js +6390 -0
  81. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/astro_n_aria-query.js.map +1 -0
  82. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/astro_n_axobject-query.js +2702 -0
  83. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/astro_n_axobject-query.js.map +1 -0
  84. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/astro_n_html-escaper.js +65 -0
  85. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/astro_n_html-escaper.js.map +1 -0
  86. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +455 -0
  87. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +1 -0
  88. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/audit-C3SVr7dT.js +1534 -0
  89. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/audit-C3SVr7dT.js.map +1 -0
  90. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/highlight-BLtvqtzO.js +48 -0
  91. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/highlight-BLtvqtzO.js.map +1 -0
  92. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/icons-C92i61-2.js +42 -0
  93. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/icons-C92i61-2.js.map +1 -0
  94. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/package.json +3 -0
  95. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/rolldown-runtime-BvCyGRYZ.js +4 -0
  96. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/settings-v5GgKks_.js +190 -0
  97. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/settings-v5GgKks_.js.map +1 -0
  98. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/toolbar-BFvJVp26.js +458 -0
  99. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/toolbar-BFvJVp26.js.map +1 -0
  100. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/ui-library-Cd35oAvo.js +1016 -0
  101. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/ui-library-Cd35oAvo.js.map +1 -0
  102. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/window-vo3A6TMn.js +32 -0
  103. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/window-vo3A6TMn.js.map +1 -0
  104. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/xray-DwhzGckb.js +111 -0
  105. package/examples/complete-sites/dog-shelter-single-page/node_modules/.vite/deps/xray-DwhzGckb.js.map +1 -0
  106. package/examples/complete-sites/dog-shelter-single-page/site/.norna/generated-images.json +44 -12
  107. package/examples/complete-sites/dog-shelter-single-page/site/config.md +3 -0
  108. package/examples/complete-sites/dog-shelter-single-page/site/content.md +20 -24
  109. package/examples/complete-sites/dog-shelter-single-page/site/images/what-we-do/rescue-dog.jpg +0 -0
  110. package/examples/complete-sites/dog-shelter-single-page/site/images/you-can-help/foster-dog.jpg +0 -0
  111. package/examples/complete-sites/dog-shelter-single-page/site/public/logo.svg +9 -0
  112. package/examples/complete-sites/dog-shelter-single-page/site/sitewide-content.md +12 -2
  113. package/examples/complete-sites/dog-shelter-single-page/site/theme.md +1 -23
  114. package/examples/feature-demos/media-and-surfaces/.astro/data-store.json +1 -1
  115. package/examples/feature-demos/media-and-surfaces/.astro/dev.log +15 -2
  116. package/examples/feature-demos/media-and-surfaces/README.md +7 -1
  117. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/_metadata.json +7 -1
  118. package/examples/{complete-sites/dog-shelter-multi-page → feature-demos/media-and-surfaces}/node_modules/.vite/deps/embla-carousel.js +1 -1
  119. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/embla-carousel.js.map +1 -0
  120. package/examples/feature-demos/media-and-surfaces/site/config.md +4 -0
  121. package/examples/feature-demos/media-and-surfaces/site/content.md +5 -51
  122. package/examples/feature-demos/media-and-surfaces/site/routes/010-media/route-content.md +10 -25
  123. package/examples/feature-demos/media-and-surfaces/site/routes/020-surfaces/route-content.md +1 -1
  124. package/examples/feature-demos/sitewide-content/.astro/collections/site.schema.json +59 -0
  125. package/examples/feature-demos/sitewide-content/.astro/collections/sitewide.schema.json +138 -0
  126. package/examples/feature-demos/sitewide-content/.astro/collections/theme.schema.json +755 -0
  127. package/examples/feature-demos/sitewide-content/.astro/content-assets.mjs +1 -0
  128. package/examples/feature-demos/sitewide-content/.astro/content-modules.mjs +1 -0
  129. package/examples/feature-demos/sitewide-content/.astro/content.d.ts +183 -0
  130. package/examples/feature-demos/sitewide-content/.astro/data-store.json +1 -0
  131. package/examples/feature-demos/sitewide-content/.astro/dev.json +13 -0
  132. package/examples/feature-demos/sitewide-content/.astro/dev.log +18 -0
  133. package/examples/feature-demos/sitewide-content/.astro/settings.json +5 -0
  134. package/examples/feature-demos/sitewide-content/.astro/types.d.ts +2 -0
  135. package/examples/feature-demos/sitewide-content/README.md +18 -0
  136. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/_metadata.json +74 -0
  137. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/astro-B3JHJyiv.js +434 -0
  138. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/astro-B3JHJyiv.js.map +1 -0
  139. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/astro_n_aria-query.js +6390 -0
  140. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/astro_n_aria-query.js.map +1 -0
  141. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/astro_n_axobject-query.js +2702 -0
  142. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/astro_n_axobject-query.js.map +1 -0
  143. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/astro_n_html-escaper.js +65 -0
  144. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/astro_n_html-escaper.js.map +1 -0
  145. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +455 -0
  146. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +1 -0
  147. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/audit-C3SVr7dT.js +1534 -0
  148. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/audit-C3SVr7dT.js.map +1 -0
  149. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/highlight-BLtvqtzO.js +48 -0
  150. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/highlight-BLtvqtzO.js.map +1 -0
  151. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/icons-C92i61-2.js +42 -0
  152. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/icons-C92i61-2.js.map +1 -0
  153. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/package.json +3 -0
  154. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/rolldown-runtime-BvCyGRYZ.js +4 -0
  155. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/settings-v5GgKks_.js +190 -0
  156. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/settings-v5GgKks_.js.map +1 -0
  157. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/toolbar-BFvJVp26.js +458 -0
  158. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/toolbar-BFvJVp26.js.map +1 -0
  159. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/ui-library-Cd35oAvo.js +1016 -0
  160. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/ui-library-Cd35oAvo.js.map +1 -0
  161. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/window-vo3A6TMn.js +32 -0
  162. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/window-vo3A6TMn.js.map +1 -0
  163. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/xray-DwhzGckb.js +111 -0
  164. package/examples/feature-demos/sitewide-content/node_modules/.vite/deps/xray-DwhzGckb.js.map +1 -0
  165. package/examples/feature-demos/sitewide-content/site/.norna/generated-images.json +1 -0
  166. package/examples/feature-demos/sitewide-content/site/config.md +3 -0
  167. package/examples/feature-demos/sitewide-content/site/content.md +16 -0
  168. package/examples/feature-demos/sitewide-content/site/public/logo.svg +6 -0
  169. package/examples/feature-demos/sitewide-content/site/public/robots.txt +2 -0
  170. package/examples/feature-demos/sitewide-content/site/routes/010-identity/route-content.md +20 -0
  171. package/examples/feature-demos/sitewide-content/site/routes/020-notices/route-content.md +22 -0
  172. package/examples/feature-demos/sitewide-content/site/routes/030-footer/route-content.md +17 -0
  173. package/examples/feature-demos/sitewide-content/site/sitewide-content.md +33 -0
  174. package/examples/feature-demos/sitewide-content/site/theme.md +3 -0
  175. package/examples/feature-demos/theme-presets/.astro/dev.log +37 -0
  176. package/examples/feature-demos/theme-presets/README.md +5 -1
  177. package/examples/feature-demos/theme-presets/site/config.md +3 -0
  178. package/fixtures/basic/site/config.md +3 -0
  179. package/fixtures/content-model-v2/site/config.md +3 -0
  180. package/package.json +12 -7
  181. package/scripts/build-pages.mjs +4 -6
  182. package/scripts/check-config.mjs +1 -10
  183. package/scripts/deploy-site.mjs +12 -8
  184. package/scripts/lib/example-sites.mjs +3 -0
  185. package/scripts/lib/github-repository.mjs +44 -0
  186. package/scripts/lib/norna-markdown-blocks.mjs +97 -10
  187. package/scripts/lib/project-config.mjs +112 -123
  188. package/scripts/lib/site-content.mjs +7 -0
  189. package/scripts/lib/site-paths.mjs +6 -2
  190. package/scripts/test-client-javascript.mjs +236 -0
  191. package/scripts/test-content-check.mjs +103 -0
  192. package/scripts/test-content-model-v2.mjs +9 -13
  193. package/scripts/test-documentation.mjs +116 -0
  194. package/scripts/test-engine-commands.mjs +12 -3
  195. package/scripts/test-examples.mjs +9 -0
  196. package/scripts/test-package-check.mjs +17 -9
  197. package/scripts/test-project-config.mjs +130 -0
  198. package/scripts/test-temporary-visibility.mjs +3 -10
  199. package/scripts/test-theme-presets.mjs +3 -8
  200. package/scripts/watch-pages-deploy.mjs +15 -11
  201. package/src/components/GalleryGrid.astro +30 -230
  202. package/src/components/ImageCarousel.astro +184 -0
  203. package/src/components/MobileSectionNavigationScript.astro +14 -0
  204. package/src/components/NornaSmoothScrollScript.astro +250 -0
  205. package/src/components/SiteNavigation.astro +58 -341
  206. package/src/components/SiteNotesScript.astro +43 -0
  207. package/src/components/SitePage.astro +4 -1
  208. package/src/components/SiteSection.astro +4 -47
  209. package/src/layouts/BaseLayout.astro +1 -1
  210. package/src/lib/galleryImages.ts +53 -0
  211. package/src/styles/global.css +81 -1
  212. package/starters/basic/.github/workflows/deploy.yml +1 -2
  213. package/starters/basic/README.md +10 -8
  214. package/starters/basic/site/config.md +3 -0
  215. package/starters/project/.github/workflows/deploy.yml +1 -2
  216. package/starters/project/README.md +3 -4
  217. package/starters/project/site/config.md +3 -0
  218. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/astro-D_GIpJIE.js.map +0 -1
  219. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/audit-DM9Hkl5B.js.map +0 -1
  220. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/embla-carousel.js.map +0 -1
  221. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/highlight-DnZ1ULOL.js.map +0 -1
  222. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/icons-RyebKi45.js.map +0 -1
  223. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/settings-DaU-gNnK.js.map +0 -1
  224. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/toolbar-63PA619H.js.map +0 -1
  225. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/ui-library--bkcNSYz.js.map +0 -1
  226. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/window-jPkpKVcy.js.map +0 -1
  227. package/examples/complete-sites/dog-shelter-multi-page/node_modules/.vite/deps/xray-BXCHpEJP.js.map +0 -1
  228. package/examples/complete-sites/dog-shelter-multi-page/site/config.mjs +0 -30
  229. package/examples/complete-sites/dog-shelter-multi-page/site/routes/010-dogs/images/dogs/rex.svg +0 -19
  230. package/examples/complete-sites/dog-shelter-multi-page/site/routes/010-dogs/images/dogs/rover.svg +0 -19
  231. package/examples/complete-sites/dog-shelter-single-page/site/config.mjs +0 -30
  232. package/examples/complete-sites/dog-shelter-single-page/site/images/what-we-do/dog-house.svg +0 -25
  233. package/examples/complete-sites/dog-shelter-single-page/site/images/you-can-help/heart.svg +0 -13
  234. package/examples/feature-demos/media-and-surfaces/site/config.mjs +0 -30
  235. package/examples/feature-demos/media-and-surfaces/site/routes/010-media/theme.md +0 -3
  236. package/examples/feature-demos/theme-presets/site/config.mjs +0 -30
  237. package/fixtures/basic/site/config.mjs +0 -27
  238. package/fixtures/content-model-v2/site/config.mjs +0 -11
  239. package/starters/basic/site/config.mjs +0 -27
  240. package/starters/project/site/config.mjs +0 -30
@@ -0,0 +1,184 @@
1
+ ---
2
+ import projectConfig from '../../scripts/lib/project-config.mjs';
3
+ import { getImageAttributes } from '../lib/generatedImages';
4
+ import {
5
+ getCaptionId,
6
+ getCarouselAspectRatio,
7
+ getGalleryImageSizes,
8
+ getImageClass,
9
+ getImageLoadingProps,
10
+ type GalleryImage,
11
+ } from '../lib/galleryImages';
12
+
13
+ interface Props {
14
+ title: string;
15
+ images: GalleryImage[];
16
+ maxAvailableWidthPercent: {
17
+ desktop: number;
18
+ mobile: number;
19
+ };
20
+ priorityFirstImage?: boolean;
21
+ }
22
+
23
+ const { title, images, maxAvailableWidthPercent, priorityFirstImage = false } = Astro.props;
24
+ const galleryImageSizes = getGalleryImageSizes(maxAvailableWidthPercent);
25
+ const carouselAspectRatio = getCarouselAspectRatio(images);
26
+ const carouselStyle = carouselAspectRatio
27
+ ? `--gallery-carousel-aspect-ratio: ${carouselAspectRatio}`
28
+ : undefined;
29
+ ---
30
+
31
+ <div class="gallery" aria-label={`${projectConfig.locale.labels.gallery}: ${title}`}>
32
+ <section
33
+ class="gallery-item gallery-carousel"
34
+ data-carousel
35
+ tabindex="0"
36
+ role="group"
37
+ aria-roledescription="carousel"
38
+ aria-label={`${title}, bildkarusell`}
39
+ style={carouselStyle}
40
+ >
41
+ <div class="gallery-carousel-stage">
42
+ <div class="gallery-carousel-viewport" data-carousel-viewport>
43
+ <div class="gallery-carousel-slides">
44
+ {images.map((image, imageIndex) => {
45
+ const captionId = image.caption ? getCaptionId(image, 0, imageIndex) : undefined;
46
+
47
+ return (
48
+ <figure class="gallery-carousel-slide" data-carousel-slide>
49
+ <div class="gallery-carousel-image-frame">
50
+ <img
51
+ {...getImageAttributes(image.src ?? image.image, galleryImageSizes)}
52
+ class:list={['gallery-image', getImageClass(image)]}
53
+ alt={image.alt ?? ''}
54
+ aria-describedby={captionId}
55
+ {...getImageLoadingProps(priorityFirstImage && imageIndex === 0)}
56
+ />
57
+ </div>
58
+ </figure>
59
+ );
60
+ })}
61
+ </div>
62
+ </div>
63
+ <button
64
+ class="gallery-carousel-button gallery-carousel-button-previous"
65
+ type="button"
66
+ data-carousel-previous
67
+ aria-label="Föregående bild"
68
+ title="Föregående bild"
69
+ >
70
+ <span aria-hidden="true">‹</span>
71
+ </button>
72
+ <button
73
+ class="gallery-carousel-button gallery-carousel-button-next"
74
+ type="button"
75
+ data-carousel-next
76
+ aria-label="Nästa bild"
77
+ title="Nästa bild"
78
+ >
79
+ <span aria-hidden="true">›</span>
80
+ </button>
81
+ <output class="gallery-carousel-position" data-carousel-position aria-live="polite">
82
+ 1 / {images.length}
83
+ </output>
84
+ </div>
85
+ <div class="gallery-carousel-captions" data-carousel-captions>
86
+ {images.map((image, imageIndex) => (
87
+ image.caption && (
88
+ <p
89
+ class="gallery-meta gallery-carousel-caption"
90
+ id={getCaptionId(image, 0, imageIndex)}
91
+ data-carousel-caption
92
+ data-carousel-caption-index={imageIndex}
93
+ >
94
+ <span class="gallery-details">
95
+ <span>{image.caption}</span>
96
+ </span>
97
+ </p>
98
+ )
99
+ ))}
100
+ </div>
101
+ </section>
102
+ </div>
103
+
104
+ <script>
105
+ import EmblaCarousel from 'embla-carousel';
106
+
107
+ const initializeCarousel = (carousel: HTMLElement) => {
108
+ if (carousel.dataset.carouselInitialized === 'true') return;
109
+
110
+ const viewport = carousel.querySelector<HTMLElement>('[data-carousel-viewport]');
111
+ const previous = carousel.querySelector<HTMLButtonElement>('[data-carousel-previous]');
112
+ const next = carousel.querySelector<HTMLButtonElement>('[data-carousel-next]');
113
+ const position = carousel.querySelector<HTMLOutputElement>('[data-carousel-position]');
114
+ const captions = Array.from(carousel.querySelectorAll<HTMLElement>('[data-carousel-caption]'));
115
+ const slides = Array.from(carousel.querySelectorAll<HTMLElement>('[data-carousel-slide]'));
116
+ if (!viewport || !previous || !next || !position || slides.length < 2) return;
117
+
118
+ let controlsTimeout: number | undefined;
119
+ const showControls = () => {
120
+ carousel.classList.add('is-interacting');
121
+ window.clearTimeout(controlsTimeout);
122
+ controlsTimeout = window.setTimeout(() => {
123
+ carousel.classList.remove('is-interacting');
124
+ }, 1600);
125
+ };
126
+
127
+ carousel.dataset.carouselInitialized = 'true';
128
+ carousel.dataset.carouselReady = 'true';
129
+
130
+ const embla = EmblaCarousel(viewport, {
131
+ align: 'start',
132
+ containScroll: false,
133
+ loop: true,
134
+ });
135
+
136
+ const update = () => {
137
+ const activeIndex = embla.selectedScrollSnap();
138
+ slides.forEach((slide, index) => {
139
+ const isSelected = index === activeIndex;
140
+ slide.classList.toggle('is-active', isSelected);
141
+ slide.setAttribute('aria-hidden', String(!isSelected));
142
+ });
143
+ captions.forEach((caption) => {
144
+ const isSelected = Number(caption.dataset.carouselCaptionIndex) === activeIndex;
145
+ caption.classList.toggle('is-active', isSelected);
146
+ caption.setAttribute('aria-hidden', String(!isSelected));
147
+ });
148
+ previous.disabled = false;
149
+ next.disabled = false;
150
+ position.textContent = `${activeIndex + 1} / ${slides.length}`;
151
+ };
152
+
153
+ previous.addEventListener('click', () => {
154
+ showControls();
155
+ embla.scrollPrev();
156
+ });
157
+ next.addEventListener('click', () => {
158
+ showControls();
159
+ embla.scrollNext();
160
+ });
161
+ carousel.addEventListener('focusin', showControls);
162
+ carousel.addEventListener('pointerdown', showControls);
163
+ carousel.addEventListener('keydown', (event) => {
164
+ if (event.key === 'ArrowLeft') {
165
+ event.preventDefault();
166
+ showControls();
167
+ embla.scrollPrev();
168
+ }
169
+ if (event.key === 'ArrowRight') {
170
+ event.preventDefault();
171
+ showControls();
172
+ embla.scrollNext();
173
+ }
174
+ });
175
+ embla.on('select', () => {
176
+ showControls();
177
+ update();
178
+ });
179
+ embla.on('reInit', update);
180
+ update();
181
+ };
182
+
183
+ document.querySelectorAll<HTMLElement>('[data-carousel]').forEach(initializeCarousel);
184
+ </script>
@@ -0,0 +1,14 @@
1
+ <script>
2
+ const mobileMenu = document.querySelector<HTMLDetailsElement>('.mobile-nav-menu');
3
+ const sectionLinks = Array.from(document.querySelectorAll<HTMLAnchorElement>('.mobile-page-nav a'));
4
+
5
+ sectionLinks.forEach((link) => {
6
+ link.addEventListener('click', (event) => {
7
+ const linkUrl = new URL(link.href, window.location.href);
8
+ if (linkUrl.pathname !== window.location.pathname) return;
9
+
10
+ if (mobileMenu) mobileMenu.open = false;
11
+ if (event.detail > 0) link.blur();
12
+ });
13
+ });
14
+ </script>
@@ -0,0 +1,250 @@
1
+ <script>
2
+ const siteTop = document.querySelector<HTMLElement>('.site-top');
3
+ const sections = Array.from(document.querySelectorAll<HTMLElement>('.site-section'));
4
+ const links = Array.from(document.querySelectorAll<HTMLAnchorElement>('.site-nav-submenu a, .mobile-page-nav a, .page-nav a'));
5
+ const mobileMenu = document.querySelector<HTMLDetailsElement>('.mobile-nav-menu');
6
+ const anchorCorrectionDelays = [120, 320, 700, 1_000, 2_000];
7
+ const reduceMotionQuery = window.matchMedia('(prefers-reduced-motion: reduce)');
8
+ const minimumScrollDuration = 700;
9
+ const maximumScrollDuration = 1_800;
10
+ const scrollDurationPerPixel = 0.18;
11
+ const scrollInterruptEvents = ['wheel', 'touchstart'];
12
+ const scrollInterruptKeys = new Set(['ArrowDown', 'ArrowUp', 'End', 'Home', 'PageDown', 'PageUp', ' ']);
13
+ let activeScrollAnimation: { cancel: () => void } | null = null;
14
+ let anchorCorrectionToken = 0;
15
+
16
+ if ('scrollRestoration' in history) {
17
+ history.scrollRestoration = 'manual';
18
+ }
19
+
20
+ const setAnchorOffset = () => {
21
+ if (!siteTop) return;
22
+
23
+ const offset = Math.ceil(siteTop.getBoundingClientRect().height);
24
+ document.documentElement.style.setProperty('--site-top-anchor-offset', `${offset}px`);
25
+ return offset;
26
+ };
27
+
28
+ const getSectionFromHash = (hash: string) => {
29
+ const id = hash.replace(/^#/, '');
30
+ return sections.find((section) => section.id === id);
31
+ };
32
+
33
+ const getSectionHeading = (section?: HTMLElement) => section?.querySelector<HTMLElement>('h1, h2');
34
+
35
+ const cancelActiveScroll = () => {
36
+ activeScrollAnimation?.cancel();
37
+ activeScrollAnimation = null;
38
+ };
39
+
40
+ const getHashSection = () => getSectionFromHash(window.location.hash);
41
+
42
+ const setHash = (targetHash: string) => {
43
+ if (window.location.hash !== targetHash) {
44
+ history.pushState(null, '', targetHash);
45
+ }
46
+ };
47
+
48
+ const closeMobileMenu = () => {
49
+ if (mobileMenu) mobileMenu.open = false;
50
+ };
51
+
52
+ const getSectionScrollTop = (section?: HTMLElement) => {
53
+ const heading = getSectionHeading(section);
54
+ if (!heading) return;
55
+
56
+ const offset = setAnchorOffset();
57
+ if (offset === undefined) return;
58
+
59
+ return Math.max(0, heading.getBoundingClientRect().top + window.scrollY - offset);
60
+ };
61
+
62
+ const jumpTo = (top: number) => {
63
+ cancelActiveScroll();
64
+
65
+ const root = document.documentElement;
66
+ const previousScrollBehavior = root.style.scrollBehavior;
67
+ root.style.scrollBehavior = 'auto';
68
+ window.scrollTo(0, top);
69
+ root.style.scrollBehavior = previousScrollBehavior;
70
+ };
71
+
72
+ const getScrollDuration = (distance: number) => {
73
+ if (reduceMotionQuery.matches) return 0;
74
+
75
+ return Math.min(
76
+ maximumScrollDuration,
77
+ Math.max(minimumScrollDuration, Math.abs(distance) * scrollDurationPerPixel),
78
+ );
79
+ };
80
+
81
+ const easeInOutCubic = (progress: number) => (
82
+ progress < 0.5
83
+ ? 4 * progress * progress * progress
84
+ : 1 - Math.pow(-2 * progress + 2, 3) / 2
85
+ );
86
+
87
+ const animateScrollTo = (
88
+ target: number | (() => number | undefined),
89
+ { duration, onComplete }: { duration?: number; onComplete?: () => void } = {},
90
+ ) => {
91
+ cancelActiveScroll();
92
+
93
+ const getTop = typeof target === 'function' ? target : () => target;
94
+ let latestTop = getTop();
95
+ if (latestTop === undefined) return 0;
96
+
97
+ const startY = window.scrollY;
98
+ const scrollDuration = duration ?? getScrollDuration(latestTop - startY);
99
+
100
+ if (scrollDuration <= 0) {
101
+ const top = getTop();
102
+ if (top === undefined) return 0;
103
+
104
+ jumpTo(top);
105
+ onComplete?.();
106
+ return 0;
107
+ }
108
+
109
+ const root = document.documentElement;
110
+ const previousScrollBehavior = root.style.scrollBehavior;
111
+ const startedAt = performance.now();
112
+ let frameId = 0;
113
+ const state = { cancel: () => {} };
114
+
115
+ const cleanup = () => {
116
+ scrollInterruptEvents.forEach((eventName) => {
117
+ window.removeEventListener(eventName, interruptScroll);
118
+ });
119
+ window.removeEventListener('keydown', interruptScrollKey);
120
+ root.style.scrollBehavior = previousScrollBehavior;
121
+ delete root.dataset.nornaScrollActive;
122
+
123
+ if (activeScrollAnimation === state) {
124
+ activeScrollAnimation = null;
125
+ }
126
+ };
127
+
128
+ const cancel = () => {
129
+ if (frameId) cancelAnimationFrame(frameId);
130
+ cleanup();
131
+ };
132
+
133
+ const interruptScroll = () => cancel();
134
+ const interruptScrollKey = (event: KeyboardEvent) => {
135
+ if (scrollInterruptKeys.has(event.key)) interruptScroll();
136
+ };
137
+
138
+ const step = (now: number) => {
139
+ const progress = Math.min(1, (now - startedAt) / scrollDuration);
140
+ const top = getTop();
141
+ if (top !== undefined) latestTop = top;
142
+
143
+ window.scrollTo(0, startY + (latestTop - startY) * easeInOutCubic(progress));
144
+
145
+ if (progress >= 1) {
146
+ window.scrollTo(0, latestTop);
147
+ cleanup();
148
+ onComplete?.();
149
+ return;
150
+ }
151
+
152
+ frameId = requestAnimationFrame(step);
153
+ };
154
+
155
+ state.cancel = cancel;
156
+ activeScrollAnimation = state;
157
+ root.style.scrollBehavior = 'auto';
158
+ root.dataset.nornaScrollActive = 'true';
159
+ scrollInterruptEvents.forEach((eventName) => {
160
+ window.addEventListener(eventName, interruptScroll, { passive: true });
161
+ });
162
+ window.addEventListener('keydown', interruptScrollKey);
163
+ frameId = requestAnimationFrame(step);
164
+
165
+ return scrollDuration;
166
+ };
167
+
168
+ const scrollToSection = (
169
+ section: HTMLElement,
170
+ { animate = true, duration, onComplete }: { animate?: boolean; duration?: number; onComplete?: () => void } = {},
171
+ ) => {
172
+ const top = getSectionScrollTop(section);
173
+ if (top === undefined) return 0;
174
+
175
+ return animate
176
+ ? animateScrollTo(() => getSectionScrollTop(section), { duration, onComplete })
177
+ : (jumpTo(top), onComplete?.(), 0);
178
+ };
179
+
180
+ const correctCurrentHashAnchor = () => {
181
+ if (activeScrollAnimation) return;
182
+
183
+ const section = getHashSection();
184
+ if (section) scrollToSection(section, { animate: false });
185
+ };
186
+
187
+ const scheduleHashAnchorCorrection = () => {
188
+ if (!window.location.hash) return;
189
+
190
+ anchorCorrectionToken += 1;
191
+ const token = anchorCorrectionToken;
192
+
193
+ anchorCorrectionDelays.forEach((delay) => {
194
+ window.setTimeout(() => {
195
+ if (token === anchorCorrectionToken) requestAnimationFrame(correctCurrentHashAnchor);
196
+ }, delay);
197
+ });
198
+ };
199
+
200
+ const syncToLocation = ({ scrollHashlessTop = false } = {}) => {
201
+ const section = getHashSection();
202
+
203
+ if (section) {
204
+ scrollToSection(section, { animate: false });
205
+ scheduleHashAnchorCorrection();
206
+ return;
207
+ }
208
+
209
+ if (scrollHashlessTop) jumpTo(0);
210
+ };
211
+
212
+ links.forEach((link) => {
213
+ link.addEventListener('click', (event) => {
214
+ const linkUrl = new URL(link.href, window.location.href);
215
+ if (linkUrl.pathname !== window.location.pathname) return;
216
+
217
+ const section = getSectionFromHash(linkUrl.hash);
218
+ if (!section) return;
219
+
220
+ const top = getSectionScrollTop(section);
221
+ if (top === undefined) return;
222
+
223
+ event.preventDefault();
224
+ const duration = getScrollDuration(top - window.scrollY);
225
+
226
+ setHash(linkUrl.hash);
227
+ closeMobileMenu();
228
+ if (event.detail > 0) link.blur();
229
+
230
+ scrollToSection(section, {
231
+ duration,
232
+ onComplete: scheduleHashAnchorCorrection,
233
+ });
234
+ });
235
+ });
236
+
237
+ setAnchorOffset();
238
+ requestAnimationFrame(() => syncToLocation());
239
+
240
+ if (siteTop && 'ResizeObserver' in window) {
241
+ new ResizeObserver(setAnchorOffset).observe(siteTop);
242
+ }
243
+
244
+ window.addEventListener('resize', setAnchorOffset);
245
+ window.addEventListener('load', scheduleHashAnchorCorrection);
246
+ window.addEventListener('hashchange', () => syncToLocation({ scrollHashlessTop: true }));
247
+ window.addEventListener('popstate', () => syncToLocation({ scrollHashlessTop: true }));
248
+
249
+ window.visualViewport?.addEventListener('resize', setAnchorOffset);
250
+ </script>