@janga/norna 0.7.9 → 0.7.11

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 (287) hide show
  1. package/README.md +4 -2
  2. package/bin/norna-cli.mjs +3 -0
  3. package/docs/README.md +16 -12
  4. package/docs/commands.md +6 -0
  5. package/docs/configuration.md +10 -54
  6. package/docs/content.md +20 -21
  7. package/docs/design/norna-diagram-design.md +2 -0
  8. package/docs/design/site-examples-structure.md +35 -24
  9. package/docs/engine-development.md +13 -7
  10. package/docs/getting-started.md +56 -19
  11. package/docs/routes.md +8 -6
  12. package/docs/site-structure.md +11 -6
  13. package/docs/sitewide-content.md +74 -0
  14. package/docs/theme.md +125 -140
  15. package/docs/typography.md +72 -45
  16. package/examples/README.md +41 -0
  17. package/examples/complete-sites/dog-shelter-multi-page/.astro/collections/site.schema.json +59 -0
  18. package/examples/complete-sites/dog-shelter-multi-page/.astro/collections/sitewide.schema.json +138 -0
  19. package/examples/complete-sites/dog-shelter-multi-page/.astro/collections/theme.schema.json +755 -0
  20. package/examples/complete-sites/dog-shelter-multi-page/.astro/content-modules.mjs +1 -0
  21. package/examples/{typography-presets → complete-sites/dog-shelter-multi-page}/.astro/content.d.ts +8 -0
  22. package/examples/complete-sites/dog-shelter-multi-page/.astro/data-store.json +1 -0
  23. package/examples/{typography-presets → complete-sites/dog-shelter-multi-page}/.astro/dev-local.json +2 -2
  24. package/examples/{typography-presets → complete-sites/dog-shelter-multi-page}/.astro/dev.json +2 -2
  25. package/examples/complete-sites/dog-shelter-multi-page/.astro/dev.log +53 -0
  26. package/examples/complete-sites/dog-shelter-multi-page/.astro/settings.json +5 -0
  27. package/examples/complete-sites/dog-shelter-multi-page/README.md +9 -0
  28. package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/_metadata.json +13 -7
  29. package/examples/complete-sites/dog-shelter-multi-page/site/.norna/generated-images.json +18 -0
  30. package/examples/complete-sites/dog-shelter-multi-page/site/config.mjs +30 -0
  31. package/examples/complete-sites/dog-shelter-multi-page/site/content.md +12 -0
  32. package/examples/complete-sites/dog-shelter-multi-page/site/public/favicon.svg +10 -0
  33. package/examples/complete-sites/dog-shelter-multi-page/site/routes/010-dogs/images/dogs/rex.svg +19 -0
  34. package/examples/complete-sites/dog-shelter-multi-page/site/routes/010-dogs/images/dogs/rover.svg +19 -0
  35. package/examples/complete-sites/dog-shelter-multi-page/site/routes/010-dogs/route-content.md +30 -0
  36. package/examples/complete-sites/dog-shelter-multi-page/site/routes/020-adopt/route-content.md +12 -0
  37. package/examples/complete-sites/dog-shelter-multi-page/site/sitewide-content.md +6 -0
  38. package/examples/complete-sites/dog-shelter-multi-page/site/theme.md +25 -0
  39. package/examples/complete-sites/dog-shelter-single-page/README.md +9 -0
  40. package/examples/complete-sites/dog-shelter-single-page/site/.norna/generated-images.json +18 -0
  41. package/examples/complete-sites/dog-shelter-single-page/site/config.mjs +30 -0
  42. package/examples/complete-sites/dog-shelter-single-page/site/content.md +38 -0
  43. package/examples/complete-sites/dog-shelter-single-page/site/images/what-we-do/dog-house.svg +25 -0
  44. package/examples/complete-sites/dog-shelter-single-page/site/images/you-can-help/heart.svg +13 -0
  45. package/examples/complete-sites/dog-shelter-single-page/site/public/favicon.svg +10 -0
  46. package/examples/complete-sites/dog-shelter-single-page/site/public/robots.txt +2 -0
  47. package/examples/complete-sites/dog-shelter-single-page/site/sitewide-content.md +6 -0
  48. package/examples/complete-sites/dog-shelter-single-page/site/theme.md +25 -0
  49. package/examples/feature-demos/media-and-surfaces/.astro/collections/site.schema.json +59 -0
  50. package/examples/feature-demos/media-and-surfaces/.astro/collections/sitewide.schema.json +138 -0
  51. package/examples/feature-demos/media-and-surfaces/.astro/collections/theme.schema.json +755 -0
  52. package/examples/feature-demos/media-and-surfaces/.astro/content-modules.mjs +1 -0
  53. package/examples/feature-demos/media-and-surfaces/.astro/content.d.ts +183 -0
  54. package/examples/feature-demos/media-and-surfaces/.astro/data-store.json +1 -0
  55. package/examples/feature-demos/media-and-surfaces/.astro/dev.log +11 -0
  56. package/examples/feature-demos/media-and-surfaces/.astro/settings.json +5 -0
  57. package/examples/feature-demos/media-and-surfaces/.astro/types.d.ts +2 -0
  58. package/examples/feature-demos/media-and-surfaces/README.md +15 -0
  59. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/_metadata.json +74 -0
  60. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro-B3JHJyiv.js +434 -0
  61. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro-B3JHJyiv.js.map +1 -0
  62. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_n_aria-query.js +6390 -0
  63. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_n_aria-query.js.map +1 -0
  64. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_n_axobject-query.js +2702 -0
  65. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_n_axobject-query.js.map +1 -0
  66. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_n_html-escaper.js +65 -0
  67. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_n_html-escaper.js.map +1 -0
  68. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +455 -0
  69. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +1 -0
  70. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/audit-C3SVr7dT.js +1534 -0
  71. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/audit-C3SVr7dT.js.map +1 -0
  72. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/highlight-BLtvqtzO.js +48 -0
  73. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/highlight-BLtvqtzO.js.map +1 -0
  74. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/icons-C92i61-2.js +42 -0
  75. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/icons-C92i61-2.js.map +1 -0
  76. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/settings-v5GgKks_.js +190 -0
  77. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/settings-v5GgKks_.js.map +1 -0
  78. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/toolbar-BFvJVp26.js +458 -0
  79. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/toolbar-BFvJVp26.js.map +1 -0
  80. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/ui-library-Cd35oAvo.js +1016 -0
  81. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/ui-library-Cd35oAvo.js.map +1 -0
  82. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/window-vo3A6TMn.js +32 -0
  83. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/window-vo3A6TMn.js.map +1 -0
  84. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/xray-DwhzGckb.js +111 -0
  85. package/examples/feature-demos/media-and-surfaces/node_modules/.vite/deps/xray-DwhzGckb.js.map +1 -0
  86. package/examples/feature-demos/media-and-surfaces/site/.norna/generated-images.json +66 -0
  87. package/examples/feature-demos/media-and-surfaces/site/config.mjs +30 -0
  88. package/examples/feature-demos/media-and-surfaces/site/content.md +64 -0
  89. package/examples/feature-demos/media-and-surfaces/site/public/robots.txt +2 -0
  90. package/examples/feature-demos/media-and-surfaces/site/routes/010-media/images/cards/card-carousel.svg +11 -0
  91. package/examples/feature-demos/media-and-surfaces/site/routes/010-media/images/cards/card-stack.svg +9 -0
  92. package/examples/feature-demos/media-and-surfaces/site/routes/010-media/images/cards/card-surfaces.svg +8 -0
  93. package/examples/feature-demos/media-and-surfaces/site/routes/010-media/images/carousel/carousel-one.svg +8 -0
  94. package/examples/feature-demos/media-and-surfaces/site/routes/010-media/images/carousel/carousel-three.svg +13 -0
  95. package/examples/feature-demos/media-and-surfaces/site/routes/010-media/images/carousel/carousel-two.svg +8 -0
  96. package/examples/feature-demos/media-and-surfaces/site/routes/010-media/images/stack/stack-one.svg +8 -0
  97. package/examples/feature-demos/media-and-surfaces/site/routes/010-media/images/stack/stack-two.svg +9 -0
  98. package/examples/feature-demos/media-and-surfaces/site/routes/010-media/route-content.md +85 -0
  99. package/examples/feature-demos/media-and-surfaces/site/routes/010-media/theme.md +3 -0
  100. package/examples/feature-demos/media-and-surfaces/site/routes/020-surfaces/route-content.md +44 -0
  101. package/examples/feature-demos/media-and-surfaces/site/routes/020-surfaces/theme.md +8 -0
  102. package/examples/feature-demos/media-and-surfaces/site/sitewide-content.md +6 -0
  103. package/examples/feature-demos/media-and-surfaces/site/theme.md +3 -0
  104. package/examples/{typography-presets/.astro/collections/theme.schema.json → feature-demos/theme-presets/.astro/collections/routeTheme.schema.json} +143 -65
  105. package/examples/feature-demos/theme-presets/.astro/collections/site.schema.json +59 -0
  106. package/examples/feature-demos/theme-presets/.astro/collections/sitewide.schema.json +138 -0
  107. package/examples/feature-demos/theme-presets/.astro/collections/theme.schema.json +755 -0
  108. package/examples/feature-demos/theme-presets/.astro/content-assets.mjs +4 -0
  109. package/examples/feature-demos/theme-presets/.astro/content-modules.mjs +1 -0
  110. package/examples/feature-demos/theme-presets/.astro/content.d.ts +183 -0
  111. package/examples/feature-demos/theme-presets/.astro/data-store.json +1 -0
  112. package/examples/feature-demos/theme-presets/.astro/dev-local.json +8 -0
  113. package/examples/feature-demos/theme-presets/.astro/dev.json +15 -0
  114. package/examples/feature-demos/theme-presets/.astro/dev.log +127 -0
  115. package/examples/feature-demos/theme-presets/.astro/types.d.ts +2 -0
  116. package/examples/feature-demos/theme-presets/README.md +14 -0
  117. package/examples/feature-demos/theme-presets/node_modules/.vite/deps/embla-carousel.js +1389 -0
  118. package/examples/feature-demos/theme-presets/node_modules/.vite/deps/embla-carousel.js.map +1 -0
  119. package/examples/feature-demos/theme-presets/node_modules/.vite/deps/package.json +3 -0
  120. package/examples/feature-demos/theme-presets/node_modules/.vite/deps/rolldown-runtime-BvCyGRYZ.js +4 -0
  121. package/examples/feature-demos/theme-presets/site/.norna/generated-images.json +290 -0
  122. package/examples/{typography-presets → feature-demos/theme-presets}/site/config.mjs +4 -16
  123. package/examples/feature-demos/theme-presets/site/content.md +17 -0
  124. package/examples/feature-demos/theme-presets/site/routes/010-portfolio/route-content.md +34 -0
  125. package/examples/feature-demos/theme-presets/site/routes/010-portfolio/theme.md +3 -0
  126. package/examples/feature-demos/theme-presets/site/routes/020-documentation/route-content.md +33 -0
  127. package/examples/feature-demos/theme-presets/site/routes/020-documentation/theme.md +3 -0
  128. package/examples/feature-demos/theme-presets/site/routes/030-project/route-content.md +34 -0
  129. package/examples/feature-demos/theme-presets/site/routes/030-project/theme.md +3 -0
  130. package/examples/feature-demos/theme-presets/site/routes/040-statement/route-content.md +34 -0
  131. package/examples/feature-demos/theme-presets/site/routes/040-statement/theme.md +3 -0
  132. package/examples/feature-demos/theme-presets/site/sitewide-content.md +14 -0
  133. package/examples/feature-demos/theme-presets/site/theme.md +3 -0
  134. package/fixtures/basic/site/config.mjs +0 -13
  135. package/fixtures/basic/site/sitewide-content.md +12 -0
  136. package/fixtures/basic/site/theme.md +2 -2
  137. package/fixtures/content-model-v2/site/content.md +2 -5
  138. package/fixtures/content-model-v2/site/theme.md +1 -3
  139. package/package.json +9 -4
  140. package/scripts/build-pages.mjs +71 -0
  141. package/scripts/check-config.mjs +23 -1
  142. package/scripts/export-theme-preset.mjs +31 -0
  143. package/scripts/init-site.mjs +1 -0
  144. package/scripts/lib/example-sites.mjs +34 -0
  145. package/scripts/lib/frontmatter-yaml.mjs +28 -0
  146. package/scripts/lib/logo-assets.mjs +35 -0
  147. package/scripts/lib/norna-markdown-blocks.mjs +166 -0
  148. package/scripts/lib/presentation.mjs +106 -47
  149. package/scripts/lib/project-config.mjs +68 -105
  150. package/scripts/lib/site-content.mjs +26 -118
  151. package/scripts/lib/site-paths.mjs +2 -0
  152. package/scripts/lib/sitewide-config.mjs +34 -0
  153. package/scripts/lib/theme-config.mjs +75 -8
  154. package/scripts/lib/theme-presets.mjs +209 -0
  155. package/scripts/lib/typography.mjs +20 -26
  156. package/scripts/show-typography.mjs +43 -149
  157. package/scripts/sync-content-sections.mjs +19 -17
  158. package/scripts/test-content-check.mjs +59 -27
  159. package/scripts/test-content-model-v2.mjs +154 -0
  160. package/scripts/test-engine-commands.mjs +4 -1
  161. package/scripts/test-examples.mjs +15 -0
  162. package/scripts/test-navigation.mjs +73 -45
  163. package/scripts/test-package-check.mjs +66 -24
  164. package/scripts/test-temporary-visibility.mjs +19 -2
  165. package/scripts/test-theme-presets.mjs +151 -0
  166. package/src/components/CardList.astro +5 -2
  167. package/src/components/GalleryGrid.astro +37 -16
  168. package/src/components/SiteBanners.astro +100 -0
  169. package/src/components/SiteNavigation.astro +48 -31
  170. package/src/components/SitePage.astro +55 -13
  171. package/src/components/SiteSection.astro +193 -35
  172. package/src/content.config.ts +80 -48
  173. package/src/layouts/BaseLayout.astro +64 -14
  174. package/src/lib/routeThemes.ts +61 -0
  175. package/src/lib/sectionContent.ts +235 -51
  176. package/src/lib/sitePublicAssets.ts +1 -0
  177. package/src/styles/global.css +284 -93
  178. package/starters/basic/.github/workflows/deploy.yml +1 -1
  179. package/starters/basic/README.md +7 -6
  180. package/starters/basic/package.json +1 -0
  181. package/starters/basic/site/config.mjs +0 -13
  182. package/starters/basic/site/content.md +1 -1
  183. package/starters/basic/site/sitewide-content.md +14 -0
  184. package/starters/basic/site/theme.md +13 -24
  185. package/starters/project/.github/workflows/deploy.yml +1 -1
  186. package/starters/project/README.md +10 -8
  187. package/starters/project/package.json +1 -0
  188. package/starters/project/site/config.mjs +0 -13
  189. package/starters/project/site/sitewide-content.md +14 -0
  190. package/starters/project/site/theme.md +3 -11
  191. package/examples/dog-gallery/site/.norna/generated-images.json +0 -226
  192. package/examples/dog-gallery/site/config.mjs +0 -67
  193. package/examples/dog-gallery/site/content.md +0 -128
  194. package/examples/dog-gallery/site/images/black-dogs/black-puppy-meadow.png +0 -0
  195. package/examples/dog-gallery/site/images/black-dogs/photo-of-a-black-dog.jpg +0 -0
  196. package/examples/dog-gallery/site/images/brown-dogs/brown-dog.jpg +0 -0
  197. package/examples/dog-gallery/site/images/brown-dogs/dog-accompanies-master.jpg +0 -0
  198. package/examples/dog-gallery/site/images/golden-dogs/golden-retriever.jpg +0 -0
  199. package/examples/dog-gallery/site/images/golden-dogs/toller-puppy.jpg +0 -0
  200. package/examples/dog-gallery/site/images/white-dogs/white-cute-dog.jpg +0 -0
  201. package/examples/dog-gallery/site/images/white-dogs/white-puppy-garden.png +0 -0
  202. package/examples/dog-gallery/site/public/favicon.svg +0 -7
  203. package/examples/dog-gallery/site/routes/010-dog-care/route-content.md +0 -38
  204. package/examples/dog-gallery/site/theme.md +0 -112
  205. package/examples/typography-presets/.astro/collections/site.schema.json +0 -776
  206. package/examples/typography-presets/.astro/data-store.json +0 -1
  207. package/examples/typography-presets/.astro/dev.log +0 -24
  208. package/examples/typography-presets/site/.norna/generated-images.json +0 -290
  209. package/examples/typography-presets/site/content.md +0 -134
  210. package/examples/typography-presets/site/theme.md +0 -43
  211. /package/examples/{typography-presets/.astro/content-modules.mjs → complete-sites/dog-shelter-multi-page/.astro/content-assets.mjs} +0 -0
  212. /package/examples/{typography-presets → complete-sites/dog-shelter-multi-page}/.astro/types.d.ts +0 -0
  213. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro-D_GIpJIE.js +0 -0
  214. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro-D_GIpJIE.js.map +0 -0
  215. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro_n_aria-query.js +0 -0
  216. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro_n_aria-query.js.map +0 -0
  217. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro_n_axobject-query.js +0 -0
  218. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro_n_axobject-query.js.map +0 -0
  219. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro_n_html-escaper.js +0 -0
  220. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro_n_html-escaper.js.map +0 -0
  221. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +0 -0
  222. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +0 -0
  223. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/audit-DM9Hkl5B.js +0 -0
  224. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/audit-DM9Hkl5B.js.map +0 -0
  225. /package/examples/{typography-presets → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/embla-carousel.js +0 -0
  226. /package/examples/{typography-presets → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/embla-carousel.js.map +0 -0
  227. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/highlight-DnZ1ULOL.js +0 -0
  228. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/highlight-DnZ1ULOL.js.map +0 -0
  229. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/icons-RyebKi45.js +0 -0
  230. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/icons-RyebKi45.js.map +0 -0
  231. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/package.json +0 -0
  232. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/rolldown-runtime-BvCyGRYZ.js +0 -0
  233. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/settings-DaU-gNnK.js +0 -0
  234. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/settings-DaU-gNnK.js.map +0 -0
  235. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/toolbar-63PA619H.js +0 -0
  236. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/toolbar-63PA619H.js.map +0 -0
  237. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/ui-library--bkcNSYz.js +0 -0
  238. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/ui-library--bkcNSYz.js.map +0 -0
  239. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/window-jPkpKVcy.js +0 -0
  240. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/window-jPkpKVcy.js.map +0 -0
  241. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/xray-BXCHpEJP.js +0 -0
  242. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/node_modules/.vite/deps/xray-BXCHpEJP.js.map +0 -0
  243. /package/examples/{dog-gallery → complete-sites/dog-shelter-multi-page}/site/public/robots.txt +0 -0
  244. /package/examples/{typography-presets → feature-demos/media-and-surfaces}/.astro/content-assets.mjs +0 -0
  245. /package/examples/{typography-presets → feature-demos/media-and-surfaces}/node_modules/.vite/deps/package.json +0 -0
  246. /package/examples/{typography-presets → feature-demos/media-and-surfaces}/node_modules/.vite/deps/rolldown-runtime-BvCyGRYZ.js +0 -0
  247. /package/examples/{typography-presets → feature-demos/theme-presets}/.astro/settings.json +0 -0
  248. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/_metadata.json +0 -0
  249. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/astro-D_GIpJIE.js +0 -0
  250. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/astro-D_GIpJIE.js.map +0 -0
  251. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/astro_n_aria-query.js +0 -0
  252. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/astro_n_aria-query.js.map +0 -0
  253. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/astro_n_axobject-query.js +0 -0
  254. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/astro_n_axobject-query.js.map +0 -0
  255. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/astro_n_html-escaper.js +0 -0
  256. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/astro_n_html-escaper.js.map +0 -0
  257. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +0 -0
  258. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +0 -0
  259. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/audit-DM9Hkl5B.js +0 -0
  260. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/audit-DM9Hkl5B.js.map +0 -0
  261. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/highlight-DnZ1ULOL.js +0 -0
  262. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/highlight-DnZ1ULOL.js.map +0 -0
  263. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/icons-RyebKi45.js +0 -0
  264. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/icons-RyebKi45.js.map +0 -0
  265. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/settings-DaU-gNnK.js +0 -0
  266. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/settings-DaU-gNnK.js.map +0 -0
  267. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/toolbar-63PA619H.js +0 -0
  268. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/toolbar-63PA619H.js.map +0 -0
  269. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/ui-library--bkcNSYz.js +0 -0
  270. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/ui-library--bkcNSYz.js.map +0 -0
  271. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/window-jPkpKVcy.js +0 -0
  272. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/window-jPkpKVcy.js.map +0 -0
  273. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/xray-BXCHpEJP.js +0 -0
  274. /package/examples/{typography-presets → feature-demos/theme-presets}/node_modules/.vite/deps/xray-BXCHpEJP.js.map +0 -0
  275. /package/examples/{typography-presets → feature-demos/theme-presets}/site/public/robots.txt +0 -0
  276. /package/examples/{typography-presets/site/images/quiet-gallery → feature-demos/theme-presets/site/routes/010-portfolio/images/portfolio}/puppy-portrait.jpg +0 -0
  277. /package/examples/{typography-presets/site/images/quiet-gallery → feature-demos/theme-presets/site/routes/010-portfolio/images/portfolio}/shepherd-portrait.jpg +0 -0
  278. /package/examples/{typography-presets/site/images/quiet-gallery → feature-demos/theme-presets/site/routes/010-portfolio/images/portfolio}/white-puppy.jpg +0 -0
  279. /package/examples/{typography-presets/site/images/compact-gallery → feature-demos/theme-presets/site/routes/020-documentation/images/documentation}/black-schnauzer.jpg +0 -0
  280. /package/examples/{typography-presets/site/images/compact-gallery → feature-demos/theme-presets/site/routes/020-documentation/images/documentation}/retriever-portrait.jpg +0 -0
  281. /package/examples/{typography-presets/site/images/compact-gallery → feature-demos/theme-presets/site/routes/020-documentation/images/documentation}/schnauzer-portrait.jpg +0 -0
  282. /package/examples/{typography-presets/site/images/text-forward → feature-demos/theme-presets/site/routes/030-project/images/project}/boxer-portrait.jpg +0 -0
  283. /package/examples/{typography-presets/site/images/text-forward → feature-demos/theme-presets/site/routes/030-project/images/project}/grass-puppy.jpg +0 -0
  284. /package/examples/{typography-presets/site/images/text-forward → feature-demos/theme-presets/site/routes/030-project/images/project}/grey-street-dog.jpg +0 -0
  285. /package/examples/{typography-presets/site → feature-demos/theme-presets/site/routes/040-statement}/images/statement/black-pedigree.jpg +0 -0
  286. /package/examples/{typography-presets/site → feature-demos/theme-presets/site/routes/040-statement}/images/statement/sheepdog-grass.jpg +0 -0
  287. /package/examples/{typography-presets/site → feature-demos/theme-presets/site/routes/040-statement}/images/statement/sheepdog-wide.jpg +0 -0
@@ -1,71 +1,130 @@
1
1
  import {
2
2
  defaultTypography,
3
3
  resolveTypographyConfig,
4
- resolveTypographyOverride,
5
4
  } from './typography.mjs';
5
+ import { resolveThemeConfig } from './theme-presets.mjs';
6
6
 
7
- export const fallbackPresentationColors = Object.freeze({
8
- backgroundColor: 'transparent',
9
- textColor: 'var(--color-text)',
7
+ export const presentationPaletteNames = ['dark', 'light', 'paper'];
8
+
9
+ const presentationPalettes = Object.freeze({
10
+ dark: {
11
+ colorScheme: 'dark',
12
+ page: { backgroundColor: '#000000', textColor: '#f2eee6' },
13
+ surfaces: {
14
+ base: { backgroundColor: '#000000', textColor: '#f2eee6' },
15
+ soft: { backgroundColor: '#171717', textColor: '#f2eee6' },
16
+ emphasis: { backgroundColor: '#252525', textColor: '#f2eee6' },
17
+ },
18
+ frame: { backgroundColor: '#000000', textColor: '#f2eee6' },
19
+ css: {
20
+ surface: '#101010',
21
+ muted: '#aaa49a',
22
+ soft: '#302f2c',
23
+ accent: '#d8d2c8',
24
+ border: 'rgb(255 255 255 / 14%)',
25
+ navBackground: 'rgb(0 0 0 / 90%)',
26
+ navSeparator: 'rgb(255 255 255 / 28%)',
27
+ },
28
+ },
29
+ light: {
30
+ colorScheme: 'light',
31
+ page: { backgroundColor: '#ffffff', textColor: '#17201d' },
32
+ surfaces: {
33
+ base: { backgroundColor: '#ffffff', textColor: '#17201d' },
34
+ soft: { backgroundColor: '#f1f4f2', textColor: '#17201d' },
35
+ emphasis: { backgroundColor: '#dde7e1', textColor: '#17201d' },
36
+ },
37
+ frame: { backgroundColor: '#ffffff', textColor: '#17201d' },
38
+ css: {
39
+ surface: '#f7f8f7',
40
+ muted: '#5e655f',
41
+ soft: '#d9dfda',
42
+ accent: '#38645a',
43
+ border: 'rgb(0 0 0 / 14%)',
44
+ navBackground: 'rgb(255 255 255 / 92%)',
45
+ navSeparator: 'rgb(0 0 0 / 20%)',
46
+ },
47
+ },
48
+ paper: {
49
+ colorScheme: 'light',
50
+ page: { backgroundColor: '#f8f5ee', textColor: '#272522' },
51
+ surfaces: {
52
+ base: { backgroundColor: '#f8f5ee', textColor: '#272522' },
53
+ soft: { backgroundColor: '#ebe5d9', textColor: '#272522' },
54
+ emphasis: { backgroundColor: '#ded4c5', textColor: '#272522' },
55
+ },
56
+ frame: { backgroundColor: '#f8f5ee', textColor: '#272522' },
57
+ css: {
58
+ surface: '#f0ebe1',
59
+ muted: '#746e63',
60
+ soft: '#d8d0c4',
61
+ accent: '#685a43',
62
+ border: 'rgb(39 37 34 / 18%)',
63
+ navBackground: 'rgb(248 245 238 / 92%)',
64
+ navSeparator: 'rgb(39 37 34 / 24%)',
65
+ },
66
+ },
10
67
  });
11
68
 
12
69
  const normalizePresentation = (presentation) => presentation ?? {};
13
70
 
14
- export const resolveThemePresentation = (theme) => {
15
- const normalizedTheme = theme ?? {};
16
- const normalizedThemePresentation = normalizePresentation(normalizedTheme.presentation);
71
+ export const getPresentationPalette = (paletteName = 'dark') => {
72
+ const palette = presentationPalettes[paletteName];
73
+ if (!palette) {
74
+ throw new Error(`Unknown presentation palette: ${paletteName}. Use one of: ${presentationPaletteNames.join(', ')}`);
75
+ }
76
+
77
+ return palette;
78
+ };
17
79
 
80
+ export const getPresentationCssVariables = (presentation) => {
81
+ const { palette } = presentation;
18
82
  return {
19
- backgroundColor: normalizedThemePresentation.backgroundColor ?? fallbackPresentationColors.backgroundColor,
20
- textColor: normalizedThemePresentation.textColor ?? fallbackPresentationColors.textColor,
21
- inlineStyles: normalizedThemePresentation.inlineStyles ?? {},
22
- typography: resolveTypographyConfig(normalizedTheme.typography ?? defaultTypography),
83
+ '--color-page': palette.page.backgroundColor,
84
+ '--color-text': palette.page.textColor,
85
+ '--color-surface': palette.css.surface,
86
+ '--color-muted': palette.css.muted,
87
+ '--color-soft': palette.css.soft,
88
+ '--color-accent': palette.css.accent,
89
+ '--color-border': palette.css.border,
90
+ '--color-nav-background': palette.css.navBackground,
91
+ '--color-nav-separator': palette.css.navSeparator,
92
+ '--color-scheme': palette.colorScheme,
23
93
  };
24
94
  };
25
95
 
26
- export const resolvePagePresentation = (theme, pagePresentation) => {
27
- const resolvedThemePresentation = resolveThemePresentation(theme);
28
- const normalizedPagePresentation = normalizePresentation(pagePresentation);
96
+ export const resolveThemePresentation = (theme, sourceLabel = 'theme.md') => {
97
+ const normalizedTheme = resolveThemeConfig(theme, sourceLabel);
98
+ const normalizedThemePresentation = normalizePresentation(normalizedTheme.presentation);
99
+ const paletteName = normalizedThemePresentation.palette ?? 'dark';
100
+ const palette = getPresentationPalette(paletteName);
29
101
 
30
102
  return {
31
- backgroundColor: normalizedPagePresentation.backgroundColor ?? resolvedThemePresentation.backgroundColor,
32
- textColor: normalizedPagePresentation.textColor ?? resolvedThemePresentation.textColor,
33
- inlineStyles: resolvedThemePresentation.inlineStyles,
34
- typography: resolveTypographyOverride(
35
- resolvedThemePresentation.typography,
36
- normalizedPagePresentation.typography,
37
- ),
103
+ paletteName,
104
+ palette,
105
+ sectionSurfaces: {
106
+ mode: normalizedThemePresentation.sectionSurfaces?.mode ?? 'none',
107
+ sequence: normalizedThemePresentation.sectionSurfaces?.sequence ?? ['base', 'soft', 'emphasis'],
108
+ },
109
+ typography: resolveTypographyConfig(normalizedTheme.typography ?? defaultTypography),
38
110
  };
39
111
  };
40
112
 
41
- const getColorsFromPresentation = (presentation) => ({
42
- backgroundColor: presentation.backgroundColor,
43
- textColor: presentation.textColor,
44
- });
45
-
46
- const resolveColorSource = ({ colors, themeFrameColors, pagePresentation }) => {
47
- if (!colors || colors === 'theme') {
48
- return themeFrameColors;
49
- }
50
-
51
- if (colors === 'presentation') {
52
- return getColorsFromPresentation(pagePresentation);
53
- }
113
+ export const resolvePagePresentation = (theme, sourceLabel) => {
114
+ const resolvedThemePresentation = resolveThemePresentation(theme, sourceLabel);
54
115
 
55
- return colors;
116
+ return resolvedThemePresentation;
56
117
  };
57
118
 
58
- export const resolveFrameColors = ({ theme, pagePresentation, pageFrame }) => {
59
- const resolvedThemePresentation = resolveThemePresentation(theme);
60
- const themeFrameColors = resolveColorSource({
61
- colors: theme?.frame?.colors ?? 'presentation',
62
- themeFrameColors: getColorsFromPresentation(resolvedThemePresentation),
63
- pagePresentation: resolvedThemePresentation,
64
- });
119
+ export const resolveSectionSurface = (pagePresentation, sectionIndex) => {
120
+ const surfaceName = pagePresentation.sectionSurfaces.mode === 'cycle'
121
+ ? pagePresentation.sectionSurfaces.sequence[sectionIndex % pagePresentation.sectionSurfaces.sequence.length]
122
+ : 'base';
65
123
 
66
- return resolveColorSource({
67
- colors: pageFrame?.colors ?? 'theme',
68
- themeFrameColors,
69
- pagePresentation,
70
- });
124
+ return {
125
+ name: surfaceName,
126
+ ...pagePresentation.palette.surfaces[surfaceName],
127
+ };
71
128
  };
129
+
130
+ export const resolveFrameColors = ({ theme, sourceLabel }) => resolveThemePresentation(theme, sourceLabel).palette.frame;
@@ -1,6 +1,7 @@
1
1
  import { pathToFileURL } from 'node:url';
2
2
  import { siteConfigLabel, siteConfigPath, siteThemeLabel } from './site-paths.mjs';
3
3
  import { readThemeConfig } from './theme-config.mjs';
4
+ import { resolveThemeConfig } from './theme-presets.mjs';
4
5
 
5
6
  const { default: siteConfig } = await import(/* @vite-ignore */ pathToFileURL(siteConfigPath).href);
6
7
  const themeConfig = await readThemeConfig();
@@ -37,20 +38,6 @@ const readString = (object, key, path, sourceLabel = siteConfigLabel) => {
37
38
  return value.trim();
38
39
  };
39
40
 
40
- const readOptionalString = (object, key, path, sourceLabel = siteConfigLabel) => {
41
- const value = object[key];
42
-
43
- if (value === undefined || value === null || value === '') {
44
- return null;
45
- }
46
-
47
- if (typeof value !== 'string' || value.trim() === '') {
48
- throw new Error(`${path}.${key} must be a non-empty string when set in ${sourceLabel}.`);
49
- }
50
-
51
- return value.trim();
52
- };
53
-
54
41
  const readFontFamily = (object, key, path, fallback, sourceLabel = siteConfigLabel) => {
55
42
  const value = object[key] ?? fallback;
56
43
 
@@ -242,58 +229,19 @@ const readLocale = (rawLocale) => {
242
229
  lang: lang.trim(),
243
230
  labels: Object.freeze({
244
231
  closeMenu: readString({ closeMenu: labels.closeMenu ?? 'Close menu' }, 'closeMenu', 'locale.labels'),
232
+ dismissBanner: readString({ dismissBanner: labels.dismissBanner ?? 'Dismiss notice' }, 'dismissBanner', 'locale.labels'),
245
233
  skipToContent: readString({ skipToContent: labels.skipToContent ?? 'Skip to content' }, 'skipToContent', 'locale.labels'),
246
234
  sectionNavigation: readString({ sectionNavigation: labels.sectionNavigation ?? 'Sections' }, 'sectionNavigation', 'locale.labels'),
247
235
  gallery: readString({ gallery: labels.gallery ?? 'Images' }, 'gallery', 'locale.labels'),
236
+ note: readString({ note: labels.note ?? 'Note' }, 'note', 'locale.labels'),
248
237
  menu: readString({ menu: labels.menu ?? 'Menu' }, 'menu', 'locale.labels'),
249
238
  pageNavigation: readString({ pageNavigation: labels.pageNavigation ?? 'On this page' }, 'pageNavigation', 'locale.labels'),
250
239
  siteNavigation: readString({ siteNavigation: labels.siteNavigation ?? 'Pages' }, 'siteNavigation', 'locale.labels'),
240
+ siteBanners: readString({ siteBanners: labels.siteBanners ?? 'Site notices' }, 'siteBanners', 'locale.labels'),
251
241
  }),
252
242
  });
253
243
  };
254
244
 
255
- const readDateTimeFormat = (object, path) => {
256
- const dateTimeFormat = assertObject(object, path);
257
- const locale = readString(dateTimeFormat, 'locale', path);
258
- const timeZone = readString(dateTimeFormat, 'timeZone', path);
259
- const dateStyle = readString(dateTimeFormat, 'dateStyle', path);
260
- const timeStyle = readString(dateTimeFormat, 'timeStyle', path);
261
-
262
- try {
263
- new Intl.DateTimeFormat(locale, {
264
- dateStyle,
265
- timeStyle,
266
- timeZone,
267
- });
268
- } catch (error) {
269
- const message = error instanceof Error ? error.message : String(error);
270
- throw new Error(`${path} must be a valid Intl.DateTimeFormat configuration in ${siteConfigLabel}: ${message}`);
271
- }
272
-
273
- return Object.freeze({
274
- dateStyle,
275
- locale,
276
- timeStyle,
277
- timeZone,
278
- });
279
- };
280
-
281
- const readBuildInfo = (footer) => {
282
- const value = footer.buildInfo;
283
-
284
- if (value === undefined || value === null || value === false) {
285
- return null;
286
- }
287
-
288
- const buildInfo = assertObject(value, 'footer.buildInfo');
289
-
290
- return Object.freeze({
291
- enabled: readBoolean(buildInfo, 'enabled', 'footer.buildInfo', true),
292
- text: readString(buildInfo, 'text', 'footer.buildInfo'),
293
- dateTimeFormat: readDateTimeFormat(buildInfo.dateTimeFormat, 'footer.buildInfo.dateTimeFormat'),
294
- });
295
- };
296
-
297
245
  const rawConfig = assertObject(siteConfig, 'default export');
298
246
  const misplacedThemeKeys = ['layout', 'gallery', 'typography'].filter((key) => key in rawConfig);
299
247
  if (misplacedThemeKeys.length > 0) {
@@ -302,13 +250,8 @@ if (misplacedThemeKeys.length > 0) {
302
250
 
303
251
  const rawTheme = assertObject(themeConfig, 'theme frontmatter', siteThemeLabel);
304
252
  const rawSite = assertObject(rawConfig.site, 'site');
305
- const rawLayout = assertObject(rawTheme.layout ?? {}, 'layout', siteThemeLabel);
306
- const rawLayoutSpacing = assertObject(rawLayout.spacing ?? {}, 'layout.spacing', siteThemeLabel);
307
- const rawGallery = assertObject(rawTheme.gallery ?? {}, 'gallery', siteThemeLabel);
308
- const rawTypography = assertObject(rawTheme.typography ?? {}, 'typography', siteThemeLabel);
309
253
  const rawNavigation = assertObject(rawConfig.navigation ?? {}, 'navigation');
310
254
  const rawLocale = rawConfig.locale ?? {};
311
- const rawFooter = assertObject(rawConfig.footer ?? {}, 'footer');
312
255
  const rawGithub = assertObject(rawConfig.github, 'github');
313
256
  const rawDeploy = assertObject(rawConfig.deploy ?? {}, 'deploy');
314
257
  const rawDeployWatch = assertObject(rawDeploy.watch ?? {}, 'deploy.watch');
@@ -317,9 +260,9 @@ const defaultFontFamily = "Arial, 'Helvetica Neue', Helvetica, sans-serif";
317
260
  const layoutDensityNames = ['compact', 'normal', 'airy'];
318
261
  const layoutDensityProfiles = Object.freeze({
319
262
  compact: Object.freeze({
320
- bodyToImages: Object.freeze({
321
- desktop: '1rem',
322
- mobile: '0.9rem',
263
+ blockGap: Object.freeze({
264
+ desktop: '1.25em',
265
+ mobile: '1.1em',
323
266
  }),
324
267
  finalSectionBottom: Object.freeze({
325
268
  desktop: 'clamp(1.2rem, 2.4vw, 2.25rem)',
@@ -329,6 +272,10 @@ const layoutDensityProfiles = Object.freeze({
329
272
  desktop: 'clamp(1rem, 2vw, 1.75rem)',
330
273
  mobile: '1rem',
331
274
  }),
275
+ headingToBlock: Object.freeze({
276
+ desktop: '0.65em',
277
+ mobile: '0.6em',
278
+ }),
332
279
  imageGap: Object.freeze({
333
280
  desktop: 'clamp(1rem, 2vw, 1.5rem)',
334
281
  mobile: '1.25rem',
@@ -339,9 +286,9 @@ const layoutDensityProfiles = Object.freeze({
339
286
  }),
340
287
  }),
341
288
  normal: Object.freeze({
342
- bodyToImages: Object.freeze({
343
- desktop: '1.25rem',
344
- mobile: '1rem',
289
+ blockGap: Object.freeze({
290
+ desktop: '1.5em',
291
+ mobile: '1.25em',
345
292
  }),
346
293
  finalSectionBottom: Object.freeze({
347
294
  desktop: 'clamp(1.4rem, 3vw, 2.75rem)',
@@ -351,6 +298,10 @@ const layoutDensityProfiles = Object.freeze({
351
298
  desktop: 'clamp(1.25rem, 3vw, 2.5rem)',
352
299
  mobile: '1.25rem',
353
300
  }),
301
+ headingToBlock: Object.freeze({
302
+ desktop: '0.75em',
303
+ mobile: '0.7em',
304
+ }),
354
305
  imageGap: Object.freeze({
355
306
  desktop: 'clamp(1.25rem, 2.8vw, 2rem)',
356
307
  mobile: '1.5rem',
@@ -361,9 +312,9 @@ const layoutDensityProfiles = Object.freeze({
361
312
  }),
362
313
  }),
363
314
  airy: Object.freeze({
364
- bodyToImages: Object.freeze({
365
- desktop: '1.5rem',
366
- mobile: '1.25rem',
315
+ blockGap: Object.freeze({
316
+ desktop: '1.75em',
317
+ mobile: '1.5em',
367
318
  }),
368
319
  finalSectionBottom: Object.freeze({
369
320
  desktop: 'clamp(2.25rem, 5vw, 4.5rem)',
@@ -373,6 +324,10 @@ const layoutDensityProfiles = Object.freeze({
373
324
  desktop: 'clamp(2rem, 5vw, 4rem)',
374
325
  mobile: '1.5rem',
375
326
  }),
327
+ headingToBlock: Object.freeze({
328
+ desktop: '0.9em',
329
+ mobile: '0.8em',
330
+ }),
376
331
  imageGap: Object.freeze({
377
332
  desktop: 'clamp(1.5rem, 3.5vw, 2.75rem)',
378
333
  mobile: '2rem',
@@ -383,51 +338,59 @@ const layoutDensityProfiles = Object.freeze({
383
338
  }),
384
339
  }),
385
340
  });
386
- const layoutDensity = readEnum(rawLayout, 'density', 'layout', layoutDensityNames, 'normal', siteThemeLabel);
387
- const layoutSpacingDefaults = layoutDensityProfiles[layoutDensity];
341
+ export const resolveThemeVisualConfig = (theme, sourceLabel = siteThemeLabel) => {
342
+ const rawThemeConfig = assertObject(resolveThemeConfig(theme, sourceLabel), 'theme frontmatter', sourceLabel);
343
+ const rawLayoutConfig = assertObject(rawThemeConfig.layout ?? {}, 'layout', sourceLabel);
344
+ const rawLayoutSpacingConfig = assertObject(rawLayoutConfig.spacing ?? {}, 'layout.spacing', sourceLabel);
345
+ const rawGalleryConfig = assertObject(rawThemeConfig.gallery ?? {}, 'gallery', sourceLabel);
346
+ const rawTypographyConfig = assertObject(rawThemeConfig.typography ?? {}, 'typography', sourceLabel);
347
+ const resolvedLayoutDensity = readEnum(rawLayoutConfig, 'density', 'layout', layoutDensityNames, 'normal', sourceLabel);
348
+ const resolvedLayoutSpacingDefaults = layoutDensityProfiles[resolvedLayoutDensity];
349
+
350
+ return Object.freeze({
351
+ layout: Object.freeze({
352
+ density: resolvedLayoutDensity,
353
+ gutter: readResponsiveCssLength(rawLayoutConfig, 'gutter', 'layout', Object.freeze({
354
+ desktop: 'clamp(1.25rem, 4vw, 3rem)',
355
+ mobile: '1rem',
356
+ }), sourceLabel),
357
+ pageWidth: readCssLength(rawLayoutConfig, 'pageWidth', 'layout', '1180px', sourceLabel),
358
+ spacing: Object.freeze({
359
+ blockGap: readResponsiveCssLength(rawLayoutSpacingConfig, 'blockGap', 'layout.spacing', resolvedLayoutSpacingDefaults.blockGap, sourceLabel),
360
+ finalSectionBottom: readResponsiveCssLength(rawLayoutSpacingConfig, 'finalSectionBottom', 'layout.spacing', resolvedLayoutSpacingDefaults.finalSectionBottom, sourceLabel),
361
+ firstSectionTop: readResponsiveCssLength(rawLayoutSpacingConfig, 'firstSectionTop', 'layout.spacing', resolvedLayoutSpacingDefaults.firstSectionTop, sourceLabel),
362
+ headingToBlock: readResponsiveCssLength(rawLayoutSpacingConfig, 'headingToBlock', 'layout.spacing', resolvedLayoutSpacingDefaults.headingToBlock, sourceLabel),
363
+ imageGap: readResponsiveCssLength(rawLayoutSpacingConfig, 'imageGap', 'layout.spacing', resolvedLayoutSpacingDefaults.imageGap, sourceLabel),
364
+ sectionGap: readResponsiveCssLength(rawLayoutSpacingConfig, 'sectionGap', 'layout.spacing', resolvedLayoutSpacingDefaults.sectionGap, sourceLabel),
365
+ }),
366
+ }),
367
+ gallery: Object.freeze({
368
+ maxAvailableHeightPercent: readResponsivePercent(rawGalleryConfig, 'maxAvailableHeightPercent', 'gallery', Object.freeze({
369
+ desktop: 74,
370
+ mobile: 68,
371
+ }), sourceLabel),
372
+ maxAvailableWidthPercent: readResponsivePercent(rawGalleryConfig, 'maxAvailableWidthPercent', 'gallery', Object.freeze({
373
+ desktop: 100,
374
+ mobile: 100,
375
+ }), sourceLabel),
376
+ width: readCssLength(rawGalleryConfig, 'width', 'gallery', '900px', sourceLabel),
377
+ }),
378
+ typography: Object.freeze({
379
+ fontFamily: readFontFamily(rawTypographyConfig, 'fontFamily', 'typography', defaultFontFamily, sourceLabel),
380
+ }),
381
+ });
382
+ };
388
383
 
389
384
  export const projectConfig = Object.freeze({
390
385
  site: Object.freeze({
391
386
  basePath: readBasePath(rawSite, 'basePath', 'site'),
392
387
  url: readUrl(rawSite, 'url', 'site'),
393
388
  }),
394
- layout: Object.freeze({
395
- density: layoutDensity,
396
- gutter: readResponsiveCssLength(rawLayout, 'gutter', 'layout', Object.freeze({
397
- desktop: 'clamp(1.25rem, 4vw, 3rem)',
398
- mobile: '1rem',
399
- }), siteThemeLabel),
400
- pageWidth: readCssLength(rawLayout, 'pageWidth', 'layout', '1180px', siteThemeLabel),
401
- spacing: Object.freeze({
402
- bodyToImages: readResponsiveCssLength(rawLayoutSpacing, 'bodyToImages', 'layout.spacing', layoutSpacingDefaults.bodyToImages, siteThemeLabel),
403
- finalSectionBottom: readResponsiveCssLength(rawLayoutSpacing, 'finalSectionBottom', 'layout.spacing', layoutSpacingDefaults.finalSectionBottom, siteThemeLabel),
404
- firstSectionTop: readResponsiveCssLength(rawLayoutSpacing, 'firstSectionTop', 'layout.spacing', layoutSpacingDefaults.firstSectionTop, siteThemeLabel),
405
- imageGap: readResponsiveCssLength(rawLayoutSpacing, 'imageGap', 'layout.spacing', layoutSpacingDefaults.imageGap, siteThemeLabel),
406
- sectionGap: readResponsiveCssLength(rawLayoutSpacing, 'sectionGap', 'layout.spacing', layoutSpacingDefaults.sectionGap, siteThemeLabel),
407
- }),
408
- }),
409
- gallery: Object.freeze({
410
- maxAvailableHeightPercent: readResponsivePercent(rawGallery, 'maxAvailableHeightPercent', 'gallery', Object.freeze({
411
- desktop: 74,
412
- mobile: 68,
413
- }), siteThemeLabel),
414
- maxAvailableWidthPercent: readResponsivePercent(rawGallery, 'maxAvailableWidthPercent', 'gallery', Object.freeze({
415
- desktop: 100,
416
- mobile: 100,
417
- }), siteThemeLabel),
418
- width: readCssLength(rawGallery, 'width', 'gallery', '900px', siteThemeLabel),
419
- }),
420
- typography: Object.freeze({
421
- fontFamily: readFontFamily(rawTypography, 'fontFamily', 'typography', defaultFontFamily, siteThemeLabel),
422
- }),
389
+ ...resolveThemeVisualConfig(rawTheme, siteThemeLabel),
423
390
  navigation: Object.freeze({
424
391
  smoothScroll: readSmoothScroll(rawNavigation),
425
392
  }),
426
393
  locale: readLocale(rawLocale),
427
- footer: Object.freeze({
428
- buildInfo: readBuildInfo(rawFooter),
429
- copyrightMessage: readOptionalString(rawFooter, 'copyrightMessage', 'footer'),
430
- }),
431
394
  github: Object.freeze({
432
395
  repo: readString(rawGithub, 'repo', 'github'),
433
396
  branch: readString(rawGithub, 'branch', 'github'),
@@ -18,24 +18,20 @@ export const supportedImageExtensions = new Set([...rasterImageExtensions, ...st
18
18
 
19
19
  const h2Regex = /^##\s+.*$/gm;
20
20
  const explicitHeadingIdRegex = /\s*\{#([a-z0-9-]+)\}\s*$/;
21
- const inlineStyleReferenceRegex = /\[[^\]\n]+\]\{\.([a-z][a-z0-9-]*)\}/g;
21
+ const deprecatedInlineStyleReferenceRegex = /\[[^\]\n]+\]\{\.([a-z][a-z0-9-]*)\}/g;
22
22
  const frontmatterDelimiterRegex = /^---\s*$/;
23
- const knownContentTopLevelFrontmatterKeys = new Set(['title', 'description', 'navigation', 'presentation', 'frame', 'sections']);
24
- const knownThemeTopLevelFrontmatterKeys = new Set(['navigation', 'layout', 'gallery', 'typography', 'presentation', 'frame']);
25
- const frontmatterColorKeys = new Set(['backgroundColor', 'textColor', 'color']);
26
- const quotedHexColorRegex = /^["']#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})["']$/;
27
- const unquotedHexColorRegex = /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;
23
+ const knownContentTopLevelFrontmatterKeys = new Set(['title', 'description', 'navigation', 'sections']);
24
+ const knownThemeTopLevelFrontmatterKeys = new Set(['navigation', 'preset', 'layout', 'gallery', 'typography', 'presentation']);
25
+ const knownRouteThemeTopLevelFrontmatterKeys = new Set(['navigation', 'preset', 'layout', 'gallery', 'typography', 'presentation']);
26
+ const knownSitewideTopLevelFrontmatterKeys = new Set(['navigation', 'banners', 'footer']);
28
27
  const knownNestedFrontmatterKeys = new Set([
29
28
  'align',
30
29
  'alt',
31
- 'backgroundColor',
32
30
  'body',
33
- 'bodyToImages',
31
+ 'blockGap',
34
32
  'brand',
35
33
  'caption',
36
34
  'carousel',
37
- 'color',
38
- 'colors',
39
35
  'density',
40
36
  'desktop',
41
37
  'fontFamily',
@@ -43,6 +39,7 @@ const knownNestedFrontmatterKeys = new Set([
43
39
  'finalSectionBottom',
44
40
  'gallery',
45
41
  'gutter',
42
+ 'headingToBlock',
46
43
  'h1',
47
44
  'h2',
48
45
  'h3',
@@ -53,8 +50,8 @@ const knownNestedFrontmatterKeys = new Set([
53
50
  'include',
54
51
  'image',
55
52
  'imageGap',
56
- 'inlineStyles',
57
53
  'label',
54
+ 'logo',
58
55
  'lineHeight',
59
56
  'maxAvailableHeightPercent',
60
57
  'maxAvailableWidthPercent',
@@ -63,15 +60,18 @@ const knownNestedFrontmatterKeys = new Set([
63
60
  'overrides',
64
61
  'paragraphSpacing',
65
62
  'pageWidth',
63
+ 'palette',
66
64
  'preset',
67
65
  'presentation',
68
66
  'rhythm',
67
+ 'sequence',
69
68
  'sections',
70
69
  'sectionGap',
70
+ 'sectionSurfaces',
71
71
  'size',
72
72
  'spacingAfter',
73
73
  'spacingBefore',
74
- 'textColor',
74
+ 'surface',
75
75
  'theme',
76
76
  'typography',
77
77
  'until',
@@ -264,79 +264,6 @@ export const validateFrontmatterStructure = (frontmatter, addIssue, {
264
264
  }
265
265
  };
266
266
 
267
- const stripInlineYamlComment = (value) => {
268
- let quote = null;
269
-
270
- for (let index = 0; index < value.length; index += 1) {
271
- const character = value[index];
272
- const previous = value[index - 1];
273
-
274
- if ((character === '"' || character === "'") && previous !== '\\') {
275
- quote = quote === character ? null : quote ?? character;
276
- continue;
277
- }
278
-
279
- if (character === '#' && quote === null && /\s/.test(previous ?? '')) {
280
- return value.slice(0, index).trimEnd();
281
- }
282
- }
283
-
284
- return value.trim();
285
- };
286
-
287
- export const validateFrontmatterColorValues = (frontmatter, addIssue, label = 'Frontmatter') => {
288
- const lines = frontmatter.split(/\r?\n/);
289
- const pathStack = [];
290
-
291
- for (const [index, line] of lines.entries()) {
292
- const lineNumber = index + 1;
293
- const trimmed = line.trim();
294
-
295
- if (!trimmed || trimmed.startsWith('#') || frontmatterDelimiterRegex.test(line)) {
296
- continue;
297
- }
298
-
299
- const match = line.match(/^(\s*)([A-Za-z][A-Za-z0-9-]*):\s*(.*?)\s*$/);
300
- if (!match) continue;
301
-
302
- const indent = match[1].length;
303
- const key = match[2];
304
- const rawValue = match[3].trim();
305
-
306
- while (pathStack.length > 0 && pathStack[pathStack.length - 1].indent >= indent) {
307
- pathStack.pop();
308
- }
309
-
310
- const pathLabel = [...pathStack.map((entry) => entry.key), key].join('.');
311
-
312
- if (!frontmatterColorKeys.has(key)) {
313
- if (!rawValue) {
314
- pathStack.push({ indent, key });
315
- }
316
- continue;
317
- }
318
-
319
- const value = stripInlineYamlComment(rawValue);
320
-
321
- if (quotedHexColorRegex.test(value)) continue;
322
-
323
- if (unquotedHexColorRegex.test(rawValue)) {
324
- addIssue({
325
- severity: 'error',
326
- message: `${label} line ${lineNumber} defines ${pathLabel} as an unquoted hex color. YAML treats "#" as the start of a comment unless the value is quoted.`,
327
- fix: `Write it as ${key}: "#ffd84d" or another quoted hex color.`,
328
- });
329
- continue;
330
- }
331
-
332
- addIssue({
333
- severity: 'error',
334
- message: `${label} line ${lineNumber} defines ${pathLabel} with invalid color value ${rawValue || '(empty)'}.`,
335
- fix: `Use a quoted hex color in #rgb, #rrggbb, or #rrggbbaa form, for example ${key}: "#ffd84d".`,
336
- });
337
- }
338
- };
339
-
340
267
  export const validateContentFrontmatterStructure = (frontmatter, addIssue) =>
341
268
  validateFrontmatterStructure(frontmatter, addIssue, {
342
269
  knownTopLevelFrontmatterKeys: knownContentTopLevelFrontmatterKeys,
@@ -349,6 +276,18 @@ export const validateThemeFrontmatterStructure = (frontmatter, addIssue) =>
349
276
  fileKind: 'theme',
350
277
  });
351
278
 
279
+ export const validateRouteThemeFrontmatterStructure = (frontmatter, addIssue) =>
280
+ validateFrontmatterStructure(frontmatter, addIssue, {
281
+ knownTopLevelFrontmatterKeys: knownRouteThemeTopLevelFrontmatterKeys,
282
+ fileKind: 'route theme',
283
+ });
284
+
285
+ export const validateSitewideFrontmatterStructure = (frontmatter, addIssue) =>
286
+ validateFrontmatterStructure(frontmatter, addIssue, {
287
+ knownTopLevelFrontmatterKeys: knownSitewideTopLevelFrontmatterKeys,
288
+ fileKind: 'sitewide content',
289
+ });
290
+
352
291
  export const readThemeFile = async (sitePath) => readSiteFile(sitePath, siteThemeLabel);
353
292
 
354
293
  export const getFrontmatterSections = (frontmatter) => {
@@ -374,42 +313,11 @@ export const getFrontmatterSections = (frontmatter) => {
374
313
  return sections;
375
314
  };
376
315
 
377
- export const getFrontmatterInlineStyleNames = (frontmatter) => {
378
- const names = new Set();
379
- const lines = frontmatter.split(/\r?\n/);
380
- let inlineStylesIndent = null;
381
-
382
- for (const line of lines) {
383
- const inlineStylesMatch = line.match(/^(\s*)inlineStyles:\s*$/);
384
-
385
- if (inlineStylesMatch) {
386
- inlineStylesIndent = inlineStylesMatch[1].length;
387
- continue;
388
- }
389
-
390
- if (inlineStylesIndent === null) continue;
391
- if (!line.trim() || line.trim().startsWith('#')) continue;
392
-
393
- const indent = line.match(/^\s*/)?.[0].length ?? 0;
394
- if (indent <= inlineStylesIndent) {
395
- inlineStylesIndent = null;
396
- continue;
397
- }
398
-
399
- const nameMatch = line.match(new RegExp(`^\\s{${inlineStylesIndent + 2}}([a-z][a-z0-9-]*):\\s*$`));
400
- if (nameMatch) {
401
- names.add(nameMatch[1]);
402
- }
403
- }
404
-
405
- return names;
406
- };
316
+ export const getHeadingId = (heading) => heading.match(explicitHeadingIdRegex)?.[1];
407
317
 
408
- export const getInlineStyleReferences = (body) => Array.from(body.matchAll(inlineStyleReferenceRegex))
318
+ export const getDeprecatedInlineStyleReferences = (body) => Array.from(body.matchAll(deprecatedInlineStyleReferenceRegex))
409
319
  .map((match) => match[1]);
410
320
 
411
- export const getHeadingId = (heading) => heading.match(explicitHeadingIdRegex)?.[1];
412
-
413
321
  export const getBodySections = (body) => {
414
322
  const matches = Array.from(body.matchAll(h2Regex));
415
323
  const prelude = matches.length > 0 ? body.slice(0, matches[0].index) : body;
@@ -117,6 +117,7 @@ export const siteDir = resolvedSitePaths.siteDir;
117
117
  export const siteConfigPath = path.join(siteDir, 'config.mjs');
118
118
  export const siteThemePath = path.join(siteDir, 'theme.md');
119
119
  export const siteContentPath = path.join(siteDir, 'content.md');
120
+ export const sitewideContentPath = path.join(siteDir, 'sitewide-content.md');
120
121
  export const siteImagesDir = path.join(siteDir, 'images');
121
122
  export const siteRoutesDir = path.join(siteDir, 'routes');
122
123
  export const sitePublicDir = path.join(siteDir, 'public');
@@ -135,6 +136,7 @@ export const siteDirLabel = getPathLabel(siteDir);
135
136
  export const siteConfigLabel = getPathLabel(siteConfigPath);
136
137
  export const siteThemeLabel = getPathLabel(siteThemePath);
137
138
  export const siteContentLabel = getPathLabel(siteContentPath);
139
+ export const sitewideContentLabel = getPathLabel(sitewideContentPath);
138
140
  export const siteImagesLabel = getPathLabel(siteImagesDir);
139
141
  export const siteRoutesLabel = getPathLabel(siteRoutesDir);
140
142
  export const sitePublicLabel = getPathLabel(sitePublicDir);