@janga/norna 0.7.6 → 0.7.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/astro.config.mjs +76 -0
- package/bin/norna-cli.mjs +2 -2
- package/docs/README.md +3 -3
- package/docs/commands.md +6 -6
- package/docs/configuration.md +3 -2
- package/docs/content.md +152 -91
- package/docs/design/site-examples-structure.md +1 -1
- package/docs/engine-development.md +3 -3
- package/docs/getting-started.md +4 -3
- package/docs/images-and-metadata.md +112 -20
- package/docs/routes.md +9 -12
- package/docs/site-structure.md +4 -3
- package/docs/theme.md +8 -7
- package/docs/typography.md +5 -5
- package/examples/dog-gallery/site/content.md +41 -45
- package/examples/dog-gallery/site/routes/010-dog-care/route-content.md +1 -3
- package/fixtures/basic/site/content.md +2 -4
- package/fixtures/content-model-v2/.astro/collections/site.schema.json +776 -0
- package/fixtures/content-model-v2/.astro/collections/theme.schema.json +668 -0
- package/fixtures/content-model-v2/.astro/content-assets.mjs +4 -0
- package/fixtures/content-model-v2/.astro/content-modules.mjs +1 -0
- package/fixtures/content-model-v2/.astro/content.d.ts +175 -0
- package/fixtures/content-model-v2/.astro/data-store.json +1 -0
- package/fixtures/content-model-v2/.astro/dev-local.json +8 -0
- package/fixtures/content-model-v2/.astro/dev.json +13 -0
- package/fixtures/content-model-v2/.astro/dev.log +231 -0
- package/fixtures/content-model-v2/.astro/settings.json +5 -0
- package/fixtures/content-model-v2/.astro/types.d.ts +2 -0
- package/fixtures/content-model-v2/dist/_astro/GalleryGrid.astro_astro_type_script_index_0_lang.B0kxDyGz.js +1 -0
- package/fixtures/content-model-v2/dist/_astro/SitePage.BbfZub4g.css +1 -0
- package/fixtures/content-model-v2/dist/guide/index.html +3 -0
- package/fixtures/content-model-v2/dist/images/generated/intro/detail-27930b14-480.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/intro/detail-27930b14-768.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/intro/detail-27930b14-971.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/intro/duplicate-49c98f6c-480.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/intro/duplicate-49c98f6c-768.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/intro/duplicate-49c98f6c-971.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/intro/hero-6d588a83-480.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/intro/hero-6d588a83-768.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/intro/hero-6d588a83-971.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/plain/image-3770d763-1080.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/plain/image-3770d763-1440.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/plain/image-3770d763-1600.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/plain/image-3770d763-480.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/plain/image-3770d763-768.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/routes/010-guide/images/intro/duplicate-5cf614d2-480.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/routes/010-guide/images/intro/duplicate-5cf614d2-768.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/routes/010-guide/images/intro/duplicate-5cf614d2-960.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/timed/slide-one-3770d763-1080.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/timed/slide-one-3770d763-1440.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/timed/slide-one-3770d763-1600.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/timed/slide-one-3770d763-480.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/timed/slide-one-3770d763-768.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/timed/slide-three-5bdec65e-1080.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/timed/slide-three-5bdec65e-1440.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/timed/slide-three-5bdec65e-1600.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/timed/slide-three-5bdec65e-480.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/timed/slide-three-5bdec65e-768.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/timed/slide-two-26c08aee-1080.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/timed/slide-two-26c08aee-1440.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/timed/slide-two-26c08aee-1600.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/timed/slide-two-26c08aee-480.webp +0 -0
- package/fixtures/content-model-v2/dist/images/generated/timed/slide-two-26c08aee-768.webp +0 -0
- package/fixtures/content-model-v2/dist/index.html +8 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/_metadata.json +80 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/astro-D_GIpJIE.js +434 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/astro-D_GIpJIE.js.map +1 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/astro_n_aria-query.js +6390 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/astro_n_aria-query.js.map +1 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/astro_n_axobject-query.js +2702 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/astro_n_axobject-query.js.map +1 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/astro_n_html-escaper.js +65 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/astro_n_html-escaper.js.map +1 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +455 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +1 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/audit-DM9Hkl5B.js +1534 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/audit-DM9Hkl5B.js.map +1 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/embla-carousel.js +1389 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/embla-carousel.js.map +1 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/highlight-DnZ1ULOL.js +48 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/highlight-DnZ1ULOL.js.map +1 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/icons-RyebKi45.js +42 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/icons-RyebKi45.js.map +1 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/package.json +3 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/rolldown-runtime-BvCyGRYZ.js +4 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/settings-DaU-gNnK.js +190 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/settings-DaU-gNnK.js.map +1 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/toolbar-63PA619H.js +458 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/toolbar-63PA619H.js.map +1 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/ui-library--bkcNSYz.js +1016 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/ui-library--bkcNSYz.js.map +1 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/window-jPkpKVcy.js +32 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/window-jPkpKVcy.js.map +1 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/xray-BXCHpEJP.js +111 -0
- package/fixtures/content-model-v2/node_modules/.vite/deps/xray-BXCHpEJP.js.map +1 -0
- package/fixtures/content-model-v2/site/.norna/generated-images.json +210 -0
- package/fixtures/content-model-v2/site/config.mjs +11 -0
- package/fixtures/content-model-v2/site/content.md +84 -0
- package/fixtures/content-model-v2/site/images/card-section/adopt.svg +9 -0
- package/fixtures/content-model-v2/site/images/card-section/foster.svg +12 -0
- package/fixtures/content-model-v2/site/images/intro/detail.jpg +0 -0
- package/fixtures/content-model-v2/site/images/intro/duplicate.jpg +0 -0
- package/fixtures/content-model-v2/site/images/intro/hero.jpg +0 -0
- package/fixtures/content-model-v2/site/images/plain/image.jpg +0 -0
- package/fixtures/content-model-v2/site/images/timed/slide-one.jpg +0 -0
- package/fixtures/content-model-v2/site/images/timed/slide-three.jpg +0 -0
- package/fixtures/content-model-v2/site/images/timed/slide-two.jpg +0 -0
- package/fixtures/content-model-v2/site/routes/010-guide/images/intro/duplicate.jpg +0 -0
- package/fixtures/content-model-v2/site/routes/010-guide/route-content.md +17 -0
- package/fixtures/content-model-v2/site/theme.md +9 -0
- package/package.json +3 -2
- package/scripts/deploy-site.mjs +11 -4
- package/scripts/dev-local.mjs +6 -0
- package/scripts/generate-images.mjs +132 -56
- package/scripts/lib/image-dimensions.mjs +26 -0
- package/scripts/lib/norna-markdown-blocks.mjs +643 -0
- package/scripts/lib/site-content.mjs +31 -29
- package/scripts/show-typography.mjs +17 -9
- package/scripts/sync-content-sections.mjs +280 -411
- package/scripts/test-content-check.mjs +59 -45
- package/scripts/test-content-model-v2.mjs +998 -0
- package/scripts/test-engine-commands.mjs +1 -1
- package/scripts/test-package-check.mjs +36 -8
- package/scripts/test-temporary-visibility.mjs +3 -6
- package/src/components/CardList.astro +77 -0
- package/src/components/GalleryGrid.astro +22 -12
- package/src/components/SiteNavigation.astro +5 -4
- package/src/components/SitePage.astro +9 -2
- package/src/components/SiteSection.astro +35 -13
- package/src/content.config.ts +4 -17
- package/src/lib/generatedImages.ts +50 -10
- package/src/lib/sectionContent.ts +130 -35
- package/src/styles/global.css +187 -0
- package/starters/basic/site/content.md +10 -6
- package/starters/project/site/content.md +0 -9
- package/starters/project/site/routes/010-guide/route-content.md +0 -5
package/README.md
CHANGED
|
@@ -25,15 +25,15 @@ A `norna` site is file-driven:
|
|
|
25
25
|
base path, in `site/config.mjs`.
|
|
26
26
|
3. The site keeps site-wide visual theme defaults, including layout, image
|
|
27
27
|
sizing, font, typography, colors, and frame colors, in `site/theme.md`.
|
|
28
|
-
4. The site keeps homepage content, section order, image
|
|
29
|
-
captions in `site/content.md`.
|
|
28
|
+
4. The site keeps homepage content, Markdown section order, image blocks, alt
|
|
29
|
+
text, captions, and optional section metadata in `site/content.md`.
|
|
30
30
|
5. Optional route pages live under
|
|
31
31
|
`site/routes/<route-folder>/route-content.md`.
|
|
32
32
|
6. Source images live under `site/images/<section-id>/` for the homepage, or
|
|
33
33
|
under `site/routes/<route-folder>/images/<section-id>/` for a route.
|
|
34
34
|
7. Static public files live under `site/public/`.
|
|
35
|
-
8. `norna` validates the files,
|
|
36
|
-
|
|
35
|
+
8. `norna` validates the files, prepares managed images, builds static Astro
|
|
36
|
+
pages, and can help publish the committed branch through GitHub Pages.
|
|
37
37
|
|
|
38
38
|
The default site directory is `site/`. Commands can also use another directory
|
|
39
39
|
with `NORNA_SITE_DIR` or `norna --site-dir <path>`. Without an explicit site
|
|
@@ -75,7 +75,7 @@ Actions use the same engine version.
|
|
|
75
75
|
|
|
76
76
|
- Create a site: [Getting Started](docs/getting-started.md)
|
|
77
77
|
- Understand required site files: [Site Structure](docs/site-structure.md)
|
|
78
|
-
- Edit sections and image
|
|
78
|
+
- Edit sections and image blocks: [Content](docs/content.md)
|
|
79
79
|
- Configure a site: [Configuration](docs/configuration.md)
|
|
80
80
|
- Publish under a GitHub Pages project path:
|
|
81
81
|
[`site.basePath`](docs/configuration.md#sitebasepath)
|
package/astro.config.mjs
CHANGED
|
@@ -1,14 +1,87 @@
|
|
|
1
1
|
// @ts-check
|
|
2
|
+
import { execFile } from 'node:child_process';
|
|
2
3
|
import path from 'node:path';
|
|
4
|
+
import { promisify } from 'node:util';
|
|
3
5
|
import { defineConfig } from 'astro/config';
|
|
4
6
|
import {
|
|
5
7
|
astroCacheDir,
|
|
6
8
|
astroDistDir,
|
|
7
9
|
astroPublicDir,
|
|
8
10
|
engineRoot,
|
|
11
|
+
generatedImagesManifestPath,
|
|
12
|
+
siteContentPath,
|
|
13
|
+
siteImagesDir,
|
|
14
|
+
siteProjectRoot,
|
|
15
|
+
siteRoutesDir,
|
|
9
16
|
} from './scripts/lib/site-paths.mjs';
|
|
10
17
|
import projectConfig from './scripts/lib/project-config.mjs';
|
|
11
18
|
|
|
19
|
+
const execFileAsync = promisify(execFile);
|
|
20
|
+
|
|
21
|
+
const isWithinDirectory = (parentDirectory, filePath) => {
|
|
22
|
+
const relativePath = path.relative(parentDirectory, filePath);
|
|
23
|
+
return relativePath === '' || (!relativePath.startsWith('..') && !path.isAbsolute(relativePath));
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const runGenerateImages = () => execFileAsync(process.execPath, [path.join(engineRoot, 'scripts', 'generate-images.mjs')], {
|
|
27
|
+
cwd: siteProjectRoot,
|
|
28
|
+
maxBuffer: 1024 * 1024 * 10,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const nornaGeneratedImagesWatcher = () => ({
|
|
32
|
+
name: 'norna-generated-images-watcher',
|
|
33
|
+
configureServer(server) {
|
|
34
|
+
const manifestPath = path.resolve(generatedImagesManifestPath);
|
|
35
|
+
const watchedSourcePaths = [
|
|
36
|
+
siteContentPath,
|
|
37
|
+
siteImagesDir,
|
|
38
|
+
siteRoutesDir,
|
|
39
|
+
].map((watchedPath) => path.resolve(watchedPath));
|
|
40
|
+
let refreshTimer;
|
|
41
|
+
let refreshPromise = Promise.resolve();
|
|
42
|
+
|
|
43
|
+
const isRelevantSourcePath = (changedPath) => {
|
|
44
|
+
const resolvedPath = path.resolve(changedPath);
|
|
45
|
+
return watchedSourcePaths.some((watchedPath) => (
|
|
46
|
+
resolvedPath === watchedPath || isWithinDirectory(watchedPath, resolvedPath)
|
|
47
|
+
));
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const refreshImages = () => {
|
|
51
|
+
refreshPromise = refreshPromise
|
|
52
|
+
.catch(() => {})
|
|
53
|
+
.then(async () => {
|
|
54
|
+
try {
|
|
55
|
+
await runGenerateImages();
|
|
56
|
+
} catch (error) {
|
|
57
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
58
|
+
server.config.logger.error(`Norna image refresh failed:\n${message}`);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const scheduleRefreshImages = (changedPath) => {
|
|
64
|
+
if (!isRelevantSourcePath(changedPath)) return;
|
|
65
|
+
|
|
66
|
+
clearTimeout(refreshTimer);
|
|
67
|
+
refreshTimer = setTimeout(refreshImages, 250);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
server.watcher.add(manifestPath);
|
|
71
|
+
server.watcher.add(watchedSourcePaths);
|
|
72
|
+
server.watcher.on('change', (changedPath) => {
|
|
73
|
+
if (path.resolve(changedPath) !== manifestPath) return;
|
|
74
|
+
|
|
75
|
+
server.ws.send({ type: 'full-reload' });
|
|
76
|
+
});
|
|
77
|
+
server.watcher.on('add', scheduleRefreshImages);
|
|
78
|
+
server.watcher.on('change', scheduleRefreshImages);
|
|
79
|
+
server.watcher.on('unlink', scheduleRefreshImages);
|
|
80
|
+
server.watcher.on('addDir', scheduleRefreshImages);
|
|
81
|
+
server.watcher.on('unlinkDir', scheduleRefreshImages);
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
|
|
12
85
|
// https://astro.build/config
|
|
13
86
|
export default defineConfig({
|
|
14
87
|
base: projectConfig.site.basePath,
|
|
@@ -16,4 +89,7 @@ export default defineConfig({
|
|
|
16
89
|
outDir: astroDistDir,
|
|
17
90
|
publicDir: astroPublicDir,
|
|
18
91
|
srcDir: path.join(engineRoot, 'src'),
|
|
92
|
+
vite: {
|
|
93
|
+
plugins: [nornaGeneratedImagesWatcher()],
|
|
94
|
+
},
|
|
19
95
|
});
|
package/bin/norna-cli.mjs
CHANGED
|
@@ -16,7 +16,7 @@ Commands:
|
|
|
16
16
|
dev:stop Stop local dev server
|
|
17
17
|
config:check Validate site/config.mjs
|
|
18
18
|
content:check Validate site/content.md and image references
|
|
19
|
-
content:sync
|
|
19
|
+
content:sync Move misplaced Norna-managed images and refresh generated images
|
|
20
20
|
typography presets Show built-in typography preset values
|
|
21
21
|
typography show Show resolved typography for the selected site
|
|
22
22
|
site:public Sync site/public/ to public/
|
|
@@ -128,9 +128,9 @@ try {
|
|
|
128
128
|
await runScript('scripts/check-config.mjs', rest);
|
|
129
129
|
} else if (command === 'content:check') {
|
|
130
130
|
await runScript('scripts/sync-content-sections.mjs', ['--check', ...rest]);
|
|
131
|
-
await runAstroInherit(['sync']);
|
|
132
131
|
} else if (command === 'content:sync') {
|
|
133
132
|
await runScript('scripts/sync-content-sections.mjs', ['--write', ...rest]);
|
|
133
|
+
await runScript('scripts/generate-images.mjs', rest);
|
|
134
134
|
} else if (command === 'typography:presets' || (command === 'typography' && subcommand === 'presets')) {
|
|
135
135
|
const scriptArgs = command === 'typography' ? subcommandRest : rest;
|
|
136
136
|
await runScript('scripts/show-typography.mjs', ['presets', ...scriptArgs]);
|
package/docs/README.md
CHANGED
|
@@ -38,9 +38,9 @@ For a quick reference:
|
|
|
38
38
|
- [Configuration](configuration.md) describes technical `site/config.mjs`
|
|
39
39
|
fields such as URL, base path, locale labels, footer, GitHub, and deploy
|
|
40
40
|
settings.
|
|
41
|
-
- [Content](content.md) describes page frontmatter,
|
|
42
|
-
|
|
43
|
-
content validation.
|
|
41
|
+
- [Content](content.md) describes page frontmatter, Markdown-authored
|
|
42
|
+
sections, optional section metadata, image stacks, carousels, temporary
|
|
43
|
+
sections, and content validation.
|
|
44
44
|
- [Theme](theme.md) describes site-wide layout, layout density, image sizing,
|
|
45
45
|
font, typography rhythm, presentation colors, frame colors, and inline styles.
|
|
46
46
|
- [Typography](typography.md) describes presets, rhythms, roles, overrides,
|
package/docs/commands.md
CHANGED
|
@@ -101,10 +101,10 @@ should normally mean the repository's complete publishable artifact, while
|
|
|
101
101
|
cache path.
|
|
102
102
|
- `config:check`: validates `site/config.mjs` against the runtime config
|
|
103
103
|
reader.
|
|
104
|
-
- `content:check`: validates section structure
|
|
105
|
-
|
|
106
|
-
- `content:sync` / `norna:sync`:
|
|
107
|
-
|
|
104
|
+
- `content:check`: validates section structure, image references, inline
|
|
105
|
+
styles, and common content mistakes.
|
|
106
|
+
- `content:sync` / `norna:sync`: moves misplaced referenced image files after
|
|
107
|
+
confirmation.
|
|
108
108
|
- `typography presets`: prints the exact built-in values for typography
|
|
109
109
|
presets and rhythms.
|
|
110
110
|
- `typography show`: prints the selected site's resolved typography for the
|
|
@@ -112,8 +112,8 @@ should normally mean the repository's complete publishable artifact, while
|
|
|
112
112
|
and inherited page or section values are marked with `inherited: true`.
|
|
113
113
|
- `site:public`: copies `site/public/` into `site/.norna/public/` and
|
|
114
114
|
removes stale copied static files.
|
|
115
|
-
- `images`: generates WebP variants
|
|
116
|
-
`site/.norna/generated-images.json`.
|
|
115
|
+
- `images`: generates WebP variants for raster images, copies managed SVG
|
|
116
|
+
images, and writes `site/.norna/generated-images.json`.
|
|
117
117
|
- `engine:update [version|latest]`: updates the site repository's
|
|
118
118
|
`@janga/norna` dependency with `npm install --save-exact`, normalizes
|
|
119
119
|
`package-lock.json` for the pinned GitHub Actions Linux/npm environment, and
|
package/docs/configuration.md
CHANGED
|
@@ -39,7 +39,7 @@ site: {
|
|
|
39
39
|
`?`, `#`, or `//`.
|
|
40
40
|
- Consequence: use `/` for a root site or custom domain. Use
|
|
41
41
|
`/repository-name/` for a GitHub Pages project site without a custom domain.
|
|
42
|
-
Source Markdown can still use root-relative paths such as `/
|
|
42
|
+
Source Markdown can still use root-relative paths such as `/favicon.svg` or
|
|
43
43
|
`/getting-started/`; Norna prefixes them during rendering.
|
|
44
44
|
|
|
45
45
|
Examples:
|
|
@@ -141,7 +141,8 @@ Fields:
|
|
|
141
141
|
`Sections`.
|
|
142
142
|
- `locale.labels.menu`: mobile menu summary text, default `Menu`.
|
|
143
143
|
- `locale.labels.closeMenu`: reserved close-menu label, default `Close menu`.
|
|
144
|
-
- `locale.labels.gallery`: image
|
|
144
|
+
- `locale.labels.gallery`: Norna image block ARIA label prefix, default
|
|
145
|
+
`Images`.
|
|
145
146
|
|
|
146
147
|
Example:
|
|
147
148
|
|
package/docs/content.md
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
# Content
|
|
2
2
|
|
|
3
|
-
`site/content.md` is the homepage page file for a
|
|
4
|
-
|
|
5
|
-
the homepage.
|
|
3
|
+
`site/content.md` is the homepage page file for a Norna site. It contains page
|
|
4
|
+
metadata in frontmatter and the homepage content in Markdown.
|
|
6
5
|
|
|
7
|
-
Route pages use the same page
|
|
6
|
+
Route pages use the same page model in
|
|
8
7
|
`site/routes/<NNN-route-id>/route-content.md`. See [Routes](routes.md) for the
|
|
9
8
|
route-specific rules.
|
|
10
9
|
|
|
@@ -22,8 +21,7 @@ The Astro content schema validates these top-level fields in page files:
|
|
|
22
21
|
- `presentation`: optional page-level presentation overrides. See
|
|
23
22
|
[Theme](theme.md) and [Typography](typography.md).
|
|
24
23
|
- `frame`: optional page-level frame color source. See [Theme](theme.md).
|
|
25
|
-
- `sections`:
|
|
26
|
-
presentation overrides, and image rows.
|
|
24
|
+
- `sections`: optional section metadata keyed by section id.
|
|
27
25
|
|
|
28
26
|
Minimal homepage:
|
|
29
27
|
|
|
@@ -31,8 +29,6 @@ Minimal homepage:
|
|
|
31
29
|
---
|
|
32
30
|
title: My Site
|
|
33
31
|
description: A small Norna site.
|
|
34
|
-
sections:
|
|
35
|
-
- id: intro
|
|
36
32
|
---
|
|
37
33
|
|
|
38
34
|
## Intro {#intro}
|
|
@@ -42,99 +38,181 @@ Text...
|
|
|
42
38
|
|
|
43
39
|
## Sections
|
|
44
40
|
|
|
45
|
-
|
|
41
|
+
Markdown level 2 headings define the page sections and their order:
|
|
46
42
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
- `visible`: optional date window that controls whether the section is rendered.
|
|
50
|
-
- `presentation`: optional section-level visual overrides.
|
|
51
|
-
- `gallery`: optional array, defaulting to `[]`.
|
|
43
|
+
```md
|
|
44
|
+
## About {#about}
|
|
52
45
|
|
|
53
|
-
|
|
46
|
+
...
|
|
54
47
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
48
|
+
## Work {#work}
|
|
49
|
+
|
|
50
|
+
...
|
|
51
|
+
|
|
52
|
+
## Contact {#contact}
|
|
53
|
+
|
|
54
|
+
...
|
|
62
55
|
```
|
|
63
56
|
|
|
64
|
-
Every
|
|
65
|
-
explicit id:
|
|
57
|
+
Every section heading must have an explicit id:
|
|
66
58
|
|
|
67
59
|
```md
|
|
68
60
|
## Work {#work}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The id must match `^[a-z0-9-]+$`. It is used for anchors, navigation, image
|
|
64
|
+
directories, and optional section metadata. The visible section navigation
|
|
65
|
+
label comes from the Markdown heading text.
|
|
66
|
+
|
|
67
|
+
Markdown section content starts at the level 2 heading and continues until the
|
|
68
|
+
next level 2 heading. `###` and `####` headings are body subheadings within the
|
|
69
|
+
current section, not new sections.
|
|
69
70
|
|
|
70
|
-
|
|
71
|
+
## Section Metadata
|
|
72
|
+
|
|
73
|
+
Use `sections` only when a section needs structured metadata that is not
|
|
74
|
+
naturally expressed by Markdown, such as visibility or presentation overrides.
|
|
75
|
+
|
|
76
|
+
```yaml
|
|
77
|
+
sections:
|
|
78
|
+
work:
|
|
79
|
+
presentation:
|
|
80
|
+
typography:
|
|
81
|
+
preset: statement
|
|
71
82
|
```
|
|
72
83
|
|
|
73
|
-
|
|
84
|
+
Each `sections.<section-id>` key must match a Markdown heading id in the same
|
|
85
|
+
page file:
|
|
74
86
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
87
|
+
```md
|
|
88
|
+
## Work {#work}
|
|
89
|
+
```
|
|
78
90
|
|
|
79
|
-
|
|
80
|
-
from the
|
|
91
|
+
Do not list sections in frontmatter just to define order. Section order comes
|
|
92
|
+
from the Markdown heading order.
|
|
81
93
|
|
|
82
|
-
## Image
|
|
94
|
+
## Image Blocks
|
|
83
95
|
|
|
84
|
-
|
|
96
|
+
Norna-managed local images are written in Markdown fenced blocks at the point
|
|
97
|
+
where they should appear in the section.
|
|
85
98
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
99
|
+
Use `norna-image-stack` for one or more stacked images:
|
|
100
|
+
|
|
101
|
+
````md
|
|
102
|
+
```norna-image-stack
|
|
103
|
+
- image: work.jpg
|
|
104
|
+
alt: A woven artwork on a white wall.
|
|
105
|
+
caption: Work in progress.
|
|
106
|
+
```
|
|
107
|
+
````
|
|
108
|
+
|
|
109
|
+
Use `norna-image-carousel` for a carousel:
|
|
110
|
+
|
|
111
|
+
````md
|
|
112
|
+
```norna-image-carousel
|
|
113
|
+
- image: first.jpg
|
|
114
|
+
alt: First image.
|
|
115
|
+
caption: First caption.
|
|
116
|
+
- image: second.jpg
|
|
117
|
+
alt: Second image.
|
|
118
|
+
caption: Second caption.
|
|
91
119
|
```
|
|
120
|
+
````
|
|
92
121
|
|
|
93
|
-
|
|
122
|
+
Each image entry supports:
|
|
94
123
|
|
|
95
|
-
- `image`: required filename matching
|
|
124
|
+
- `image`: required filename matching
|
|
125
|
+
`^[a-z0-9][a-z0-9.-]*\.(jpe?g|png|svg)$`.
|
|
96
126
|
It must be a filename, not a path.
|
|
97
|
-
- `alt`:
|
|
98
|
-
- `caption`: optional
|
|
127
|
+
- `alt`: optional alt text. If omitted, Norna renders an empty alt attribute.
|
|
128
|
+
- `caption`: optional caption.
|
|
99
129
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
images from `site/routes/<NNN-route-id>/images/<section-id>/`.
|
|
130
|
+
Start every image entry with `- image: filename.jpg`. Optional fields use two
|
|
131
|
+
spaces of indentation:
|
|
103
132
|
|
|
104
|
-
|
|
133
|
+
````md
|
|
134
|
+
```norna-image-stack
|
|
135
|
+
- image: filename.jpg
|
|
136
|
+
alt: Optional alt text.
|
|
137
|
+
caption: Optional caption.
|
|
138
|
+
```
|
|
139
|
+
````
|
|
105
140
|
|
|
106
|
-
|
|
141
|
+
Use three or more matching backticks or tildes for fenced blocks. If you need
|
|
142
|
+
to document a Norna image block inside another Markdown code sample, make the
|
|
143
|
+
outer fence longer than the inner fence:
|
|
107
144
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
alt: "First image."
|
|
113
|
-
caption: "First caption."
|
|
114
|
-
- image: second.jpg
|
|
115
|
-
alt: "Second image."
|
|
116
|
-
caption: "Second caption."
|
|
145
|
+
````md
|
|
146
|
+
````
|
|
147
|
+
```norna-image-stack
|
|
148
|
+
- image: filename.jpg
|
|
117
149
|
```
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
single image row.
|
|
150
|
+
````
|
|
151
|
+
````
|
|
121
152
|
|
|
122
153
|
`content:check` warns when carousel images have different aspect ratios. Exact
|
|
123
154
|
matching proportions are recommended because mixed proportions can make the
|
|
124
155
|
layout move while the user changes slides.
|
|
125
156
|
|
|
157
|
+
SVG files are allowed in image stacks and carousels. When an SVG has a
|
|
158
|
+
`viewBox` or numeric `width` and `height`, Norna uses that ratio in the same
|
|
159
|
+
layout logic as raster images. SVG files without an intrinsic aspect ratio are
|
|
160
|
+
rendered directly, but carousel usage produces a warning because stable slide
|
|
161
|
+
sizing is less predictable.
|
|
162
|
+
|
|
163
|
+
## Image Files
|
|
164
|
+
|
|
165
|
+
Homepage images live under:
|
|
166
|
+
|
|
167
|
+
```text
|
|
168
|
+
site/images/<section-id>/
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Route images live under:
|
|
172
|
+
|
|
173
|
+
```text
|
|
174
|
+
site/routes/<NNN-route-id>/images/<section-id>/
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Image references in Norna image blocks use only the filename:
|
|
178
|
+
|
|
179
|
+
````md
|
|
180
|
+
```norna-image-stack
|
|
181
|
+
- image: portrait.jpg
|
|
182
|
+
```
|
|
183
|
+
````
|
|
184
|
+
|
|
185
|
+
If `portrait.jpg` is referenced from `## Team {#team}`, the expected homepage
|
|
186
|
+
location is `site/images/team/portrait.jpg`.
|
|
187
|
+
|
|
188
|
+
Filenames do not have to be globally unique for the site to be valid. Automatic
|
|
189
|
+
sync only moves files when the filename identifies exactly one source candidate
|
|
190
|
+
within the current page or route.
|
|
191
|
+
|
|
192
|
+
## Markdown Images
|
|
193
|
+
|
|
194
|
+
Markdown image syntax is allowed for external images and public static assets:
|
|
195
|
+
|
|
196
|
+
```md
|
|
197
|
+

|
|
198
|
+

|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Relative local Markdown images such as `` are not
|
|
202
|
+
managed by Norna. Use `norna-image-stack` or `norna-image-carousel` for local
|
|
203
|
+
site images that should be validated, processed and synced.
|
|
204
|
+
|
|
126
205
|
## Temporary Sections
|
|
127
206
|
|
|
128
|
-
Use `sections
|
|
129
|
-
date window:
|
|
207
|
+
Use `sections.<section-id>.visible` for sections that should be rendered only
|
|
208
|
+
during a date window:
|
|
130
209
|
|
|
131
210
|
```yaml
|
|
132
211
|
sections:
|
|
133
|
-
|
|
212
|
+
exhibition:
|
|
134
213
|
visible:
|
|
135
214
|
from: "2026-08-01"
|
|
136
215
|
until: "2026-09-16"
|
|
137
|
-
gallery: []
|
|
138
216
|
```
|
|
139
217
|
|
|
140
218
|
`from` is inclusive. `until` is exclusive. With the example above, the section
|
|
@@ -156,17 +234,6 @@ NORNA_TODAY=2026-08-15 npm run norna:build
|
|
|
156
234
|
|
|
157
235
|
## Markdown Text
|
|
158
236
|
|
|
159
|
-
Section Markdown starts at the matching level 2 heading and continues until the
|
|
160
|
-
next level 2 heading.
|
|
161
|
-
|
|
162
|
-
```md
|
|
163
|
-
## Intro {#intro}
|
|
164
|
-
|
|
165
|
-
Paragraph text.
|
|
166
|
-
|
|
167
|
-
Another paragraph.
|
|
168
|
-
```
|
|
169
|
-
|
|
170
237
|
Inline styles use this Markdown form:
|
|
171
238
|
|
|
172
239
|
```md
|
|
@@ -184,16 +251,17 @@ Run:
|
|
|
184
251
|
npm run norna:content:check
|
|
185
252
|
```
|
|
186
253
|
|
|
187
|
-
This checks section
|
|
188
|
-
files, misplaced referenced images, duplicate image references,
|
|
189
|
-
|
|
190
|
-
|
|
254
|
+
This checks section heading ids, section metadata, duplicate image names,
|
|
255
|
+
missing image files, misplaced referenced images, duplicate image references,
|
|
256
|
+
invalid image blocks, unreferenced images, undefined inline styles, Markdown
|
|
257
|
+
image references to unmanaged local files, and common frontmatter indentation
|
|
258
|
+
and structure mistakes.
|
|
191
259
|
|
|
192
260
|
Frontmatter uses YAML indentation. Use ordinary spaces, not tabs or
|
|
193
261
|
non-breaking spaces. `content:check` reports a focused error when indentation is
|
|
194
262
|
invalid, when a key is indented under a line that already has a value, or when a
|
|
195
|
-
known nested key such as
|
|
196
|
-
|
|
263
|
+
known nested key such as section-specific `typography` appears at the top
|
|
264
|
+
level:
|
|
197
265
|
|
|
198
266
|
```yaml
|
|
199
267
|
presentation:
|
|
@@ -205,14 +273,7 @@ presentation:
|
|
|
205
273
|
```
|
|
206
274
|
|
|
207
275
|
Top-level page frontmatter may contain only `title`, `description`,
|
|
208
|
-
`navigation`, `presentation`, `frame`, and `sections`.
|
|
209
|
-
under one `sections[]` item:
|
|
210
|
-
|
|
211
|
-
```yaml
|
|
212
|
-
sections:
|
|
213
|
-
- id: work
|
|
214
|
-
gallery: []
|
|
215
|
-
```
|
|
276
|
+
`navigation`, `presentation`, `frame`, and `sections`.
|
|
216
277
|
|
|
217
278
|
Run:
|
|
218
279
|
|
|
@@ -220,6 +281,6 @@ Run:
|
|
|
220
281
|
npm run norna:sync
|
|
221
282
|
```
|
|
222
283
|
|
|
223
|
-
This
|
|
224
|
-
|
|
225
|
-
|
|
284
|
+
This moves referenced image files into the section directory shown by the
|
|
285
|
+
Markdown placement. It prompts before writing unless `--yes` is passed. Sync is
|
|
286
|
+
currently page- or route-local: it does not move images across routes.
|
|
@@ -26,7 +26,7 @@ site repository. It should not be a showcase for every feature.
|
|
|
26
26
|
An example site is a runnable site that demonstrates one or more features.
|
|
27
27
|
|
|
28
28
|
Examples can be richer than the starter. They may show routes, navigation,
|
|
29
|
-
typography presets, typography rhythms,
|
|
29
|
+
typography presets, typography rhythms, image stacks, image carousels, inline
|
|
30
30
|
styles, and site-specific configuration choices.
|
|
31
31
|
|
|
32
32
|
### Documentation Site
|
|
@@ -9,13 +9,13 @@ This document is for work on the reusable `norna` package itself.
|
|
|
9
9
|
- `scripts/lib/site-paths.mjs`: engine/site path resolution.
|
|
10
10
|
- `scripts/lib/project-config.mjs`: `site/config.mjs` validation and defaults.
|
|
11
11
|
- `scripts/sync-content-sections.mjs`: content validation and sync behavior.
|
|
12
|
-
- `scripts/generate-images.mjs`:
|
|
12
|
+
- `scripts/generate-images.mjs`: managed image pipeline and manifest.
|
|
13
13
|
- `scripts/sync-site-public.mjs`: static public file sync.
|
|
14
14
|
- `scripts/deploy-site.mjs`: deploy and deploy:commit behavior.
|
|
15
15
|
- `scripts/watch-pages-deploy.mjs`: GitHub Pages workflow monitor.
|
|
16
16
|
- `src/content.config.ts`: Astro content schema.
|
|
17
|
-
- `src/components/` and `src/layouts/`: rendered page, navigation, image
|
|
18
|
-
and layout.
|
|
17
|
+
- `src/components/` and `src/layouts/`: rendered page, navigation, image
|
|
18
|
+
blocks, and layout.
|
|
19
19
|
- `tests/`: Playwright navigation diagnostics.
|
|
20
20
|
- `fixtures/basic/site/`: minimal site used for engine checks.
|
|
21
21
|
- `starters/basic/`: copyable site starter.
|
package/docs/getting-started.md
CHANGED
|
@@ -67,8 +67,9 @@ Commit the generated `package-lock.json` after the first install.
|
|
|
67
67
|
2. Edit `site/theme.md` for site-wide layout, layout density, typography
|
|
68
68
|
rhythm, image sizing, font, typography preset, colors, inline styles, and
|
|
69
69
|
frame colors, or omit it to use engine defaults.
|
|
70
|
-
3. Edit `site/content.md` for homepage title, description, sections,
|
|
71
|
-
image
|
|
70
|
+
3. Edit `site/content.md` for homepage title, description, Markdown sections,
|
|
71
|
+
text, Norna image blocks, page/section presentation overrides, alt text, and
|
|
72
|
+
captions.
|
|
72
73
|
4. Put source images under `site/images/<section-id>/`.
|
|
73
74
|
5. Add optional route pages under
|
|
74
75
|
`site/routes/<NNN-route-id>/route-content.md`, for example
|
|
@@ -104,7 +105,7 @@ site: {
|
|
|
104
105
|
```
|
|
105
106
|
|
|
106
107
|
After that, content files may still use root-style internal links such as
|
|
107
|
-
`/getting-started/` and `/
|
|
108
|
+
`/getting-started/` and `/favicon.svg`. Norna applies `site.basePath` during
|
|
108
109
|
rendering so the built links work under the GitHub Pages project path.
|
|
109
110
|
|
|
110
111
|
The starter includes a GitHub Pages workflow in `.github/workflows/deploy.yml`.
|