@janga/norna 0.7.9 → 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 +14 -11
- package/docs/configuration.md +9 -54
- package/docs/content.md +6 -7
- package/docs/design/norna-diagram-design.md +2 -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 +0 -717
- package/examples/typography-presets/.astro/collections/sitewide.schema.json +114 -0
- package/examples/typography-presets/.astro/collections/theme.schema.json +161 -59
- package/examples/typography-presets/.astro/content.d.ts +8 -0
- package/examples/typography-presets/.astro/data-store.json +1 -1
- package/examples/typography-presets/.astro/dev-local.json +2 -2
- package/examples/typography-presets/.astro/dev.json +2 -2
- package/examples/typography-presets/.astro/dev.log +37 -16
- package/examples/typography-presets/site/.norna/generated-images.json +76 -76
- package/examples/typography-presets/site/config.mjs +0 -13
- package/examples/typography-presets/site/content.md +8 -126
- 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/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/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/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 +9 -9
- 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 -118
- package/scripts/lib/site-paths.mjs +2 -0
- package/scripts/lib/sitewide-config.mjs +34 -0
- package/scripts/lib/theme-config.mjs +72 -8
- package/scripts/lib/typography.mjs +20 -26
- package/scripts/show-typography.mjs +40 -147
- package/scripts/sync-content-sections.mjs +19 -17
- package/scripts/test-content-check.mjs +59 -27
- package/scripts/test-content-model-v2.mjs +154 -0
- package/scripts/test-engine-commands.mjs +3 -1
- package/scripts/test-package-check.mjs +66 -24
- 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 +193 -35
- 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 +235 -51
- 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
- /package/examples/typography-presets/site/{images → routes/010-quiet-gallery/images}/quiet-gallery/puppy-portrait.jpg +0 -0
- /package/examples/typography-presets/site/{images → routes/010-quiet-gallery/images}/quiet-gallery/shepherd-portrait.jpg +0 -0
- /package/examples/typography-presets/site/{images → routes/010-quiet-gallery/images}/quiet-gallery/white-puppy.jpg +0 -0
- /package/examples/typography-presets/site/{images → routes/020-compact-gallery/images}/compact-gallery/black-schnauzer.jpg +0 -0
- /package/examples/typography-presets/site/{images → routes/020-compact-gallery/images}/compact-gallery/retriever-portrait.jpg +0 -0
- /package/examples/typography-presets/site/{images → routes/020-compact-gallery/images}/compact-gallery/schnauzer-portrait.jpg +0 -0
- /package/examples/typography-presets/site/{images → routes/030-text-forward/images}/text-forward/boxer-portrait.jpg +0 -0
- /package/examples/typography-presets/site/{images → routes/030-text-forward/images}/text-forward/grass-puppy.jpg +0 -0
- /package/examples/typography-presets/site/{images → routes/030-text-forward/images}/text-forward/grey-street-dog.jpg +0 -0
- /package/examples/typography-presets/site/{images → routes/040-statement/images}/statement/black-pedigree.jpg +0 -0
- /package/examples/typography-presets/site/{images → routes/040-statement/images}/statement/sheepdog-grass.jpg +0 -0
- /package/examples/typography-presets/site/{images → routes/040-statement/images}/statement/sheepdog-wide.jpg +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
import projectConfig from '../../scripts/lib/project-config.mjs';
|
|
3
3
|
import { withBasePath } from '../lib/basePath';
|
|
4
|
-
import type {
|
|
4
|
+
import type { SectionNavigation } from '../lib/sectionContent';
|
|
5
5
|
import type { SitePage } from '../lib/sitePages';
|
|
6
6
|
|
|
7
7
|
type FrameColors = {
|
|
@@ -11,13 +11,20 @@ type FrameColors = {
|
|
|
11
11
|
|
|
12
12
|
interface Props {
|
|
13
13
|
brandLabel: string;
|
|
14
|
+
logo?: {
|
|
15
|
+
src: string;
|
|
16
|
+
alt: string;
|
|
17
|
+
height?: string;
|
|
18
|
+
};
|
|
14
19
|
currentPage: SitePage;
|
|
15
|
-
|
|
16
|
-
|
|
20
|
+
pageNavigation: Array<{
|
|
21
|
+
page: SitePage;
|
|
22
|
+
sections: SectionNavigation[];
|
|
23
|
+
}>;
|
|
17
24
|
frameColors: FrameColors;
|
|
18
25
|
}
|
|
19
26
|
|
|
20
|
-
const { brandLabel,
|
|
27
|
+
const { brandLabel, logo, currentPage, pageNavigation, frameColors } = Astro.props;
|
|
21
28
|
const smoothScrollConfig = projectConfig.navigation.smoothScroll;
|
|
22
29
|
const siteTopBackgroundColor = frameColors.backgroundColor === 'transparent'
|
|
23
30
|
? 'var(--color-page)'
|
|
@@ -25,12 +32,15 @@ const siteTopBackgroundColor = frameColors.backgroundColor === 'transparent'
|
|
|
25
32
|
const navigationStyle = [
|
|
26
33
|
`--site-top-background-color: ${siteTopBackgroundColor}`,
|
|
27
34
|
`--site-top-text-color: ${frameColors.textColor}`,
|
|
35
|
+
...(logo?.height ? [`--site-brand-logo-height: ${logo.height}`] : []),
|
|
28
36
|
].join('; ');
|
|
29
|
-
const showSiteNavigation =
|
|
30
|
-
const
|
|
31
|
-
const
|
|
37
|
+
const showSiteNavigation = pageNavigation.length > 1;
|
|
38
|
+
const homePage = pageNavigation.find(({ page }) => page.isHome)?.page ?? currentPage;
|
|
39
|
+
const currentPageNavigation = pageNavigation.find(({ page }) => page.pathname === currentPage.pathname);
|
|
32
40
|
const isCurrentPage = (page: SitePage) => page.pathname === currentPage.pathname;
|
|
33
41
|
const pageHref = (page: SitePage) => withBasePath(projectConfig.site.basePath, page.pathname);
|
|
42
|
+
const sectionHref = (page: SitePage, section: SectionNavigation) => `${pageHref(page)}#${section.id}`;
|
|
43
|
+
const hasSectionMenu = (pageSections: SectionNavigation[]) => pageSections.length > 1;
|
|
34
44
|
---
|
|
35
45
|
|
|
36
46
|
<a class="skip-link" href="#main-content">{projectConfig.locale.labels.skipToContent}</a>
|
|
@@ -42,15 +52,30 @@ const pageHref = (page: SitePage) => withBasePath(projectConfig.site.basePath, p
|
|
|
42
52
|
>
|
|
43
53
|
{showSiteNavigation && (
|
|
44
54
|
<div class="site-nav-row">
|
|
45
|
-
<a class="site-brand" href={pageHref(homePage)}>
|
|
55
|
+
<a class="site-brand" href={pageHref(homePage)}>
|
|
56
|
+
{logo ? <img class="site-brand-logo" src={withBasePath(projectConfig.site.basePath, logo.src)} alt={logo.alt} /> : brandLabel}
|
|
57
|
+
</a>
|
|
46
58
|
|
|
47
59
|
<nav class="site-nav" aria-label={projectConfig.locale.labels.siteNavigation}>
|
|
48
60
|
<ul>
|
|
49
|
-
{
|
|
50
|
-
<li>
|
|
61
|
+
{pageNavigation.map(({ page, sections: pageSections }) => (
|
|
62
|
+
<li class:list={['site-nav-item', { 'site-nav-item-has-sections': hasSectionMenu(pageSections) }]}>
|
|
51
63
|
<a href={pageHref(page)} aria-current={isCurrentPage(page) ? 'page' : undefined}>
|
|
52
64
|
{page.navigation.label}
|
|
53
65
|
</a>
|
|
66
|
+
{hasSectionMenu(pageSections) && (
|
|
67
|
+
<div class="site-nav-submenu">
|
|
68
|
+
<nav aria-label={`${page.navigation.label} ${projectConfig.locale.labels.pageNavigation}`}>
|
|
69
|
+
<ul>
|
|
70
|
+
{pageSections.map((section) => (
|
|
71
|
+
<li>
|
|
72
|
+
<a href={sectionHref(page, section)}>{section.title}</a>
|
|
73
|
+
</li>
|
|
74
|
+
))}
|
|
75
|
+
</ul>
|
|
76
|
+
</nav>
|
|
77
|
+
</div>
|
|
78
|
+
)}
|
|
54
79
|
</li>
|
|
55
80
|
))}
|
|
56
81
|
</ul>
|
|
@@ -64,7 +89,7 @@ const pageHref = (page: SitePage) => withBasePath(projectConfig.site.basePath, p
|
|
|
64
89
|
<nav class="mobile-site-nav" aria-label={projectConfig.locale.labels.siteNavigation}>
|
|
65
90
|
<h2>{projectConfig.locale.labels.siteNavigation}</h2>
|
|
66
91
|
<ul>
|
|
67
|
-
{
|
|
92
|
+
{pageNavigation.map(({ page }) => (
|
|
68
93
|
<li>
|
|
69
94
|
<a href={pageHref(page)} aria-current={isCurrentPage(page) ? 'page' : undefined}>
|
|
70
95
|
{page.navigation.label}
|
|
@@ -74,13 +99,13 @@ const pageHref = (page: SitePage) => withBasePath(projectConfig.site.basePath, p
|
|
|
74
99
|
</ul>
|
|
75
100
|
</nav>
|
|
76
101
|
|
|
77
|
-
{
|
|
102
|
+
{currentPageNavigation && hasSectionMenu(currentPageNavigation.sections) && (
|
|
78
103
|
<nav class="mobile-page-nav" aria-label={projectConfig.locale.labels.pageNavigation}>
|
|
79
104
|
<h2>{projectConfig.locale.labels.pageNavigation}</h2>
|
|
80
105
|
<ul>
|
|
81
|
-
{sections.map((section, index) => (
|
|
106
|
+
{currentPageNavigation.sections.map((section, index) => (
|
|
82
107
|
<li>
|
|
83
|
-
<a href={
|
|
108
|
+
<a href={sectionHref(currentPage, section)} aria-current={index === 0 ? 'true' : undefined}>
|
|
84
109
|
{section.title}
|
|
85
110
|
</a>
|
|
86
111
|
</li>
|
|
@@ -93,25 +118,12 @@ const pageHref = (page: SitePage) => withBasePath(projectConfig.site.basePath, p
|
|
|
93
118
|
</div>
|
|
94
119
|
)}
|
|
95
120
|
|
|
96
|
-
{showPageNavigation && (
|
|
97
|
-
<nav class="page-nav" aria-label={projectConfig.locale.labels.pageNavigation}>
|
|
98
|
-
<ul>
|
|
99
|
-
{sections.map((section, index) => (
|
|
100
|
-
<li>
|
|
101
|
-
<a href={`#${section.id}`} aria-current={index === 0 ? 'true' : undefined}>
|
|
102
|
-
{section.title}
|
|
103
|
-
</a>
|
|
104
|
-
</li>
|
|
105
|
-
))}
|
|
106
|
-
</ul>
|
|
107
|
-
</nav>
|
|
108
|
-
)}
|
|
109
121
|
</header>
|
|
110
122
|
|
|
111
123
|
<script>
|
|
112
124
|
const siteTop = document.querySelector('.site-top');
|
|
113
125
|
const sections = Array.from(document.querySelectorAll('.site-section'));
|
|
114
|
-
const links = Array.from(document.querySelectorAll('.
|
|
126
|
+
const links = Array.from(document.querySelectorAll('.site-nav-submenu a, .mobile-page-nav a'));
|
|
115
127
|
const mobileMenu = document.querySelector('.mobile-nav-menu');
|
|
116
128
|
const smoothScrollConfig = JSON.parse(siteTop?.getAttribute('data-smooth-scroll') ?? '{}');
|
|
117
129
|
const anchorPadding = 0;
|
|
@@ -159,7 +171,9 @@ const pageHref = (page: SitePage) => withBasePath(projectConfig.site.basePath, p
|
|
|
159
171
|
activeSectionId = section.id;
|
|
160
172
|
|
|
161
173
|
links.forEach((link) => {
|
|
162
|
-
const
|
|
174
|
+
const linkUrl = new URL(link.href, window.location.href);
|
|
175
|
+
const isCurrentPageLink = linkUrl.pathname === window.location.pathname;
|
|
176
|
+
const isActive = isCurrentPageLink && linkUrl.hash === `#${section.id}`;
|
|
163
177
|
if (isActive) {
|
|
164
178
|
link.setAttribute('aria-current', 'true');
|
|
165
179
|
} else {
|
|
@@ -372,7 +386,10 @@ const pageHref = (page: SitePage) => withBasePath(projectConfig.site.basePath, p
|
|
|
372
386
|
|
|
373
387
|
links.forEach((link) => {
|
|
374
388
|
link.addEventListener('click', (event) => {
|
|
375
|
-
const
|
|
389
|
+
const linkUrl = new URL(link.href, window.location.href);
|
|
390
|
+
if (linkUrl.pathname !== window.location.pathname) return;
|
|
391
|
+
|
|
392
|
+
const section = getSectionFromHash(linkUrl.hash);
|
|
376
393
|
if (!(section instanceof HTMLElement)) return;
|
|
377
394
|
|
|
378
395
|
const top = getSectionScrollTop(section);
|
|
@@ -380,7 +397,7 @@ const pageHref = (page: SitePage) => withBasePath(projectConfig.site.basePath, p
|
|
|
380
397
|
|
|
381
398
|
event.preventDefault();
|
|
382
399
|
|
|
383
|
-
const targetHash =
|
|
400
|
+
const targetHash = linkUrl.hash;
|
|
384
401
|
const duration = getScrollDuration(top - window.scrollY);
|
|
385
402
|
|
|
386
403
|
setHash(targetHash);
|
|
@@ -3,12 +3,16 @@ import { getCollection, type CollectionEntry } from 'astro:content';
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import BaseLayout from '../layouts/BaseLayout.astro';
|
|
5
5
|
import SiteNavigation from './SiteNavigation.astro';
|
|
6
|
+
import SiteBanners from './SiteBanners.astro';
|
|
6
7
|
import SiteSection from './SiteSection.astro';
|
|
7
|
-
import { getSectionsContent } from '../lib/sectionContent';
|
|
8
|
+
import { getSectionNavigation, getSectionsContent } from '../lib/sectionContent';
|
|
9
|
+
import { getRouteTheme } from '../lib/routeThemes';
|
|
8
10
|
import { getNavigationPages, getSitePage, getSitePages } from '../lib/sitePages';
|
|
9
11
|
import { getToday, isVisibleToday } from '../lib/visibility';
|
|
12
|
+
import { getLogoAsset } from '../lib/sitePublicAssets';
|
|
10
13
|
import { readSiteFile } from '../../scripts/lib/site-content.mjs';
|
|
11
14
|
import { siteContentPath, siteRoutesDir } from '../../scripts/lib/site-paths.mjs';
|
|
15
|
+
import { resolveThemeVisualConfig } from '../../scripts/lib/project-config.mjs';
|
|
12
16
|
import { resolveFrameColors, resolvePagePresentation } from '../../scripts/lib/presentation.mjs';
|
|
13
17
|
|
|
14
18
|
type SiteEntry = CollectionEntry<'site'>;
|
|
@@ -28,23 +32,55 @@ const pageContentPath = currentPage.routeDirectory
|
|
|
28
32
|
? path.join(siteRoutesDir, currentPage.routeDirectory, 'route-content.md')
|
|
29
33
|
: siteContentPath;
|
|
30
34
|
const rawMarkdown = (await readSiteFile(pageContentPath, siteContentLabel)).body;
|
|
31
|
-
const [
|
|
35
|
+
const [siteTheme] = await getCollection('theme');
|
|
36
|
+
if (!siteTheme) {
|
|
37
|
+
throw new Error('site/theme.md is required. Add a root theme before building the site.');
|
|
38
|
+
}
|
|
39
|
+
const [sitewide] = await getCollection('sitewide');
|
|
40
|
+
const sitewideData = sitewide?.data ?? {};
|
|
32
41
|
const navigationPages = getNavigationPages(sitePages);
|
|
33
42
|
const homePage = sitePages.find((page) => page.isHome) ?? currentPage;
|
|
34
|
-
const
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
43
|
+
const siteThemeData = siteTheme.data;
|
|
44
|
+
const routeTheme = await getRouteTheme(currentPage.routeDirectory);
|
|
45
|
+
const themeData = routeTheme?.data ?? siteThemeData;
|
|
46
|
+
const themeLabel = routeTheme
|
|
47
|
+
? `${siteDirectory || 'site'}/${routeTheme.id}`
|
|
48
|
+
: `${siteDirectory || 'site'}/theme.md`;
|
|
49
|
+
const visualTheme = resolveThemeVisualConfig(themeData, themeLabel);
|
|
50
|
+
const brandLabel = sitewideData.navigation?.brand ?? homePage.title;
|
|
51
|
+
const logoAsset = getLogoAsset();
|
|
52
|
+
const logoSettings = sitewideData.navigation?.logo;
|
|
53
|
+
const logo = logoAsset
|
|
54
|
+
? {
|
|
55
|
+
src: logoAsset.href,
|
|
56
|
+
alt: logoSettings?.alt ?? brandLabel,
|
|
57
|
+
height: logoSettings?.height,
|
|
58
|
+
}
|
|
59
|
+
: undefined;
|
|
60
|
+
const pagePresentation = resolvePagePresentation(themeData);
|
|
61
|
+
const frameColors = resolveFrameColors({ theme: themeData });
|
|
42
62
|
const sections = entry.data.sections;
|
|
43
63
|
const siteHtml = entry.rendered?.html ?? '';
|
|
44
|
-
const resolvedSections = getSectionsContent(siteHtml, rawMarkdown, sections, currentPage
|
|
64
|
+
const resolvedSections = await getSectionsContent(siteHtml, rawMarkdown, sections, currentPage);
|
|
45
65
|
const today = getToday();
|
|
66
|
+
const siteBanners = (sitewideData.banners ?? []).filter((banner) => isVisibleToday(banner.visible, today));
|
|
46
67
|
const visibleSections = resolvedSections.filter((section) => isVisibleToday(section.visible, today));
|
|
47
68
|
|
|
69
|
+
const getPageContent = async (page: SitePage) => {
|
|
70
|
+
if (page.pathname === currentPage.pathname) {
|
|
71
|
+
return visibleSections.map(({ id, title }) => ({ id, title }));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const pageSections = getSectionNavigation(page.entry.rendered?.html ?? '', page.entry.data.sections);
|
|
75
|
+
|
|
76
|
+
return pageSections.filter((section) => isVisibleToday(page.entry.data.sections[section.id]?.visible, today));
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const pageNavigation = await Promise.all(navigationPages.map(async (page) => ({
|
|
80
|
+
page,
|
|
81
|
+
sections: await getPageContent(page),
|
|
82
|
+
})));
|
|
83
|
+
|
|
48
84
|
if (visibleSections.length === 0) {
|
|
49
85
|
throw new Error(`No sections are visible for ${today}. Check sections.<section-id>.visible in ${siteContentLabel}.`);
|
|
50
86
|
}
|
|
@@ -53,22 +89,28 @@ if (visibleSections.length === 0) {
|
|
|
53
89
|
<BaseLayout
|
|
54
90
|
title={entry.data.title}
|
|
55
91
|
description={entry.data.description}
|
|
92
|
+
presentation={pagePresentation}
|
|
93
|
+
visualTheme={visualTheme}
|
|
56
94
|
frameColors={frameColors}
|
|
57
95
|
pathname={currentPage.pathname}
|
|
96
|
+
footer={sitewideData.footer}
|
|
58
97
|
>
|
|
59
98
|
<SiteNavigation
|
|
60
99
|
brandLabel={brandLabel}
|
|
100
|
+
logo={logo}
|
|
61
101
|
currentPage={currentPage}
|
|
62
|
-
|
|
63
|
-
sections={visibleSections}
|
|
102
|
+
pageNavigation={pageNavigation}
|
|
64
103
|
frameColors={frameColors}
|
|
65
104
|
/>
|
|
105
|
+
<SiteBanners banners={siteBanners} />
|
|
66
106
|
|
|
67
107
|
<main id="main-content" class="site-content">
|
|
68
108
|
{visibleSections.map((section, index) => (
|
|
69
109
|
<SiteSection
|
|
70
110
|
section={section}
|
|
71
111
|
pagePresentation={pagePresentation}
|
|
112
|
+
visualTheme={visualTheme}
|
|
113
|
+
sectionIndex={index}
|
|
72
114
|
headingLevel={index === 0 ? 1 : 2}
|
|
73
115
|
priorityGalleryImage={index === 0}
|
|
74
116
|
/>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
2
|
+
import projectConfig from '../../scripts/lib/project-config.mjs';
|
|
3
|
+
import { resolveSectionSurface } from '../../scripts/lib/presentation.mjs';
|
|
4
4
|
import CardList from './CardList.astro';
|
|
5
5
|
import GalleryGrid from './GalleryGrid.astro';
|
|
6
6
|
import type { ResolvedSection } from '../lib/sectionContent';
|
|
@@ -9,31 +9,44 @@ type ResponsiveValue<T> = { desktop: T; mobile: T };
|
|
|
9
9
|
type ResponsiveOverride<T> = { desktop?: T; mobile?: T };
|
|
10
10
|
type TextAlign = 'left' | 'center' | 'right';
|
|
11
11
|
type TextSize = 'small' | 'medium' | 'large' | 'xlarge';
|
|
12
|
-
type
|
|
13
|
-
backgroundColor?: string;
|
|
14
|
-
textColor?: string;
|
|
15
|
-
typography?: NonNullable<ResolvedSection['presentation']>['typography'];
|
|
16
|
-
};
|
|
12
|
+
type TextWidth = 'narrow' | 'normal' | 'wide';
|
|
17
13
|
type ResolvedPagePresentation = {
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
palette: {
|
|
15
|
+
surfaces: Record<'base' | 'soft' | 'emphasis', { backgroundColor: string; textColor: string }>;
|
|
16
|
+
};
|
|
17
|
+
sectionSurfaces: {
|
|
18
|
+
mode: 'none' | 'cycle';
|
|
19
|
+
sequence: Array<'base' | 'soft' | 'emphasis'>;
|
|
20
|
+
};
|
|
20
21
|
typography: {
|
|
21
22
|
preset: string;
|
|
22
23
|
values: any;
|
|
23
24
|
};
|
|
24
25
|
};
|
|
26
|
+
type VisualTheme = {
|
|
27
|
+
gallery: {
|
|
28
|
+
maxAvailableWidthPercent: {
|
|
29
|
+
desktop: number;
|
|
30
|
+
mobile: number;
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
};
|
|
25
34
|
|
|
26
35
|
interface Props {
|
|
27
36
|
section: ResolvedSection;
|
|
28
37
|
pagePresentation: ResolvedPagePresentation;
|
|
38
|
+
visualTheme: VisualTheme;
|
|
39
|
+
sectionIndex: number;
|
|
29
40
|
headingLevel: 1 | 2;
|
|
30
41
|
priorityGalleryImage?: boolean;
|
|
31
42
|
}
|
|
32
43
|
|
|
33
|
-
const { section, pagePresentation, headingLevel, priorityGalleryImage = false } = Astro.props;
|
|
44
|
+
const { section, pagePresentation, visualTheme, sectionIndex, headingLevel, priorityGalleryImage = false } = Astro.props;
|
|
34
45
|
const HeadingTag = headingLevel === 1 ? 'h1' : 'h2';
|
|
35
46
|
// Sections are authored with Markdown ## headings even when the first one renders as HTML h1.
|
|
36
47
|
const sectionHeadingVisualLevel = 'h2';
|
|
48
|
+
const firstContentBlockIsVisual = section.contentBlocks[0]?.type !== 'html'
|
|
49
|
+
&& section.contentBlocks[0]?.type !== 'note';
|
|
37
50
|
type HeadingLevel = 'h1' | 'h2' | 'h3' | 'h4';
|
|
38
51
|
const headingSizeValues: Record<HeadingLevel, Record<TextSize, ResponsiveValue<string>>> = {
|
|
39
52
|
h1: {
|
|
@@ -145,7 +158,6 @@ const captionSizeValues: Record<TextSize, ResponsiveValue<string>> = {
|
|
|
145
158
|
mobile: 'clamp(1.04rem, 0.98rem + 0.18vw, 1.12rem)',
|
|
146
159
|
},
|
|
147
160
|
};
|
|
148
|
-
const sectionOverride = section.presentation as SectionPresentationOverride | undefined;
|
|
149
161
|
const getSizeValue = (
|
|
150
162
|
sizeValues: Record<TextSize, ResponsiveValue<string>>,
|
|
151
163
|
size: TextSize,
|
|
@@ -153,15 +165,17 @@ const getSizeValue = (
|
|
|
153
165
|
const getHeadingSizeValue = (level: HeadingLevel, size: TextSize) => (
|
|
154
166
|
headingSizeValues[level][size]
|
|
155
167
|
);
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
const
|
|
168
|
+
const textWidthValues: Record<TextWidth, string> = {
|
|
169
|
+
narrow: 'min(60ch, var(--text-width))',
|
|
170
|
+
normal: 'min(72ch, var(--text-width))',
|
|
171
|
+
wide: 'min(72ch, var(--gallery-layout-width))',
|
|
172
|
+
};
|
|
173
|
+
const surface = resolveSectionSurface(pagePresentation, sectionIndex);
|
|
174
|
+
const typography = pagePresentation.typography.values;
|
|
162
175
|
const heading = typography.headings[sectionHeadingVisualLevel] as {
|
|
163
176
|
align: ResponsiveValue<TextAlign>;
|
|
164
177
|
size: TextSize;
|
|
178
|
+
weight: number;
|
|
165
179
|
lineHeight: number;
|
|
166
180
|
spacingBefore: string;
|
|
167
181
|
spacingAfter: string;
|
|
@@ -172,6 +186,7 @@ const markdownHeadings = Object.fromEntries(markdownHeadingLevels.map((level) =>
|
|
|
172
186
|
typography.headings[level] as {
|
|
173
187
|
align: ResponsiveValue<TextAlign>;
|
|
174
188
|
size: TextSize;
|
|
189
|
+
weight: number;
|
|
175
190
|
lineHeight: number;
|
|
176
191
|
spacingBefore: string;
|
|
177
192
|
spacingAfter: string;
|
|
@@ -188,20 +203,54 @@ const justifyByAlign: Record<TextAlign, string> = {
|
|
|
188
203
|
center: 'center',
|
|
189
204
|
right: 'flex-end',
|
|
190
205
|
};
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
206
|
+
const bodyWidthValue = textWidthValues[typography.body.width as TextWidth];
|
|
207
|
+
const headingWidth = {
|
|
208
|
+
desktop: bodyWidthValue,
|
|
209
|
+
mobile: bodyWidthValue,
|
|
210
|
+
};
|
|
211
|
+
const bodyWidth = {
|
|
212
|
+
desktop: bodyWidthValue,
|
|
213
|
+
mobile: bodyWidthValue,
|
|
214
|
+
};
|
|
215
|
+
const isMediaBlock = (block: ResolvedSection['contentBlocks'][number]) => (
|
|
216
|
+
block.type === 'image-stack'
|
|
217
|
+
|| block.type === 'image-carousel'
|
|
218
|
+
|| block.type === 'card-list'
|
|
219
|
+
);
|
|
200
220
|
const hasMedia = section.contentBlocks.some((block) => (
|
|
201
221
|
block.type === 'image-stack'
|
|
202
222
|
|| block.type === 'image-carousel'
|
|
203
223
|
|| (block.type === 'card-list' && block.cards.some((card) => card.image))
|
|
204
224
|
));
|
|
225
|
+
const hasMarginNotes = section.contentBlocks.some((block) => (
|
|
226
|
+
block.type === 'note'
|
|
227
|
+
));
|
|
228
|
+
const getNoteGridRow = (blocks: ResolvedSection['contentBlocks'], blockIndex: number) => Math.max(
|
|
229
|
+
1,
|
|
230
|
+
blocks.slice(0, blockIndex).filter((block) => block.type === 'html').length,
|
|
231
|
+
);
|
|
232
|
+
const noteLabel = projectConfig.locale.labels.note;
|
|
233
|
+
const contentGroups: Array<
|
|
234
|
+
| { type: 'reading'; blocks: ResolvedSection['contentBlocks'] }
|
|
235
|
+
| { type: 'media'; block: Extract<ResolvedSection['contentBlocks'][number], { type: 'image-stack' | 'image-carousel' | 'card-list' }> }
|
|
236
|
+
> = [];
|
|
237
|
+
let readingBlocks: ResolvedSection['contentBlocks'] = [];
|
|
238
|
+
const flushReadingBlocks = () => {
|
|
239
|
+
if (readingBlocks.length === 0) return;
|
|
240
|
+
contentGroups.push({ type: 'reading', blocks: readingBlocks });
|
|
241
|
+
readingBlocks = [];
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
for (const block of section.contentBlocks) {
|
|
245
|
+
if (isMediaBlock(block)) {
|
|
246
|
+
flushReadingBlocks();
|
|
247
|
+
contentGroups.push({ type: 'media', block });
|
|
248
|
+
} else {
|
|
249
|
+
readingBlocks = [...readingBlocks, block];
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
flushReadingBlocks();
|
|
205
254
|
const markdownHeadingStyle = (level: 'h3' | 'h4') => {
|
|
206
255
|
const headingConfig = markdownHeadings[level];
|
|
207
256
|
const fontSize = getHeadingSizeValue(level, headingConfig.size);
|
|
@@ -211,18 +260,20 @@ const markdownHeadingStyle = (level: 'h3' | 'h4') => {
|
|
|
211
260
|
`--section-markdown-${level}-align-mobile: ${headingConfig.align.mobile}`,
|
|
212
261
|
`--section-markdown-${level}-font-size-desktop: ${fontSize.desktop}`,
|
|
213
262
|
`--section-markdown-${level}-font-size-mobile: ${fontSize.mobile}`,
|
|
263
|
+
`--section-markdown-${level}-font-weight: ${headingConfig.weight}`,
|
|
214
264
|
`--section-markdown-${level}-line-height: ${headingConfig.lineHeight}`,
|
|
215
265
|
`--section-markdown-${level}-spacing-before: ${headingConfig.spacingBefore}`,
|
|
216
266
|
`--section-markdown-${level}-spacing-after: ${headingConfig.spacingAfter}`,
|
|
217
267
|
];
|
|
218
268
|
};
|
|
219
269
|
const sectionStyle = [
|
|
220
|
-
`--section-background-color: ${backgroundColor}`,
|
|
221
|
-
`--section-text-color: ${textColor}`,
|
|
270
|
+
`--section-background-color: ${surface.backgroundColor}`,
|
|
271
|
+
`--section-text-color: ${surface.textColor}`,
|
|
222
272
|
`--section-heading-align-desktop: ${headingAlign.desktop}`,
|
|
223
273
|
`--section-heading-align-mobile: ${headingAlign.mobile}`,
|
|
224
274
|
`--section-heading-font-size-desktop: ${headingFontSize.desktop}`,
|
|
225
275
|
`--section-heading-font-size-mobile: ${headingFontSize.mobile}`,
|
|
276
|
+
`--section-heading-font-weight: ${heading.weight}`,
|
|
226
277
|
`--section-heading-line-height: ${heading.lineHeight}`,
|
|
227
278
|
`--section-heading-spacing-after: ${heading.spacingAfter}`,
|
|
228
279
|
`--section-heading-width-desktop: ${headingWidth.desktop}`,
|
|
@@ -250,7 +301,13 @@ const sectionStyle = [
|
|
|
250
301
|
|
|
251
302
|
<section
|
|
252
303
|
id={section.id}
|
|
253
|
-
class=
|
|
304
|
+
class:list={[
|
|
305
|
+
'site-section',
|
|
306
|
+
{
|
|
307
|
+
'site-section-first-block-is-visual': firstContentBlockIsVisual,
|
|
308
|
+
'site-section-has-margin-notes': hasMarginNotes,
|
|
309
|
+
},
|
|
310
|
+
]}
|
|
254
311
|
data-section-title={section.title}
|
|
255
312
|
style={sectionStyle}
|
|
256
313
|
>
|
|
@@ -260,33 +317,134 @@ const sectionStyle = [
|
|
|
260
317
|
</header>
|
|
261
318
|
|
|
262
319
|
<div class:list={['section-body', { 'section-body-has-gallery': hasMedia, 'section-body-text-only': !hasMedia }]}>
|
|
263
|
-
{
|
|
264
|
-
if (
|
|
265
|
-
|
|
320
|
+
{contentGroups.map((group, groupIndex) => {
|
|
321
|
+
if (group.type === 'reading') {
|
|
322
|
+
if (hasMarginNotes && group.blocks.some((block) => block.type === 'note')) {
|
|
323
|
+
return (
|
|
324
|
+
<div class="section-reading-layout">
|
|
325
|
+
{group.blocks.map((block, blockIndex) => {
|
|
326
|
+
if (block.type === 'html') {
|
|
327
|
+
return block.html && <div class="section-markdown" set:html={block.html} />;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const noteGridRow = getNoteGridRow(group.blocks, blockIndex);
|
|
331
|
+
return (
|
|
332
|
+
<aside
|
|
333
|
+
class="section-note section-note-margin"
|
|
334
|
+
id={block.id}
|
|
335
|
+
aria-label={`${noteLabel}${block.number ? ` ${block.number}` : ''}`}
|
|
336
|
+
role="note"
|
|
337
|
+
data-note-id={block.id}
|
|
338
|
+
style={`grid-row: ${noteGridRow};`}
|
|
339
|
+
>
|
|
340
|
+
{block.number && block.referenceId && (
|
|
341
|
+
<a class="section-note-number" href={`#${block.referenceId}`} aria-label={`${noteLabel} ${block.number}`}>
|
|
342
|
+
{block.number}
|
|
343
|
+
</a>
|
|
344
|
+
)}
|
|
345
|
+
<div class="section-note-content" set:html={block.html} />
|
|
346
|
+
</aside>
|
|
347
|
+
);
|
|
348
|
+
})}
|
|
349
|
+
</div>
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return group.blocks.map((block, blockIndex) => {
|
|
354
|
+
if (block.type === 'html') {
|
|
355
|
+
return block.html && <div class="section-markdown" set:html={block.html} />;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const noteGridRow = getNoteGridRow(group.blocks, blockIndex);
|
|
359
|
+
return (
|
|
360
|
+
<aside
|
|
361
|
+
class="section-note section-note-margin"
|
|
362
|
+
id={block.id}
|
|
363
|
+
aria-label={`${noteLabel}${block.number ? ` ${block.number}` : ''}`}
|
|
364
|
+
role="note"
|
|
365
|
+
data-note-id={block.id}
|
|
366
|
+
style={`grid-row: ${noteGridRow};`}
|
|
367
|
+
>
|
|
368
|
+
{block.number && block.referenceId && (
|
|
369
|
+
<a class="section-note-number" href={`#${block.referenceId}`} aria-label={`${noteLabel} ${block.number}`}>
|
|
370
|
+
{block.number}
|
|
371
|
+
</a>
|
|
372
|
+
)}
|
|
373
|
+
<div class="section-note-content" set:html={block.html} />
|
|
374
|
+
</aside>
|
|
375
|
+
);
|
|
376
|
+
});
|
|
266
377
|
}
|
|
267
378
|
|
|
379
|
+
const block = group.block;
|
|
268
380
|
if (block.type === 'image-carousel') {
|
|
269
381
|
return (
|
|
270
382
|
<GalleryGrid
|
|
271
383
|
title={section.title}
|
|
272
384
|
items={[{ carousel: block.images }]}
|
|
273
|
-
|
|
385
|
+
maxAvailableWidthPercent={visualTheme.gallery.maxAvailableWidthPercent}
|
|
386
|
+
priorityFirstImage={priorityGalleryImage && groupIndex === 0}
|
|
274
387
|
/>
|
|
275
388
|
);
|
|
276
389
|
}
|
|
277
390
|
|
|
278
391
|
if (block.type === 'card-list') {
|
|
279
|
-
return <CardList cards={block.cards} layout={block.layout} flow={block.flow} size={block.size} width={block.width} />;
|
|
392
|
+
return <CardList cards={block.cards} maxAvailableWidthPercent={visualTheme.gallery.maxAvailableWidthPercent} layout={block.layout} flow={block.flow} size={block.size} width={block.width} />;
|
|
280
393
|
}
|
|
281
394
|
|
|
282
395
|
return (
|
|
283
396
|
<GalleryGrid
|
|
284
397
|
title={section.title}
|
|
285
398
|
items={block.images}
|
|
286
|
-
|
|
399
|
+
maxAvailableWidthPercent={visualTheme.gallery.maxAvailableWidthPercent}
|
|
400
|
+
priorityFirstImage={priorityGalleryImage && groupIndex === 0}
|
|
287
401
|
/>
|
|
288
402
|
);
|
|
289
403
|
})}
|
|
290
404
|
</div>
|
|
291
405
|
</div>
|
|
292
406
|
</section>
|
|
407
|
+
|
|
408
|
+
<script>
|
|
409
|
+
const noteParts = Array.from(document.querySelectorAll<HTMLElement>('[data-note-id]'));
|
|
410
|
+
const noteReferences = noteParts.filter((part) => part.matches('.section-note-ref a'));
|
|
411
|
+
const marginNotes = noteParts.filter((part) => part.matches('.section-note'));
|
|
412
|
+
|
|
413
|
+
const setNoteState = (noteId: string, active: boolean) => {
|
|
414
|
+
noteParts
|
|
415
|
+
.filter((part) => part.dataset.noteId === noteId)
|
|
416
|
+
.forEach((part) => part.classList.toggle('is-referenced', active));
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
noteParts.forEach((part) => {
|
|
420
|
+
const noteId = part.dataset.noteId;
|
|
421
|
+
if (!noteId) return;
|
|
422
|
+
|
|
423
|
+
part.addEventListener('pointerenter', () => setNoteState(noteId, true));
|
|
424
|
+
part.addEventListener('pointerleave', () => setNoteState(noteId, false));
|
|
425
|
+
part.addEventListener('focusin', () => setNoteState(noteId, true));
|
|
426
|
+
part.addEventListener('focusout', () => setNoteState(noteId, false));
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
const alignMarginNotes = () => {
|
|
430
|
+
const wideLayout = window.matchMedia('(min-width: 1101px)').matches;
|
|
431
|
+
if (!wideLayout) {
|
|
432
|
+
marginNotes.forEach((note) => note.style.removeProperty('--section-note-offset'));
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
marginNotes.forEach((note) => {
|
|
437
|
+
const noteId = note.dataset.noteId;
|
|
438
|
+
const reference = noteReferences.find((part) => part.dataset.noteId === noteId);
|
|
439
|
+
if (!noteId || !reference) return;
|
|
440
|
+
|
|
441
|
+
note.style.removeProperty('--section-note-offset');
|
|
442
|
+
const offset = reference.getBoundingClientRect().top - note.getBoundingClientRect().top;
|
|
443
|
+
note.style.setProperty('--section-note-offset', `${Math.max(0, offset)}px`);
|
|
444
|
+
});
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
alignMarginNotes();
|
|
448
|
+
window.addEventListener('resize', alignMarginNotes);
|
|
449
|
+
document.fonts?.ready.then(alignMarginNotes);
|
|
450
|
+
</script>
|