@janga/norna 0.7.8 → 0.7.10
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.
- package/README.md +1 -1
- package/docs/README.md +18 -11
- package/docs/configuration.md +9 -54
- package/docs/content.md +6 -7
- package/docs/design/norna-diagram-design.md +158 -0
- package/docs/getting-started.md +18 -16
- package/docs/routes.md +7 -6
- package/docs/site-structure.md +4 -2
- package/docs/sitewide-content.md +74 -0
- package/docs/theme.md +61 -128
- package/docs/typography.md +45 -33
- package/examples/001-dog-shelter-single-page/README.md +9 -0
- package/examples/001-dog-shelter-single-page/site/config.mjs +27 -0
- package/examples/001-dog-shelter-single-page/site/content.md +38 -0
- package/examples/001-dog-shelter-single-page/site/images/what-we-do/dog-house.svg +25 -0
- package/examples/001-dog-shelter-single-page/site/images/you-can-help/heart.svg +13 -0
- package/examples/001-dog-shelter-single-page/site/public/favicon.svg +10 -0
- package/examples/001-dog-shelter-single-page/site/public/robots.txt +2 -0
- package/examples/001-dog-shelter-single-page/site/sitewide-content.md +6 -0
- package/examples/001-dog-shelter-single-page/site/theme.md +25 -0
- package/examples/002-dog-shelter-multi-page/README.md +9 -0
- package/examples/002-dog-shelter-multi-page/site/config.mjs +27 -0
- package/examples/002-dog-shelter-multi-page/site/content.md +12 -0
- package/examples/002-dog-shelter-multi-page/site/public/favicon.svg +10 -0
- package/examples/002-dog-shelter-multi-page/site/public/robots.txt +2 -0
- package/examples/002-dog-shelter-multi-page/site/routes/010-dogs/images/dogs/rex.svg +19 -0
- package/examples/002-dog-shelter-multi-page/site/routes/010-dogs/images/dogs/rover.svg +19 -0
- package/examples/002-dog-shelter-multi-page/site/routes/010-dogs/route-content.md +30 -0
- package/examples/002-dog-shelter-multi-page/site/routes/020-adopt/route-content.md +12 -0
- package/examples/002-dog-shelter-multi-page/site/sitewide-content.md +6 -0
- package/examples/002-dog-shelter-multi-page/site/theme.md +25 -0
- package/examples/dog-gallery/.astro/collections/site.schema.json +776 -0
- package/examples/dog-gallery/.astro/collections/theme.schema.json +672 -0
- package/examples/dog-gallery/.astro/content-assets.mjs +4 -0
- package/examples/dog-gallery/.astro/content-modules.mjs +1 -0
- package/examples/dog-gallery/.astro/content.d.ts +175 -0
- package/examples/dog-gallery/.astro/data-store.json +1 -0
- package/examples/dog-gallery/.astro/dev-local.json +8 -0
- package/examples/dog-gallery/.astro/dev.json +13 -0
- package/examples/dog-gallery/.astro/dev.log +95 -0
- package/examples/dog-gallery/.astro/settings.json +5 -0
- package/examples/dog-gallery/.astro/types.d.ts +2 -0
- package/examples/dog-gallery/dist/_astro/GalleryGrid.astro_astro_type_script_index_0_lang.B0kxDyGz.js +1 -0
- package/examples/dog-gallery/dist/_astro/SitePage.DicGh_5N.css +1 -0
- package/examples/dog-gallery/dist/dog-care/index.html +15 -0
- package/examples/dog-gallery/dist/dog-shelter-logo.svg +14 -0
- package/examples/dog-gallery/dist/favicon.svg +7 -0
- package/examples/dog-gallery/dist/images/generated/black-dogs/black-puppy-meadow-7255e3d9-1080.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/black-dogs/black-puppy-meadow-7255e3d9-1440.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/black-dogs/black-puppy-meadow-7255e3d9-1536.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/black-dogs/black-puppy-meadow-7255e3d9-480.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/black-dogs/black-puppy-meadow-7255e3d9-768.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/black-dogs/photo-of-a-black-dog-5cf614d2-480.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/black-dogs/photo-of-a-black-dog-5cf614d2-768.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/black-dogs/photo-of-a-black-dog-5cf614d2-960.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/brown-dogs/brown-dog-50ef114e-1080.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/brown-dogs/brown-dog-50ef114e-1440.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/brown-dogs/brown-dog-50ef114e-1920.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/brown-dogs/brown-dog-50ef114e-3939.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/brown-dogs/brown-dog-50ef114e-480.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/brown-dogs/brown-dog-50ef114e-768.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/brown-dogs/dog-accompanies-master-7ef804d5-1080.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/brown-dogs/dog-accompanies-master-7ef804d5-1440.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/brown-dogs/dog-accompanies-master-7ef804d5-1920.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/brown-dogs/dog-accompanies-master-7ef804d5-2400.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/brown-dogs/dog-accompanies-master-7ef804d5-480.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/brown-dogs/dog-accompanies-master-7ef804d5-768.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/golden-dogs/golden-retriever-f797fd88-1080.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/golden-dogs/golden-retriever-f797fd88-1440.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/golden-dogs/golden-retriever-f797fd88-1600.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/golden-dogs/golden-retriever-f797fd88-480.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/golden-dogs/golden-retriever-f797fd88-768.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/golden-dogs/toller-puppy-a2a8fbc0-1080.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/golden-dogs/toller-puppy-a2a8fbc0-1440.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/golden-dogs/toller-puppy-a2a8fbc0-1920.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/golden-dogs/toller-puppy-a2a8fbc0-2576.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/golden-dogs/toller-puppy-a2a8fbc0-480.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/golden-dogs/toller-puppy-a2a8fbc0-768.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/white-dogs/white-cute-dog-077a0689-1080.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/white-dogs/white-cute-dog-077a0689-1277.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/white-dogs/white-cute-dog-077a0689-480.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/white-dogs/white-cute-dog-077a0689-768.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/white-dogs/white-puppy-garden-d3ab2ca0-1080.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/white-dogs/white-puppy-garden-d3ab2ca0-1440.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/white-dogs/white-puppy-garden-d3ab2ca0-1536.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/white-dogs/white-puppy-garden-d3ab2ca0-480.webp +0 -0
- package/examples/dog-gallery/dist/images/generated/white-dogs/white-puppy-garden-d3ab2ca0-768.webp +0 -0
- package/examples/dog-gallery/dist/index.html +45 -0
- package/examples/dog-gallery/dist/robots.txt +2 -0
- package/examples/dog-gallery/node_modules/.vite/deps/_metadata.json +7 -7
- package/examples/dog-gallery/site/.norna/generated-images.json +8 -8
- package/examples/dog-gallery/site/config.mjs +0 -20
- package/examples/dog-gallery/site/content.md +4 -5
- package/examples/dog-gallery/site/public/logo.svg +14 -0
- package/examples/dog-gallery/site/sitewide-content.md +16 -0
- package/examples/dog-gallery/site/theme.md +13 -24
- package/examples/presentation/README.md +15 -0
- package/examples/presentation/site/.norna/generated-images.json +66 -0
- package/examples/presentation/site/config.mjs +29 -0
- package/examples/presentation/site/content.md +67 -0
- package/examples/presentation/site/public/robots.txt +2 -0
- package/examples/presentation/site/routes/010-presets/route-content.md +63 -0
- package/examples/presentation/site/routes/010-presets/theme.md +4 -0
- package/examples/presentation/site/routes/020-media/images/cards/card-carousel.svg +11 -0
- package/examples/presentation/site/routes/020-media/images/cards/card-stack.svg +9 -0
- package/examples/presentation/site/routes/020-media/images/cards/card-surfaces.svg +8 -0
- package/examples/presentation/site/routes/020-media/images/carousel/carousel-one.svg +8 -0
- package/examples/presentation/site/routes/020-media/images/carousel/carousel-three.svg +13 -0
- package/examples/presentation/site/routes/020-media/images/carousel/carousel-two.svg +8 -0
- package/examples/presentation/site/routes/020-media/images/stack/stack-one.svg +8 -0
- package/examples/presentation/site/routes/020-media/images/stack/stack-two.svg +9 -0
- package/examples/presentation/site/routes/020-media/route-content.md +77 -0
- package/examples/presentation/site/routes/020-media/theme.md +4 -0
- package/examples/presentation/site/routes/030-surfaces/route-content.md +44 -0
- package/examples/presentation/site/routes/030-surfaces/theme.md +7 -0
- package/examples/presentation/site/sitewide-content.md +6 -0
- package/examples/presentation/site/theme.md +43 -0
- package/examples/typography-presets/.astro/collections/routeTheme.schema.json +746 -0
- package/examples/typography-presets/.astro/collections/site.schema.json +59 -0
- package/examples/typography-presets/.astro/collections/sitewide.schema.json +114 -0
- package/examples/typography-presets/.astro/collections/theme.schema.json +770 -0
- package/examples/typography-presets/.astro/content-assets.mjs +4 -0
- package/examples/typography-presets/.astro/content-modules.mjs +1 -0
- package/examples/typography-presets/.astro/content.d.ts +183 -0
- package/examples/typography-presets/.astro/data-store.json +1 -0
- package/examples/typography-presets/.astro/dev-local.json +8 -0
- package/examples/typography-presets/.astro/dev.json +15 -0
- package/examples/typography-presets/.astro/dev.log +45 -0
- package/examples/typography-presets/.astro/settings.json +5 -0
- package/examples/typography-presets/.astro/types.d.ts +2 -0
- package/examples/typography-presets/node_modules/.vite/deps/_metadata.json +80 -0
- package/examples/typography-presets/node_modules/.vite/deps/astro-D_GIpJIE.js +434 -0
- package/examples/typography-presets/node_modules/.vite/deps/astro-D_GIpJIE.js.map +1 -0
- package/examples/typography-presets/node_modules/.vite/deps/astro_n_aria-query.js +6390 -0
- package/examples/typography-presets/node_modules/.vite/deps/astro_n_aria-query.js.map +1 -0
- package/examples/typography-presets/node_modules/.vite/deps/astro_n_axobject-query.js +2702 -0
- package/examples/typography-presets/node_modules/.vite/deps/astro_n_axobject-query.js.map +1 -0
- package/examples/typography-presets/node_modules/.vite/deps/astro_n_html-escaper.js +65 -0
- package/examples/typography-presets/node_modules/.vite/deps/astro_n_html-escaper.js.map +1 -0
- package/examples/typography-presets/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +455 -0
- package/examples/typography-presets/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +1 -0
- package/examples/typography-presets/node_modules/.vite/deps/audit-DM9Hkl5B.js +1534 -0
- package/examples/typography-presets/node_modules/.vite/deps/audit-DM9Hkl5B.js.map +1 -0
- package/examples/typography-presets/node_modules/.vite/deps/embla-carousel.js +1389 -0
- package/examples/typography-presets/node_modules/.vite/deps/embla-carousel.js.map +1 -0
- package/examples/typography-presets/node_modules/.vite/deps/highlight-DnZ1ULOL.js +48 -0
- package/examples/typography-presets/node_modules/.vite/deps/highlight-DnZ1ULOL.js.map +1 -0
- package/examples/typography-presets/node_modules/.vite/deps/icons-RyebKi45.js +42 -0
- package/examples/typography-presets/node_modules/.vite/deps/icons-RyebKi45.js.map +1 -0
- package/examples/typography-presets/node_modules/.vite/deps/package.json +3 -0
- package/examples/typography-presets/node_modules/.vite/deps/rolldown-runtime-BvCyGRYZ.js +4 -0
- package/examples/typography-presets/node_modules/.vite/deps/settings-DaU-gNnK.js +190 -0
- package/examples/typography-presets/node_modules/.vite/deps/settings-DaU-gNnK.js.map +1 -0
- package/examples/typography-presets/node_modules/.vite/deps/toolbar-63PA619H.js +458 -0
- package/examples/typography-presets/node_modules/.vite/deps/toolbar-63PA619H.js.map +1 -0
- package/examples/typography-presets/node_modules/.vite/deps/ui-library--bkcNSYz.js +1016 -0
- package/examples/typography-presets/node_modules/.vite/deps/ui-library--bkcNSYz.js.map +1 -0
- package/examples/typography-presets/node_modules/.vite/deps/window-jPkpKVcy.js +32 -0
- package/examples/typography-presets/node_modules/.vite/deps/window-jPkpKVcy.js.map +1 -0
- package/examples/typography-presets/node_modules/.vite/deps/xray-BXCHpEJP.js +111 -0
- package/examples/typography-presets/node_modules/.vite/deps/xray-BXCHpEJP.js.map +1 -0
- package/examples/typography-presets/site/.norna/generated-images.json +290 -0
- package/examples/typography-presets/site/config.mjs +29 -0
- package/examples/typography-presets/site/content.md +16 -0
- package/examples/typography-presets/site/public/robots.txt +2 -0
- package/examples/typography-presets/site/routes/010-quiet-gallery/images/quiet-gallery/puppy-portrait.jpg +0 -0
- package/examples/typography-presets/site/routes/010-quiet-gallery/images/quiet-gallery/shepherd-portrait.jpg +0 -0
- package/examples/typography-presets/site/routes/010-quiet-gallery/images/quiet-gallery/white-puppy.jpg +0 -0
- package/examples/typography-presets/site/routes/010-quiet-gallery/route-content.md +34 -0
- package/examples/typography-presets/site/routes/010-quiet-gallery/theme.md +4 -0
- package/examples/typography-presets/site/routes/020-compact-gallery/images/compact-gallery/black-schnauzer.jpg +0 -0
- package/examples/typography-presets/site/routes/020-compact-gallery/images/compact-gallery/retriever-portrait.jpg +0 -0
- package/examples/typography-presets/site/routes/020-compact-gallery/images/compact-gallery/schnauzer-portrait.jpg +0 -0
- package/examples/typography-presets/site/routes/020-compact-gallery/route-content.md +33 -0
- package/examples/typography-presets/site/routes/020-compact-gallery/theme.md +4 -0
- package/examples/typography-presets/site/routes/030-text-forward/images/text-forward/boxer-portrait.jpg +0 -0
- package/examples/typography-presets/site/routes/030-text-forward/images/text-forward/grass-puppy.jpg +0 -0
- package/examples/typography-presets/site/routes/030-text-forward/images/text-forward/grey-street-dog.jpg +0 -0
- package/examples/typography-presets/site/routes/030-text-forward/route-content.md +34 -0
- package/examples/typography-presets/site/routes/030-text-forward/theme.md +4 -0
- package/examples/typography-presets/site/routes/040-statement/images/statement/black-pedigree.jpg +0 -0
- package/examples/typography-presets/site/routes/040-statement/images/statement/sheepdog-grass.jpg +0 -0
- package/examples/typography-presets/site/routes/040-statement/images/statement/sheepdog-wide.jpg +0 -0
- package/examples/typography-presets/site/routes/040-statement/route-content.md +34 -0
- package/examples/typography-presets/site/routes/040-statement/theme.md +4 -0
- package/examples/typography-presets/site/sitewide-content.md +14 -0
- package/examples/typography-presets/site/theme.md +43 -0
- package/fixtures/basic/site/config.mjs +0 -13
- package/fixtures/basic/site/sitewide-content.md +12 -0
- package/fixtures/basic/site/theme.md +2 -2
- package/fixtures/content-model-v2/site/content.md +2 -5
- package/fixtures/content-model-v2/site/theme.md +1 -3
- package/package.json +3 -2
- package/scripts/check-config.mjs +22 -1
- package/scripts/lib/frontmatter-yaml.mjs +28 -0
- package/scripts/lib/logo-assets.mjs +35 -0
- package/scripts/lib/norna-markdown-blocks.mjs +166 -0
- package/scripts/lib/presentation.mjs +104 -46
- package/scripts/lib/project-config.mjs +67 -105
- package/scripts/lib/site-content.mjs +26 -42
- package/scripts/lib/site-paths.mjs +2 -0
- package/scripts/lib/sitewide-config.mjs +34 -0
- package/scripts/lib/theme-config.mjs +72 -6
- package/scripts/lib/typography.mjs +20 -26
- package/scripts/show-typography.mjs +40 -147
- package/scripts/sync-content-sections.mjs +19 -14
- package/scripts/test-content-check.mjs +76 -14
- package/scripts/test-content-model-v2.mjs +154 -0
- package/scripts/test-engine-commands.mjs +3 -1
- package/scripts/test-package-check.mjs +78 -19
- package/scripts/test-temporary-visibility.mjs +19 -2
- package/src/components/CardList.astro +5 -2
- package/src/components/GalleryGrid.astro +37 -16
- package/src/components/SiteBanners.astro +100 -0
- package/src/components/SiteNavigation.astro +48 -31
- package/src/components/SitePage.astro +55 -13
- package/src/components/SiteSection.astro +194 -36
- package/src/content.config.ts +78 -48
- package/src/layouts/BaseLayout.astro +64 -14
- package/src/lib/routeThemes.ts +61 -0
- package/src/lib/sectionContent.ts +236 -44
- package/src/lib/sitePublicAssets.ts +1 -0
- package/src/styles/global.css +282 -93
- package/starters/basic/.github/workflows/deploy.yml +1 -1
- package/starters/basic/README.md +7 -6
- package/starters/basic/site/config.mjs +0 -13
- package/starters/basic/site/content.md +1 -1
- package/starters/basic/site/sitewide-content.md +14 -0
- package/starters/basic/site/theme.md +13 -24
- package/starters/project/.github/workflows/deploy.yml +1 -1
- package/starters/project/README.md +10 -8
- package/starters/project/site/config.mjs +0 -13
- package/starters/project/site/sitewide-content.md +14 -0
- package/starters/project/site/theme.md +3 -11
|
@@ -45,8 +45,8 @@ const makePngHeader = ({ width, height }) => {
|
|
|
45
45
|
const defaultTheme = `---
|
|
46
46
|
typography:
|
|
47
47
|
preset: quiet-gallery
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
presentation:
|
|
49
|
+
palette: dark
|
|
50
50
|
---
|
|
51
51
|
`;
|
|
52
52
|
|
|
@@ -173,14 +173,6 @@ test('content:check warns when carousel images use different aspect ratios', asy
|
|
|
173
173
|
});
|
|
174
174
|
});
|
|
175
175
|
|
|
176
|
-
const inlineStyleTheme = `---
|
|
177
|
-
presentation:
|
|
178
|
-
inlineStyles:
|
|
179
|
-
highlight:
|
|
180
|
-
color: "#ffd84d"
|
|
181
|
-
---
|
|
182
|
-
`;
|
|
183
|
-
|
|
184
176
|
const inlineStyleSite = `---
|
|
185
177
|
title: Inline Style
|
|
186
178
|
---
|
|
@@ -188,10 +180,9 @@ title: Inline Style
|
|
|
188
180
|
This has [known text]{.highlight} and [unknown text]{.missing}.
|
|
189
181
|
`;
|
|
190
182
|
|
|
191
|
-
test('content:check fails when Markdown uses
|
|
183
|
+
test('content:check fails when Markdown uses removed inline color styles', async () => {
|
|
192
184
|
await withTempProject({
|
|
193
185
|
site: inlineStyleSite,
|
|
194
|
-
theme: inlineStyleTheme,
|
|
195
186
|
files: [],
|
|
196
187
|
}, async (root) => {
|
|
197
188
|
const result = runContentScript(root, ['--check']);
|
|
@@ -199,8 +190,79 @@ test('content:check fails when Markdown uses undefined inline styles', async ()
|
|
|
199
190
|
|
|
200
191
|
assert.equal(result.status, 1, output);
|
|
201
192
|
assert.match(output, /^Content check failed\./m);
|
|
202
|
-
assert.match(output, /Inline style "\.
|
|
203
|
-
assert.
|
|
193
|
+
assert.match(output, /Inline color style "\.highlight" is no longer supported in site\/content\.md\./);
|
|
194
|
+
assert.match(output, /Inline color style "\.missing" is no longer supported in site\/content\.md\./);
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
test('content:check suggests semantic Markdown instead of inline color styles', async () => {
|
|
199
|
+
await withTempProject({
|
|
200
|
+
site: `---
|
|
201
|
+
title: Invalid Inline Color
|
|
202
|
+
---
|
|
203
|
+
## Intro {#intro}
|
|
204
|
+
This has [yellow text]{.yellow}.
|
|
205
|
+
`,
|
|
206
|
+
files: [],
|
|
207
|
+
}, async (root) => {
|
|
208
|
+
const result = runContentScript(root, ['--check']);
|
|
209
|
+
const output = getOutput(result);
|
|
210
|
+
|
|
211
|
+
assert.equal(result.status, 1, output);
|
|
212
|
+
assert.match(output, /^Content check failed\./m);
|
|
213
|
+
assert.match(output, /Use standard Markdown emphasis, a blockquote, or a semantic Norna block instead of color syntax\./);
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
const inlineNoteSite = `---
|
|
218
|
+
title: Inline notes
|
|
219
|
+
description: Fixture
|
|
220
|
+
---
|
|
221
|
+
## Intro {#intro}
|
|
222
|
+
This paragraph has a note reference.{note-ref}
|
|
223
|
+
|
|
224
|
+
{note: Additional context.}
|
|
225
|
+
|
|
226
|
+
\`\`\`text
|
|
227
|
+
{note-ref}
|
|
228
|
+
{note: This is code, not a note.
|
|
229
|
+
\`\`\`
|
|
230
|
+
`;
|
|
231
|
+
|
|
232
|
+
test('content:check accepts valid inline notes and ignores note syntax in code fences', async () => {
|
|
233
|
+
await withTempProject({ site: inlineNoteSite, files: [] }, async (root) => {
|
|
234
|
+
const result = runContentScript(root, ['--check']);
|
|
235
|
+
const output = getOutput(result);
|
|
236
|
+
|
|
237
|
+
assert.equal(result.status, 0, output);
|
|
238
|
+
assert.match(output, /Content check passed\./);
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
test('content:check rejects unpaired and repeated inline notes', async () => {
|
|
243
|
+
await withTempProject({
|
|
244
|
+
site: `---
|
|
245
|
+
title: Invalid inline notes
|
|
246
|
+
description: Fixture
|
|
247
|
+
---
|
|
248
|
+
## Orphan {#orphan}
|
|
249
|
+
{note: This note has no reference.}
|
|
250
|
+
|
|
251
|
+
## Missing {#missing}
|
|
252
|
+
This reference has no note.{note-ref}
|
|
253
|
+
|
|
254
|
+
## Repeated {#repeated}
|
|
255
|
+
This paragraph has {note-ref} two references {note-ref}.
|
|
256
|
+
`,
|
|
257
|
+
files: [],
|
|
258
|
+
}, async (root) => {
|
|
259
|
+
const result = runContentScript(root, ['--check']);
|
|
260
|
+
const output = getOutput(result);
|
|
261
|
+
|
|
262
|
+
assert.equal(result.status, 1, output);
|
|
263
|
+
assert.match(output, /A "\{note: \.\.\.\}" requires a preceding paragraph containing "\{note-ref\}"\./);
|
|
264
|
+
assert.match(output, /contains "\{note-ref\}" but has no following "\{note: \.\.\.\}"/);
|
|
265
|
+
assert.match(output, /A paragraph may contain only one "\{note-ref\}"\./);
|
|
204
266
|
});
|
|
205
267
|
});
|
|
206
268
|
|
|
@@ -129,6 +129,99 @@ test('content model v2 fixture checks and builds', async () => {
|
|
|
129
129
|
}
|
|
130
130
|
});
|
|
131
131
|
|
|
132
|
+
test('route theme replaces visual theme while site identity stays at the root', async () => {
|
|
133
|
+
const { root, siteDir } = await createTempSite({ underRepoCache: true });
|
|
134
|
+
try {
|
|
135
|
+
await writeFile(path.join(siteDir, 'theme.md'), `---
|
|
136
|
+
typography:
|
|
137
|
+
preset: text-forward
|
|
138
|
+
---
|
|
139
|
+
`);
|
|
140
|
+
await writeFile(path.join(siteDir, 'sitewide-content.md'), `---
|
|
141
|
+
navigation:
|
|
142
|
+
brand: Root Brand
|
|
143
|
+
---
|
|
144
|
+
`);
|
|
145
|
+
await mkdir(path.join(siteDir, 'routes', '010-guide'), { recursive: true });
|
|
146
|
+
await writeFile(path.join(siteDir, 'content.md'), `---
|
|
147
|
+
title: Root
|
|
148
|
+
description: Root page
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Home {#home}
|
|
152
|
+
|
|
153
|
+
Root content.
|
|
154
|
+
`);
|
|
155
|
+
await writeFile(path.join(siteDir, 'routes', '010-guide', 'route-content.md'), `---
|
|
156
|
+
title: Guide
|
|
157
|
+
description: Guide page
|
|
158
|
+
navigation:
|
|
159
|
+
label: Guide
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Guide {#guide}
|
|
163
|
+
|
|
164
|
+
Route content.
|
|
165
|
+
`);
|
|
166
|
+
await writeFile(path.join(siteDir, 'routes', '010-guide', 'theme.md'), `---
|
|
167
|
+
typography:
|
|
168
|
+
preset: statement
|
|
169
|
+
presentation:
|
|
170
|
+
palette: light
|
|
171
|
+
---
|
|
172
|
+
`);
|
|
173
|
+
|
|
174
|
+
await runNorna(['--site-dir', siteDir, 'build']);
|
|
175
|
+
const rootHtml = await readFile(path.join(path.dirname(siteDir), 'dist', 'index.html'), 'utf8');
|
|
176
|
+
const routeHtml = await readFile(path.join(path.dirname(siteDir), 'dist', 'guide', 'index.html'), 'utf8');
|
|
177
|
+
assert.match(rootHtml, /--color-page: #000000/);
|
|
178
|
+
assert.match(routeHtml, /--color-page: #ffffff/);
|
|
179
|
+
assert.match(routeHtml, /<a class="site-brand" href="\/">Root Brand<\/a>/);
|
|
180
|
+
} finally {
|
|
181
|
+
await rm(root, { recursive: true, force: true });
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
test('route theme cannot define site identity', async () => {
|
|
186
|
+
const { root, siteDir } = await createTempSite({ underRepoCache: true });
|
|
187
|
+
try {
|
|
188
|
+
await mkdir(path.join(siteDir, 'routes', '010-guide'), { recursive: true });
|
|
189
|
+
await writeFile(path.join(siteDir, 'content.md'), `---
|
|
190
|
+
title: Root
|
|
191
|
+
description: Root page
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## Home {#home}
|
|
195
|
+
|
|
196
|
+
Root content.
|
|
197
|
+
`);
|
|
198
|
+
await writeFile(path.join(siteDir, 'routes', '010-guide', 'route-content.md'), `---
|
|
199
|
+
title: Guide
|
|
200
|
+
description: Guide page
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## Guide {#guide}
|
|
204
|
+
|
|
205
|
+
Route content.
|
|
206
|
+
`);
|
|
207
|
+
await writeFile(path.join(siteDir, 'routes', '010-guide', 'theme.md'), `---
|
|
208
|
+
navigation:
|
|
209
|
+
brand: Route Brand
|
|
210
|
+
---
|
|
211
|
+
`);
|
|
212
|
+
|
|
213
|
+
await assert.rejects(
|
|
214
|
+
runNorna(['--site-dir', siteDir, 'config:check']),
|
|
215
|
+
(error) => {
|
|
216
|
+
assert.match(error.output, /may not define navigation\. Brand and logo belong in site\/sitewide-content\.md/);
|
|
217
|
+
return true;
|
|
218
|
+
},
|
|
219
|
+
);
|
|
220
|
+
} finally {
|
|
221
|
+
await rm(root, { recursive: true, force: true });
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
|
|
132
225
|
test('content:check warns for local Markdown images but not external Markdown images', async () => {
|
|
133
226
|
const { root, siteDir } = await createTempSite();
|
|
134
227
|
try {
|
|
@@ -189,6 +282,40 @@ description: Fixture
|
|
|
189
282
|
}
|
|
190
283
|
});
|
|
191
284
|
|
|
285
|
+
test('inline notes render as linked numbered margin notes', async () => {
|
|
286
|
+
const { root, siteDir } = await createTempSite({ underRepoCache: true });
|
|
287
|
+
try {
|
|
288
|
+
await writeFile(path.join(siteDir, 'content.md'), `---
|
|
289
|
+
title: Inline notes
|
|
290
|
+
description: Fixture
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## Intro {#intro}
|
|
294
|
+
|
|
295
|
+
The first paragraph points to an explanation.{note-ref}
|
|
296
|
+
|
|
297
|
+
{note: This explanation names \`sitewide-content.md\` and stays beside the paragraph on wide screens.}
|
|
298
|
+
|
|
299
|
+
The second paragraph has its own explanation.{note-ref}
|
|
300
|
+
|
|
301
|
+
{note: This is the second explanation.}
|
|
302
|
+
`);
|
|
303
|
+
|
|
304
|
+
await runNorna(['--site-dir', siteDir, 'build']);
|
|
305
|
+
const html = await readFile(path.join(path.dirname(siteDir), 'dist', 'index.html'), 'utf8');
|
|
306
|
+
assert.match(html, /<sup class="section-note-ref"><a id="note-ref-home-intro-1" href="#note-home-intro-1" aria-label="Note 1" aria-describedby="note-home-intro-1" data-note-id="note-home-intro-1">1<\/a><\/sup>/);
|
|
307
|
+
assert.match(html, /<aside class="section-note section-note-margin" id="note-home-intro-1" aria-label="Note 1" role="note" data-note-id="note-home-intro-1" style="grid-row: 1;">/);
|
|
308
|
+
assert.match(html, /<a class="section-note-number" href="#note-ref-home-intro-1" aria-label="Note 1">\s*1\s*<\/a>/);
|
|
309
|
+
assert.match(html, /This explanation names <code>sitewide-content\.md<\/code> and stays beside the paragraph on wide screens\./);
|
|
310
|
+
assert.match(html, /<sup class="section-note-ref"><a id="note-ref-home-intro-2" href="#note-home-intro-2" aria-label="Note 2" aria-describedby="note-home-intro-2" data-note-id="note-home-intro-2">2<\/a><\/sup>/);
|
|
311
|
+
assert.match(html, /<aside class="section-note section-note-margin" id="note-home-intro-2" aria-label="Note 2" role="note" data-note-id="note-home-intro-2" style="grid-row: 2;">/);
|
|
312
|
+
assert.match(html, /This is the second explanation\./);
|
|
313
|
+
assert.doesNotMatch(html, /\{note:/);
|
|
314
|
+
} finally {
|
|
315
|
+
await rm(root, { recursive: true, force: true });
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
|
|
192
319
|
test('updated managed SVG images get updated static output', async () => {
|
|
193
320
|
const { root, siteDir } = await createTempSite();
|
|
194
321
|
try {
|
|
@@ -842,6 +969,33 @@ description: Fixture
|
|
|
842
969
|
}
|
|
843
970
|
});
|
|
844
971
|
|
|
972
|
+
test('content:check rejects the removed norna-note block', async () => {
|
|
973
|
+
const { root, siteDir } = await createTempSite();
|
|
974
|
+
try {
|
|
975
|
+
await writeFile(path.join(siteDir, 'content.md'), `---
|
|
976
|
+
title: Removed note block
|
|
977
|
+
description: Fixture
|
|
978
|
+
---
|
|
979
|
+
|
|
980
|
+
## Intro {#intro}
|
|
981
|
+
|
|
982
|
+
\`\`\`norna-note
|
|
983
|
+
This block is no longer supported.
|
|
984
|
+
\`\`\`
|
|
985
|
+
`);
|
|
986
|
+
|
|
987
|
+
await assert.rejects(
|
|
988
|
+
() => runContentScript(siteDir, ['--check']),
|
|
989
|
+
(error) => {
|
|
990
|
+
assert.match(error.output, /Unknown Norna block "norna-note"\. Use one of: norna-image-stack, norna-image-carousel, norna-card-list\./);
|
|
991
|
+
return true;
|
|
992
|
+
},
|
|
993
|
+
);
|
|
994
|
+
} finally {
|
|
995
|
+
await rm(root, { recursive: true, force: true });
|
|
996
|
+
}
|
|
997
|
+
});
|
|
998
|
+
|
|
845
999
|
test('content:check supports tilde fenced Norna blocks', async () => {
|
|
846
1000
|
const { root, siteDir } = await createTempSite();
|
|
847
1001
|
try {
|
|
@@ -57,8 +57,10 @@ try {
|
|
|
57
57
|
for (const [presetName, preset] of Object.entries(typographyPresets)) {
|
|
58
58
|
for (const [level, heading] of Object.entries(preset.headings)) {
|
|
59
59
|
assert.equal(heading.size, 'medium', `${presetName} ${level} should use medium size`);
|
|
60
|
+
assert.ok([400, 500, 600, 700].includes(heading.weight), `${presetName} ${level} should use a supported weight`);
|
|
60
61
|
}
|
|
61
62
|
assert.equal(preset.body.size, 'medium', `${presetName} body should use medium size`);
|
|
63
|
+
assert.ok(['narrow', 'normal', 'wide'].includes(preset.body.width), `${presetName} body should use a supported width`);
|
|
62
64
|
assert.equal(preset.caption.size, 'medium', `${presetName} caption should use medium size`);
|
|
63
65
|
}
|
|
64
66
|
for (const [rhythmName, rhythm] of Object.entries(typographyRhythms)) {
|
|
@@ -76,7 +78,7 @@ try {
|
|
|
76
78
|
assert.match(showResult.stdout, /pages:/);
|
|
77
79
|
assert.match(showResult.stdout, /\s+\/:/);
|
|
78
80
|
assert.match(showResult.stdout, /value: quiet-gallery/);
|
|
79
|
-
assert.match(showResult.stdout, /
|
|
81
|
+
assert.match(showResult.stdout, /source: "site\/theme\.md"/);
|
|
80
82
|
assert.match(showResult.stdout, /intro:/);
|
|
81
83
|
|
|
82
84
|
const initAgainResult = runCli(['init', initializedSiteRoot]);
|
|
@@ -160,6 +160,7 @@ try {
|
|
|
160
160
|
assertFileExists(path.join(packagedStarterRoot, 'README.md')),
|
|
161
161
|
assertFileExists(path.join(packagedStarterRoot, 'site', 'config.mjs')),
|
|
162
162
|
assertFileExists(path.join(packagedStarterRoot, 'site', 'theme.md')),
|
|
163
|
+
assertFileExists(path.join(packagedStarterRoot, 'site', 'sitewide-content.md')),
|
|
163
164
|
assertFileExists(path.join(packagedStarterRoot, 'site', 'content.md')),
|
|
164
165
|
assertFileExists(path.join(packagedStarterRoot, 'site', 'images', 'work', '.gitkeep')),
|
|
165
166
|
assertFileExists(path.join(packagedStarterRoot, 'site', 'public', 'robots.txt')),
|
|
@@ -184,29 +185,38 @@ try {
|
|
|
184
185
|
.replace("url: 'https://example.com/'", "url: 'https://example.com/site/'")
|
|
185
186
|
.replace("basePath: '/'", "basePath: '/site/'"),
|
|
186
187
|
);
|
|
187
|
-
const
|
|
188
|
-
const
|
|
188
|
+
const packageCheckSitewidePath = path.join(siteProjectRoot, 'site', 'sitewide-content.md');
|
|
189
|
+
const packageCheckSitewide = await readFile(packageCheckSitewidePath, 'utf8');
|
|
189
190
|
await writeFile(
|
|
190
|
-
|
|
191
|
-
|
|
191
|
+
packageCheckSitewidePath,
|
|
192
|
+
packageCheckSitewide.replace('brand: Example Site', 'brand: Package Check Brand\n logo:\n alt: Package Check Logo\n height: 2.6rem'),
|
|
193
|
+
);
|
|
194
|
+
await writeFile(
|
|
195
|
+
path.join(siteProjectRoot, 'site', 'public', 'logo.svg'),
|
|
196
|
+
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><rect width="32" height="32" fill="black"/></svg>\n',
|
|
192
197
|
);
|
|
193
198
|
await writeFile(path.join(siteProjectRoot, 'site', 'content.md'), `---
|
|
194
199
|
title: Package Check Site
|
|
195
200
|
description: Site used by package checks.
|
|
196
201
|
sections:
|
|
197
202
|
intro: {}
|
|
203
|
+
warning: {}
|
|
198
204
|
work: {}
|
|
199
205
|
---
|
|
200
206
|
|
|
201
207
|
## Intro {#intro}
|
|
202
208
|
|
|
203
|
-
This page verifies that
|
|
209
|
+
This page verifies that **packaged norna sites** can build with the current content model.
|
|
204
210
|
|
|
205
211
|
<!-- norna-image-provenance:
|
|
206
212
|
image: package-check.svg
|
|
207
213
|
source: package check fixture
|
|
208
214
|
-->
|
|
209
215
|
|
|
216
|
+
## **Warning** {#warning}
|
|
217
|
+
|
|
218
|
+
This section verifies styled section headings.
|
|
219
|
+
|
|
210
220
|
## Work {#work}
|
|
211
221
|
|
|
212
222
|
This section keeps the package check independent from user-facing starter copy.
|
|
@@ -257,6 +267,10 @@ This route verifies that packaged norna sites can build route pages.
|
|
|
257
267
|
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
258
268
|
'--section-heading-font-size-desktop: clamp(1.35rem, 1.1rem + 0.9vw, 2rem)',
|
|
259
269
|
);
|
|
270
|
+
await assertFileIncludes(
|
|
271
|
+
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
272
|
+
'--section-heading-font-weight: 400',
|
|
273
|
+
);
|
|
260
274
|
await assertFileIncludes(
|
|
261
275
|
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
262
276
|
'--section-heading-spacing-after: 0.55em',
|
|
@@ -283,7 +297,15 @@ This route verifies that packaged norna sites can build route pages.
|
|
|
283
297
|
);
|
|
284
298
|
await assertFileIncludes(
|
|
285
299
|
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
286
|
-
'<a class="site-brand" href="/site/"
|
|
300
|
+
'<a class="site-brand" href="/site/"><img class="site-brand-logo" src="/site/logo.svg" alt="Package Check Logo">',
|
|
301
|
+
);
|
|
302
|
+
await assertFileIncludes(
|
|
303
|
+
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
304
|
+
'<div class="site-nav-submenu">',
|
|
305
|
+
);
|
|
306
|
+
await assertFileIncludes(
|
|
307
|
+
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
308
|
+
'href="/site/#intro">Intro</a>',
|
|
287
309
|
);
|
|
288
310
|
await assertFileIncludes(
|
|
289
311
|
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
@@ -293,6 +315,10 @@ This route verifies that packaged norna sites can build route pages.
|
|
|
293
315
|
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
294
316
|
'--section-body-line-height: 1.5',
|
|
295
317
|
);
|
|
318
|
+
await assertFileIncludes(
|
|
319
|
+
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
320
|
+
'--section-body-width-desktop: min(72ch, var(--text-width))',
|
|
321
|
+
);
|
|
296
322
|
await assertFileExcludes(
|
|
297
323
|
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
298
324
|
'norna-image-provenance',
|
|
@@ -313,21 +339,37 @@ This route verifies that packaged norna sites can build route pages.
|
|
|
313
339
|
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
314
340
|
'--section-caption-font-size-desktop: clamp(0.86rem, 0.84rem + 0.08vw, 0.92rem)',
|
|
315
341
|
);
|
|
342
|
+
await assertFileExcludes(
|
|
343
|
+
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
344
|
+
'class="inline-style',
|
|
345
|
+
);
|
|
346
|
+
await assertFileIncludes(
|
|
347
|
+
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
348
|
+
'data-section-title="Warning"',
|
|
349
|
+
);
|
|
316
350
|
await assertFileIncludes(
|
|
317
351
|
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
318
|
-
'
|
|
352
|
+
'href="/site/#warning">Warning</a>',
|
|
319
353
|
);
|
|
320
354
|
await assertFileIncludes(
|
|
321
355
|
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
322
356
|
'--section-background-color: #000000',
|
|
323
357
|
);
|
|
358
|
+
await assertFileIncludes(
|
|
359
|
+
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
360
|
+
'--section-background-color: #171717',
|
|
361
|
+
);
|
|
362
|
+
await assertFileIncludes(
|
|
363
|
+
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
364
|
+
'--section-background-color: #252525',
|
|
365
|
+
);
|
|
324
366
|
await assertFileIncludes(
|
|
325
367
|
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
326
368
|
'--site-top-background-color: #000000',
|
|
327
369
|
);
|
|
328
370
|
await assertFileIncludes(
|
|
329
371
|
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
330
|
-
'--site-top-text-color: #
|
|
372
|
+
'--site-top-text-color: #f2eee6',
|
|
331
373
|
);
|
|
332
374
|
await assertFileIncludes(
|
|
333
375
|
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
@@ -335,11 +377,11 @@ This route verifies that packaged norna sites can build route pages.
|
|
|
335
377
|
);
|
|
336
378
|
await assertFileIncludes(
|
|
337
379
|
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
338
|
-
'--site-footer-text-color: #
|
|
380
|
+
'--site-footer-text-color: #f2eee6',
|
|
339
381
|
);
|
|
340
382
|
await assertFileIncludes(
|
|
341
383
|
path.join(siteProjectRoot, 'dist', 'index.html'),
|
|
342
|
-
'--section-text-color: #
|
|
384
|
+
'--section-text-color: #f2eee6',
|
|
343
385
|
);
|
|
344
386
|
await writeFile(path.join(siteProjectRoot, 'site', 'public', 'favicon.ico'), 'fake icon');
|
|
345
387
|
await runInherit(npxBin, ['norna', 'build'], { cwd: siteProjectRoot, env: npmEnv });
|
|
@@ -365,35 +407,52 @@ This route verifies that packaged norna sites can build route pages.
|
|
|
365
407
|
);
|
|
366
408
|
await writeFile(
|
|
367
409
|
siteThemePath,
|
|
368
|
-
siteTheme.replace('
|
|
410
|
+
siteTheme.replace('\n palette: dark', '\n palette: neon'),
|
|
369
411
|
);
|
|
370
412
|
await runExpectFailure(
|
|
371
413
|
npxBin,
|
|
372
414
|
['norna', 'build'],
|
|
373
415
|
{ cwd: siteProjectRoot, env: npmEnv },
|
|
374
|
-
'presentation.
|
|
416
|
+
'presentation.palette',
|
|
375
417
|
);
|
|
376
418
|
await writeFile(
|
|
377
419
|
siteThemePath,
|
|
378
|
-
siteTheme.replace('
|
|
420
|
+
siteTheme.replace(' sectionSurfaces:\n mode: cycle', ' sectionSurfaces:\n mode: glowing'),
|
|
379
421
|
);
|
|
380
422
|
await runExpectFailure(
|
|
381
423
|
npxBin,
|
|
382
424
|
['norna', 'build'],
|
|
383
425
|
{ cwd: siteProjectRoot, env: npmEnv },
|
|
384
|
-
'presentation.
|
|
426
|
+
'presentation.sectionSurfaces.mode',
|
|
385
427
|
);
|
|
386
428
|
await writeFile(
|
|
387
429
|
siteThemePath,
|
|
388
|
-
siteTheme.replace(
|
|
430
|
+
siteTheme.replace(
|
|
431
|
+
' sectionSurfaces:\n mode: cycle',
|
|
432
|
+
' sectionSurfaces:\n mode: cycle\n sequence: [base, base]',
|
|
433
|
+
),
|
|
389
434
|
);
|
|
390
435
|
await runExpectFailure(
|
|
391
436
|
npxBin,
|
|
392
437
|
['norna', 'build'],
|
|
393
438
|
{ cwd: siteProjectRoot, env: npmEnv },
|
|
394
|
-
'presentation.
|
|
439
|
+
'presentation.sectionSurfaces',
|
|
395
440
|
);
|
|
396
441
|
await writeFile(siteThemePath, siteTheme);
|
|
442
|
+
await writeFile(
|
|
443
|
+
siteContentPath,
|
|
444
|
+
siteContent.replace(
|
|
445
|
+
'This page verifies that **packaged norna sites** can build with the current content model.',
|
|
446
|
+
'This page verifies that [packaged norna sites]{.highlight} can build with the current content model.',
|
|
447
|
+
),
|
|
448
|
+
);
|
|
449
|
+
await runExpectFailure(
|
|
450
|
+
npxBin,
|
|
451
|
+
['norna', 'build'],
|
|
452
|
+
{ cwd: siteProjectRoot, env: npmEnv },
|
|
453
|
+
'Inline color style ".highlight" is no longer supported',
|
|
454
|
+
);
|
|
455
|
+
await writeFile(siteContentPath, siteContent);
|
|
397
456
|
await writeFile(
|
|
398
457
|
siteContentPath,
|
|
399
458
|
siteContent.replace(
|
|
@@ -405,7 +464,7 @@ This route verifies that packaged norna sites can build route pages.
|
|
|
405
464
|
npxBin,
|
|
406
465
|
['norna', 'build'],
|
|
407
466
|
{ cwd: siteProjectRoot, env: npmEnv },
|
|
408
|
-
'presentation
|
|
467
|
+
'presentation',
|
|
409
468
|
);
|
|
410
469
|
await writeFile(
|
|
411
470
|
siteContentPath,
|
|
@@ -418,7 +477,7 @@ This route verifies that packaged norna sites can build route pages.
|
|
|
418
477
|
npxBin,
|
|
419
478
|
['norna', 'build'],
|
|
420
479
|
{ cwd: siteProjectRoot, env: npmEnv },
|
|
421
|
-
'sections.intro
|
|
480
|
+
'sections.intro: Unrecognized key: "presentation"',
|
|
422
481
|
);
|
|
423
482
|
await writeFile(
|
|
424
483
|
siteContentPath,
|
|
@@ -431,7 +490,7 @@ This route verifies that packaged norna sites can build route pages.
|
|
|
431
490
|
npxBin,
|
|
432
491
|
['norna', 'build'],
|
|
433
492
|
{ cwd: siteProjectRoot, env: npmEnv },
|
|
434
|
-
'sections.intro
|
|
493
|
+
'sections.intro: Unrecognized key: "presentation"',
|
|
435
494
|
);
|
|
436
495
|
await writeFile(
|
|
437
496
|
siteThemePath,
|
|
@@ -51,8 +51,23 @@ try {
|
|
|
51
51
|
await writeFile(path.join(siteDir, 'theme.md'), `---
|
|
52
52
|
typography:
|
|
53
53
|
preset: quiet-gallery
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
presentation:
|
|
55
|
+
palette: dark
|
|
56
|
+
---
|
|
57
|
+
`);
|
|
58
|
+
await writeFile(path.join(siteDir, 'sitewide-content.md'), `---
|
|
59
|
+
banners:
|
|
60
|
+
- id: expired-banner
|
|
61
|
+
visible:
|
|
62
|
+
until: "2026-01-01"
|
|
63
|
+
title: Expired banner
|
|
64
|
+
text: Expired banner text.
|
|
65
|
+
- id: active-banner
|
|
66
|
+
visible:
|
|
67
|
+
from: "2026-01-01"
|
|
68
|
+
until: "2026-12-31"
|
|
69
|
+
title: Active banner
|
|
70
|
+
text: Active banner text.
|
|
56
71
|
---
|
|
57
72
|
`);
|
|
58
73
|
await writeFile(path.join(siteDir, 'content.md'), `---
|
|
@@ -88,6 +103,8 @@ Always visible section text.
|
|
|
88
103
|
assert.match(html, /Active section text/);
|
|
89
104
|
assert.match(html, /Always visible section text/);
|
|
90
105
|
assert.doesNotMatch(html, /Expired section text/);
|
|
106
|
+
assert.match(html, /Active banner text/);
|
|
107
|
+
assert.doesNotMatch(html, /Expired banner text/);
|
|
91
108
|
|
|
92
109
|
console.log('Temporary visibility test passed.');
|
|
93
110
|
} finally {
|
|
@@ -18,16 +18,19 @@ type CardListWidth = 'text' | 'narrow' | 'normal' | 'wide';
|
|
|
18
18
|
|
|
19
19
|
interface Props {
|
|
20
20
|
cards: Card[];
|
|
21
|
+
maxAvailableWidthPercent: {
|
|
22
|
+
desktop: number;
|
|
23
|
+
};
|
|
21
24
|
layout?: CardListLayout;
|
|
22
25
|
flow?: CardListFlow;
|
|
23
26
|
size?: CardListSize;
|
|
24
27
|
width?: CardListWidth;
|
|
25
28
|
}
|
|
26
29
|
|
|
27
|
-
const { cards, layout = 'image-top', flow = 'grid', size = 'm', width = 'normal' } = Astro.props;
|
|
30
|
+
const { cards, maxAvailableWidthPercent, layout = 'image-top', flow = 'grid', size = 'm', width = 'normal' } = Astro.props;
|
|
28
31
|
const cardImageSizes = [
|
|
29
32
|
'(max-width: 700px) 100vw',
|
|
30
|
-
`min(20rem, ${
|
|
33
|
+
`min(20rem, ${maxAvailableWidthPercent.desktop}vw)`,
|
|
31
34
|
].join(', ');
|
|
32
35
|
const displayLink = (link: string) => withBasePath(projectConfig.site.basePath, link);
|
|
33
36
|
const cardListClasses = [
|