@janga/norna 0.7.2 → 0.7.4
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 +16 -16
- package/bin/norna-cli.mjs +12 -7
- package/docs/README.md +11 -12
- package/docs/commands.md +9 -9
- package/docs/configuration.md +14 -145
- package/docs/content.md +17 -15
- package/docs/design/command-organization.md +58 -57
- package/docs/design/site-examples-structure.md +6 -5
- package/docs/engine-development.md +5 -5
- package/docs/getting-started.md +20 -26
- package/docs/images-and-metadata.md +1 -1
- package/docs/local-development.md +2 -2
- package/docs/publishing.md +1 -1
- package/docs/routes.md +2 -2
- package/docs/site-structure.md +8 -7
- package/docs/theme.md +133 -7
- package/docs/typography.md +78 -43
- package/examples/dog-gallery/node_modules/.vite/deps/_metadata.json +74 -0
- package/examples/dog-gallery/node_modules/.vite/deps/astro-D_GIpJIE.js +434 -0
- package/examples/dog-gallery/node_modules/.vite/deps/astro-D_GIpJIE.js.map +1 -0
- package/examples/dog-gallery/node_modules/.vite/deps/astro_n_aria-query.js +6390 -0
- package/examples/dog-gallery/node_modules/.vite/deps/astro_n_aria-query.js.map +1 -0
- package/examples/dog-gallery/node_modules/.vite/deps/astro_n_axobject-query.js +2702 -0
- package/examples/dog-gallery/node_modules/.vite/deps/astro_n_axobject-query.js.map +1 -0
- package/examples/dog-gallery/node_modules/.vite/deps/astro_n_html-escaper.js +65 -0
- package/examples/dog-gallery/node_modules/.vite/deps/astro_n_html-escaper.js.map +1 -0
- package/examples/dog-gallery/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +455 -0
- package/examples/dog-gallery/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +1 -0
- package/examples/dog-gallery/node_modules/.vite/deps/audit-DM9Hkl5B.js +1534 -0
- package/examples/dog-gallery/node_modules/.vite/deps/audit-DM9Hkl5B.js.map +1 -0
- package/examples/dog-gallery/node_modules/.vite/deps/highlight-DnZ1ULOL.js +48 -0
- package/examples/dog-gallery/node_modules/.vite/deps/highlight-DnZ1ULOL.js.map +1 -0
- package/examples/dog-gallery/node_modules/.vite/deps/icons-RyebKi45.js +42 -0
- package/examples/dog-gallery/node_modules/.vite/deps/icons-RyebKi45.js.map +1 -0
- package/examples/dog-gallery/node_modules/.vite/deps/package.json +3 -0
- package/examples/dog-gallery/node_modules/.vite/deps/rolldown-runtime-BvCyGRYZ.js +4 -0
- package/examples/dog-gallery/node_modules/.vite/deps/settings-DaU-gNnK.js +190 -0
- package/examples/dog-gallery/node_modules/.vite/deps/settings-DaU-gNnK.js.map +1 -0
- package/examples/dog-gallery/node_modules/.vite/deps/toolbar-63PA619H.js +458 -0
- package/examples/dog-gallery/node_modules/.vite/deps/toolbar-63PA619H.js.map +1 -0
- package/examples/dog-gallery/node_modules/.vite/deps/ui-library--bkcNSYz.js +1016 -0
- package/examples/dog-gallery/node_modules/.vite/deps/ui-library--bkcNSYz.js.map +1 -0
- package/examples/dog-gallery/node_modules/.vite/deps/window-jPkpKVcy.js +32 -0
- package/examples/dog-gallery/node_modules/.vite/deps/window-jPkpKVcy.js.map +1 -0
- package/examples/dog-gallery/node_modules/.vite/deps/xray-BXCHpEJP.js +111 -0
- package/examples/dog-gallery/node_modules/.vite/deps/xray-BXCHpEJP.js.map +1 -0
- package/examples/dog-gallery/site/config.mjs +12 -42
- package/examples/dog-gallery/site/content.md +28 -6
- package/examples/dog-gallery/site/routes/dog-care/route-content.md +17 -3
- package/examples/dog-gallery/site/theme.md +83 -26
- package/fixtures/basic/site/config.mjs +2 -22
- package/fixtures/basic/site/content.md +1 -1
- package/fixtures/basic/site/theme.md +7 -3
- package/package.json +3 -3
- package/scripts/check-config.mjs +4 -3
- package/scripts/deploy-site.mjs +3 -3
- package/scripts/init-site.mjs +10 -8
- package/scripts/lib/frontmatter-yaml.mjs +85 -0
- package/scripts/lib/presentation.mjs +10 -11
- package/scripts/lib/project-config.mjs +146 -45
- package/scripts/lib/site-content.mjs +21 -3
- package/scripts/lib/theme-config.mjs +34 -0
- package/scripts/lib/typography.mjs +155 -35
- package/scripts/show-typography.mjs +127 -159
- package/scripts/sync-content-sections.mjs +6 -6
- package/scripts/test-content-check.mjs +3 -4
- package/scripts/test-engine-commands.mjs +21 -3
- package/scripts/test-package-check.mjs +34 -17
- package/scripts/test-temporary-visibility.mjs +2 -3
- package/src/components/SitePage.astro +2 -3
- package/src/components/SiteSection.astro +115 -19
- package/src/content.config.ts +66 -6
- package/src/layouts/BaseLayout.astro +11 -0
- package/src/styles/global.css +83 -52
- package/starters/basic/README.md +11 -15
- package/starters/basic/package.json +2 -2
- package/starters/basic/site/config.mjs +3 -24
- package/starters/basic/site/content.md +40 -7
- package/starters/basic/site/theme.md +84 -18
- package/starters/project/.github/workflows/deploy.yml +65 -0
- package/starters/project/README.md +72 -0
- package/starters/project/package-lock.json +4296 -0
- package/starters/project/package.json +35 -0
- package/starters/project/site/config.mjs +43 -0
- package/starters/project/site/content.md +128 -0
- package/starters/project/site/public/robots.txt +2 -0
- package/starters/project/site/routes/guide/route-content.md +85 -0
- package/starters/project/site/theme.md +43 -0
|
@@ -32,22 +32,81 @@ interface Props {
|
|
|
32
32
|
|
|
33
33
|
const { section, pagePresentation, headingLevel, priorityGalleryImage = false } = Astro.props;
|
|
34
34
|
const HeadingTag = headingLevel === 1 ? 'h1' : 'h2';
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
// Sections are authored with Markdown ## headings even when the first one renders as HTML h1.
|
|
36
|
+
const sectionHeadingVisualLevel = 'h2';
|
|
37
|
+
type HeadingLevel = 'h1' | 'h2' | 'h3' | 'h4';
|
|
38
|
+
const headingSizeValues: Record<HeadingLevel, Record<TextSize, ResponsiveValue<string>>> = {
|
|
39
|
+
h1: {
|
|
40
|
+
small: {
|
|
41
|
+
desktop: 'clamp(1.5rem, 1.18rem + 1.15vw, 2.25rem)',
|
|
42
|
+
mobile: 'clamp(1.42rem, 1.12rem + 1.5vw, 2rem)',
|
|
43
|
+
},
|
|
44
|
+
medium: {
|
|
45
|
+
desktop: 'clamp(1.85rem, 1.35rem + 1.8vw, 3.1rem)',
|
|
46
|
+
mobile: 'clamp(1.65rem, 1.18rem + 2.2vw, 2.45rem)',
|
|
47
|
+
},
|
|
48
|
+
large: {
|
|
49
|
+
desktop: 'clamp(2.25rem, 1.45rem + 2.75vw, 4.25rem)',
|
|
50
|
+
mobile: 'clamp(1.9rem, 1.26rem + 3.2vw, 3.15rem)',
|
|
51
|
+
},
|
|
52
|
+
xlarge: {
|
|
53
|
+
desktop: 'clamp(2.75rem, 1.6rem + 3.8vw, 5.5rem)',
|
|
54
|
+
mobile: 'clamp(2.2rem, 1.35rem + 4.4vw, 3.9rem)',
|
|
55
|
+
},
|
|
39
56
|
},
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
57
|
+
h2: {
|
|
58
|
+
small: {
|
|
59
|
+
desktop: 'clamp(1.1rem, 1.02rem + 0.35vw, 1.35rem)',
|
|
60
|
+
mobile: 'clamp(1.12rem, 1rem + 0.85vw, 1.42rem)',
|
|
61
|
+
},
|
|
62
|
+
medium: {
|
|
63
|
+
desktop: 'clamp(1.35rem, 1.1rem + 0.9vw, 2rem)',
|
|
64
|
+
mobile: 'clamp(1.3rem, 1.05rem + 1.35vw, 1.75rem)',
|
|
65
|
+
},
|
|
66
|
+
large: {
|
|
67
|
+
desktop: 'clamp(1.6rem, 1.22rem + 1.45vw, 2.75rem)',
|
|
68
|
+
mobile: 'clamp(1.5rem, 1.12rem + 2vw, 2.25rem)',
|
|
69
|
+
},
|
|
70
|
+
xlarge: {
|
|
71
|
+
desktop: 'clamp(2rem, 1.35rem + 2.4vw, 3.75rem)',
|
|
72
|
+
mobile: 'clamp(1.8rem, 1.25rem + 3vw, 2.85rem)',
|
|
73
|
+
},
|
|
43
74
|
},
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
75
|
+
h3: {
|
|
76
|
+
small: {
|
|
77
|
+
desktop: 'clamp(0.96rem, 0.94rem + 0.08vw, 1rem)',
|
|
78
|
+
mobile: 'clamp(0.96rem, 0.94rem + 0.08vw, 1rem)',
|
|
79
|
+
},
|
|
80
|
+
medium: {
|
|
81
|
+
desktop: 'clamp(1.05rem, 0.98rem + 0.28vw, 1.22rem)',
|
|
82
|
+
mobile: 'clamp(1.04rem, 0.98rem + 0.45vw, 1.18rem)',
|
|
83
|
+
},
|
|
84
|
+
large: {
|
|
85
|
+
desktop: 'clamp(1.16rem, 1.02rem + 0.5vw, 1.5rem)',
|
|
86
|
+
mobile: 'clamp(1.12rem, 1rem + 0.75vw, 1.36rem)',
|
|
87
|
+
},
|
|
88
|
+
xlarge: {
|
|
89
|
+
desktop: 'clamp(1.32rem, 1.08rem + 0.85vw, 1.9rem)',
|
|
90
|
+
mobile: 'clamp(1.24rem, 1.04rem + 1.15vw, 1.65rem)',
|
|
91
|
+
},
|
|
47
92
|
},
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
93
|
+
h4: {
|
|
94
|
+
small: {
|
|
95
|
+
desktop: 'clamp(0.86rem, 0.84rem + 0.08vw, 0.92rem)',
|
|
96
|
+
mobile: 'clamp(0.86rem, 0.84rem + 0.08vw, 0.92rem)',
|
|
97
|
+
},
|
|
98
|
+
medium: {
|
|
99
|
+
desktop: 'clamp(0.96rem, 0.94rem + 0.08vw, 1rem)',
|
|
100
|
+
mobile: 'clamp(0.96rem, 0.94rem + 0.08vw, 1rem)',
|
|
101
|
+
},
|
|
102
|
+
large: {
|
|
103
|
+
desktop: 'clamp(1.05rem, 1rem + 0.16vw, 1.12rem)',
|
|
104
|
+
mobile: 'clamp(1.05rem, 1rem + 0.16vw, 1.12rem)',
|
|
105
|
+
},
|
|
106
|
+
xlarge: {
|
|
107
|
+
desktop: 'clamp(1.14rem, 1.06rem + 0.26vw, 1.25rem)',
|
|
108
|
+
mobile: 'clamp(1.14rem, 1.06rem + 0.26vw, 1.25rem)',
|
|
109
|
+
},
|
|
51
110
|
},
|
|
52
111
|
};
|
|
53
112
|
const bodySizeValues: Record<TextSize, ResponsiveValue<string>> = {
|
|
@@ -91,16 +150,37 @@ const getSizeValue = (
|
|
|
91
150
|
sizeValues: Record<TextSize, ResponsiveValue<string>>,
|
|
92
151
|
size: TextSize,
|
|
93
152
|
) => sizeValues[size];
|
|
153
|
+
const getHeadingSizeValue = (level: HeadingLevel, size: TextSize) => (
|
|
154
|
+
headingSizeValues[level][size]
|
|
155
|
+
);
|
|
94
156
|
const backgroundColor = sectionOverride?.backgroundColor
|
|
95
157
|
?? pagePresentation.backgroundColor;
|
|
96
158
|
const textColor = sectionOverride?.textColor
|
|
97
159
|
?? pagePresentation.textColor;
|
|
98
160
|
const resolvedTypography = resolveTypographyOverride(pagePresentation.typography, sectionOverride?.typography);
|
|
99
161
|
const typography = resolvedTypography.values;
|
|
100
|
-
const
|
|
162
|
+
const heading = typography.headings[sectionHeadingVisualLevel] as {
|
|
163
|
+
align: ResponsiveValue<TextAlign>;
|
|
164
|
+
size: TextSize;
|
|
165
|
+
lineHeight: number;
|
|
166
|
+
spacingBefore: string;
|
|
167
|
+
spacingAfter: string;
|
|
168
|
+
};
|
|
169
|
+
const markdownHeadingLevels = ['h3', 'h4'] as const;
|
|
170
|
+
const markdownHeadings = Object.fromEntries(markdownHeadingLevels.map((level) => [
|
|
171
|
+
level,
|
|
172
|
+
typography.headings[level] as {
|
|
173
|
+
align: ResponsiveValue<TextAlign>;
|
|
174
|
+
size: TextSize;
|
|
175
|
+
lineHeight: number;
|
|
176
|
+
spacingBefore: string;
|
|
177
|
+
spacingAfter: string;
|
|
178
|
+
},
|
|
179
|
+
]));
|
|
180
|
+
const headingAlign = heading.align;
|
|
101
181
|
const bodyAlign = typography.body.align as ResponsiveValue<TextAlign>;
|
|
102
182
|
const captionAlign = typography.caption.align as ResponsiveValue<TextAlign>;
|
|
103
|
-
const headingFontSize =
|
|
183
|
+
const headingFontSize = getHeadingSizeValue(sectionHeadingVisualLevel, heading.size);
|
|
104
184
|
const bodyFontSize = getSizeValue(bodySizeValues, typography.body.size as TextSize);
|
|
105
185
|
const captionFontSize = getSizeValue(captionSizeValues, typography.caption.size as TextSize);
|
|
106
186
|
const justifyByAlign: Record<TextAlign, string> = {
|
|
@@ -118,6 +198,20 @@ const getAlignedTextWidth = (
|
|
|
118
198
|
const headingWidth = getAlignedTextWidth(headingAlign, '760px');
|
|
119
199
|
const bodyWidth = getAlignedTextWidth(bodyAlign, 'var(--text-width)');
|
|
120
200
|
const hasGallery = section.gallery.length > 0;
|
|
201
|
+
const markdownHeadingStyle = (level: 'h3' | 'h4') => {
|
|
202
|
+
const headingConfig = markdownHeadings[level];
|
|
203
|
+
const fontSize = getHeadingSizeValue(level, headingConfig.size);
|
|
204
|
+
|
|
205
|
+
return [
|
|
206
|
+
`--section-markdown-${level}-align-desktop: ${headingConfig.align.desktop}`,
|
|
207
|
+
`--section-markdown-${level}-align-mobile: ${headingConfig.align.mobile}`,
|
|
208
|
+
`--section-markdown-${level}-font-size-desktop: ${fontSize.desktop}`,
|
|
209
|
+
`--section-markdown-${level}-font-size-mobile: ${fontSize.mobile}`,
|
|
210
|
+
`--section-markdown-${level}-line-height: ${headingConfig.lineHeight}`,
|
|
211
|
+
`--section-markdown-${level}-spacing-before: ${headingConfig.spacingBefore}`,
|
|
212
|
+
`--section-markdown-${level}-spacing-after: ${headingConfig.spacingAfter}`,
|
|
213
|
+
];
|
|
214
|
+
};
|
|
121
215
|
const sectionStyle = [
|
|
122
216
|
`--section-background-color: ${backgroundColor}`,
|
|
123
217
|
`--section-text-color: ${textColor}`,
|
|
@@ -125,10 +219,12 @@ const sectionStyle = [
|
|
|
125
219
|
`--section-heading-align-mobile: ${headingAlign.mobile}`,
|
|
126
220
|
`--section-heading-font-size-desktop: ${headingFontSize.desktop}`,
|
|
127
221
|
`--section-heading-font-size-mobile: ${headingFontSize.mobile}`,
|
|
128
|
-
`--section-heading-line-height: ${
|
|
129
|
-
`--section-heading-spacing: ${
|
|
222
|
+
`--section-heading-line-height: ${heading.lineHeight}`,
|
|
223
|
+
`--section-heading-spacing-after: ${heading.spacingAfter}`,
|
|
130
224
|
`--section-heading-width-desktop: ${headingWidth.desktop}`,
|
|
131
225
|
`--section-heading-width-mobile: ${headingWidth.mobile}`,
|
|
226
|
+
...markdownHeadingStyle('h3'),
|
|
227
|
+
...markdownHeadingStyle('h4'),
|
|
132
228
|
`--section-body-align-desktop: ${bodyAlign.desktop}`,
|
|
133
229
|
`--section-body-align-mobile: ${bodyAlign.mobile}`,
|
|
134
230
|
`--section-body-font-size-desktop: ${bodyFontSize.desktop}`,
|
|
@@ -142,7 +238,7 @@ const sectionStyle = [
|
|
|
142
238
|
`--section-caption-font-size-desktop: ${captionFontSize.desktop}`,
|
|
143
239
|
`--section-caption-font-size-mobile: ${captionFontSize.mobile}`,
|
|
144
240
|
`--section-caption-line-height: ${typography.caption.lineHeight}`,
|
|
145
|
-
`--section-caption-spacing: ${typography.caption.
|
|
241
|
+
`--section-caption-spacing-before: ${typography.caption.spacingBefore}`,
|
|
146
242
|
`--section-caption-justify-desktop: ${justifyByAlign[captionAlign.desktop]}`,
|
|
147
243
|
`--section-caption-justify-mobile: ${justifyByAlign[captionAlign.mobile]}`,
|
|
148
244
|
].join('; ');
|
|
@@ -150,7 +246,7 @@ const sectionStyle = [
|
|
|
150
246
|
|
|
151
247
|
<section
|
|
152
248
|
id={section.id}
|
|
153
|
-
class
|
|
249
|
+
class="site-section"
|
|
154
250
|
data-section-title={section.title}
|
|
155
251
|
style={sectionStyle}
|
|
156
252
|
>
|
package/src/content.config.ts
CHANGED
|
@@ -18,6 +18,7 @@ const colorValue = z.string().regex(
|
|
|
18
18
|
const textAlign = z.enum(['left', 'center', 'right']);
|
|
19
19
|
const textSize = z.enum(['small', 'medium', 'large', 'xlarge']);
|
|
20
20
|
const typographyPreset = z.enum(['quiet-gallery', 'compact-gallery', 'text-forward', 'statement']);
|
|
21
|
+
const spacingDensity = z.enum(['compact', 'normal', 'airy']);
|
|
21
22
|
const lineHeight = z.number()
|
|
22
23
|
.min(1, 'Use a unitless line height of at least 1.')
|
|
23
24
|
.max(3, 'Use a unitless line height of at most 3.');
|
|
@@ -25,6 +26,12 @@ const cssLength = z.string().regex(
|
|
|
25
26
|
/^(?:0|(?:\d+(?:\.\d+)?|\.\d+)(?:px|rem|em|ch|lh))$/,
|
|
26
27
|
'Use a CSS length such as "0", "0.8em", "1rem", or "12px".',
|
|
27
28
|
);
|
|
29
|
+
const visualCssLengthUnit = String.raw`(?:px|rem|em|vw|vh|vmin|vmax|ch|%)`;
|
|
30
|
+
const visualCssLengthValue = String.raw`(?:\d+(?:\.\d+)?|\.\d+)${visualCssLengthUnit}`;
|
|
31
|
+
const visualCssLength = z.string().regex(
|
|
32
|
+
new RegExp(`^(?:0|${visualCssLengthValue}|clamp\\(\\s*${visualCssLengthValue}\\s*,\\s*${visualCssLengthValue}\\s*,\\s*${visualCssLengthValue}\\s*\\))$`),
|
|
33
|
+
'Use a CSS length such as "0", "900px", "56rem", "90%", or "clamp(1rem, 4vw, 3rem)".',
|
|
34
|
+
);
|
|
28
35
|
const inlineStyleName = z.string().regex(/^[a-z][a-z0-9-]*$/);
|
|
29
36
|
const dateOnly = z.string()
|
|
30
37
|
.regex(/^\d{4}-\d{2}-\d{2}$/, 'Use YYYY-MM-DD format.')
|
|
@@ -53,7 +60,14 @@ const commonTextPresentationOverride = {
|
|
|
53
60
|
};
|
|
54
61
|
const headingPresentationOverride = z.object({
|
|
55
62
|
...commonTextPresentationOverride,
|
|
56
|
-
|
|
63
|
+
spacingBefore: cssLength.optional(),
|
|
64
|
+
spacingAfter: cssLength.optional(),
|
|
65
|
+
}).strict();
|
|
66
|
+
const headingLevelsPresentationOverride = z.object({
|
|
67
|
+
h1: headingPresentationOverride.optional(),
|
|
68
|
+
h2: headingPresentationOverride.optional(),
|
|
69
|
+
h3: headingPresentationOverride.optional(),
|
|
70
|
+
h4: headingPresentationOverride.optional(),
|
|
57
71
|
}).strict();
|
|
58
72
|
const bodyPresentationOverride = z.object({
|
|
59
73
|
...commonTextPresentationOverride,
|
|
@@ -61,20 +75,64 @@ const bodyPresentationOverride = z.object({
|
|
|
61
75
|
}).strict();
|
|
62
76
|
const captionPresentationOverride = z.object({
|
|
63
77
|
...commonTextPresentationOverride,
|
|
64
|
-
|
|
78
|
+
spacingBefore: cssLength.optional(),
|
|
65
79
|
}).strict();
|
|
66
80
|
const typographyOverrides = z.object({
|
|
67
|
-
|
|
81
|
+
headings: headingLevelsPresentationOverride.optional(),
|
|
68
82
|
body: bodyPresentationOverride.optional(),
|
|
69
83
|
caption: captionPresentationOverride.optional(),
|
|
70
84
|
}).strict();
|
|
71
85
|
const typography = z.object({
|
|
72
86
|
preset: typographyPreset.optional(),
|
|
87
|
+
rhythm: spacingDensity.optional(),
|
|
88
|
+
overrides: typographyOverrides.optional(),
|
|
89
|
+
}).strict().refine(
|
|
90
|
+
(value) => value.preset !== undefined || value.rhythm !== undefined || value.overrides !== undefined,
|
|
91
|
+
'Specify preset, rhythm, overrides, or both.',
|
|
92
|
+
);
|
|
93
|
+
const themeTypography = z.object({
|
|
94
|
+
fontFamily: z.string()
|
|
95
|
+
.min(1)
|
|
96
|
+
.refine((value) => !/[\n\r;{}]/.test(value), 'Do not use semicolons, braces, or line breaks.').optional(),
|
|
97
|
+
preset: typographyPreset.optional(),
|
|
98
|
+
rhythm: spacingDensity.optional(),
|
|
73
99
|
overrides: typographyOverrides.optional(),
|
|
74
100
|
}).strict().refine(
|
|
75
|
-
(value) => value.preset !== undefined || value.overrides !== undefined,
|
|
76
|
-
'Specify preset, overrides, or both.',
|
|
101
|
+
(value) => value.fontFamily !== undefined || value.preset !== undefined || value.rhythm !== undefined || value.overrides !== undefined,
|
|
102
|
+
'Specify fontFamily, preset, rhythm, overrides, or both.',
|
|
77
103
|
);
|
|
104
|
+
const responsiveCssLength = z.union([
|
|
105
|
+
visualCssLength,
|
|
106
|
+
z.object({
|
|
107
|
+
desktop: visualCssLength,
|
|
108
|
+
mobile: visualCssLength,
|
|
109
|
+
}).strict(),
|
|
110
|
+
]);
|
|
111
|
+
const responsivePercent = z.union([
|
|
112
|
+
z.number().positive().max(100),
|
|
113
|
+
z.object({
|
|
114
|
+
desktop: z.number().positive().max(100),
|
|
115
|
+
mobile: z.number().positive().max(100),
|
|
116
|
+
}).strict(),
|
|
117
|
+
]);
|
|
118
|
+
const themeLayoutSpacing = z.object({
|
|
119
|
+
bodyToImages: responsiveCssLength.optional(),
|
|
120
|
+
finalSectionBottom: responsiveCssLength.optional(),
|
|
121
|
+
firstSectionTop: responsiveCssLength.optional(),
|
|
122
|
+
imageGap: responsiveCssLength.optional(),
|
|
123
|
+
sectionGap: responsiveCssLength.optional(),
|
|
124
|
+
}).strict();
|
|
125
|
+
const themeLayout = z.object({
|
|
126
|
+
density: spacingDensity.optional(),
|
|
127
|
+
pageWidth: visualCssLength.optional(),
|
|
128
|
+
gutter: responsiveCssLength.optional(),
|
|
129
|
+
spacing: themeLayoutSpacing.optional(),
|
|
130
|
+
}).strict();
|
|
131
|
+
const themeGallery = z.object({
|
|
132
|
+
width: visualCssLength.optional(),
|
|
133
|
+
maxAvailableWidthPercent: responsivePercent.optional(),
|
|
134
|
+
maxAvailableHeightPercent: responsivePercent.optional(),
|
|
135
|
+
}).strict();
|
|
78
136
|
const sectionPresentationOverride = z.object({
|
|
79
137
|
backgroundColor: colorValue.optional(),
|
|
80
138
|
textColor: colorValue.optional(),
|
|
@@ -86,7 +144,6 @@ const themePresentation = z.object({
|
|
|
86
144
|
inlineStyles: z.record(inlineStyleName, z.object({
|
|
87
145
|
color: colorValue,
|
|
88
146
|
}).strict()).optional(),
|
|
89
|
-
typography: typography.optional(),
|
|
90
147
|
}).strict();
|
|
91
148
|
const pagePresentation = z.object({
|
|
92
149
|
backgroundColor: colorValue.optional(),
|
|
@@ -141,6 +198,9 @@ const siteSchema = z.object({
|
|
|
141
198
|
|
|
142
199
|
const themeSchema = z.object({
|
|
143
200
|
navigation: themeNavigation.optional(),
|
|
201
|
+
layout: themeLayout.optional(),
|
|
202
|
+
gallery: themeGallery.optional(),
|
|
203
|
+
typography: themeTypography.optional(),
|
|
144
204
|
presentation: themePresentation.optional(),
|
|
145
205
|
frame: frame.optional(),
|
|
146
206
|
}).strict();
|
|
@@ -29,12 +29,23 @@ const iconLinks = getIconLinks();
|
|
|
29
29
|
const scrollBehavior = projectConfig.navigation.smoothScroll.enabled ? 'smooth' : 'auto';
|
|
30
30
|
const formatPercent = (value: number) => `${value}%`;
|
|
31
31
|
const formatViewportHeightPercent = (value: number) => `${value}svh`;
|
|
32
|
+
const layoutSpacing = projectConfig.layout.spacing;
|
|
32
33
|
const documentStyle = [
|
|
33
34
|
`scroll-behavior: ${scrollBehavior}`,
|
|
34
35
|
`--font-sans: ${projectConfig.typography.fontFamily}`,
|
|
35
36
|
`--page-width: ${projectConfig.layout.pageWidth}`,
|
|
36
37
|
`--content-gutter-desktop: ${projectConfig.layout.gutter.desktop}`,
|
|
37
38
|
`--content-gutter-mobile: ${projectConfig.layout.gutter.mobile}`,
|
|
39
|
+
`--space-nav-to-first-section-desktop: ${layoutSpacing.firstSectionTop.desktop}`,
|
|
40
|
+
`--space-nav-to-first-section-mobile: ${layoutSpacing.firstSectionTop.mobile}`,
|
|
41
|
+
`--space-section-body-to-gallery-desktop: ${layoutSpacing.bodyToImages.desktop}`,
|
|
42
|
+
`--space-section-body-to-gallery-mobile: ${layoutSpacing.bodyToImages.mobile}`,
|
|
43
|
+
`--space-gallery-item-desktop: ${layoutSpacing.imageGap.desktop}`,
|
|
44
|
+
`--space-gallery-item-mobile: ${layoutSpacing.imageGap.mobile}`,
|
|
45
|
+
`--space-section-to-section-desktop: ${layoutSpacing.sectionGap.desktop}`,
|
|
46
|
+
`--space-section-to-section-mobile: ${layoutSpacing.sectionGap.mobile}`,
|
|
47
|
+
`--space-final-section-bottom-desktop: ${layoutSpacing.finalSectionBottom.desktop}`,
|
|
48
|
+
`--space-final-section-bottom-mobile: ${layoutSpacing.finalSectionBottom.mobile}`,
|
|
38
49
|
`--gallery-width: ${projectConfig.gallery.width}`,
|
|
39
50
|
`--gallery-max-available-width-desktop: ${formatPercent(projectConfig.gallery.maxAvailableWidthPercent.desktop)}`,
|
|
40
51
|
`--gallery-max-available-width-mobile: ${formatPercent(projectConfig.gallery.maxAvailableWidthPercent.mobile)}`,
|
package/src/styles/global.css
CHANGED
|
@@ -28,20 +28,21 @@
|
|
|
28
28
|
--gallery-max-image-height: var(--gallery-max-image-height-desktop);
|
|
29
29
|
--nav-top-safety-inset: 0px;
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
--space-nav-to-first-section:
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
--space-section-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
--space-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
--space-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
--space-section-
|
|
31
|
+
--space-nav-to-first-section-desktop: clamp(1.875rem, 3vw, 2.75rem);
|
|
32
|
+
--space-nav-to-first-section-mobile: 1.375rem;
|
|
33
|
+
--space-nav-to-first-section: var(--space-nav-to-first-section-desktop);
|
|
34
|
+
--space-section-body-to-gallery-desktop: clamp(1.25rem, 2.5vw, 2rem);
|
|
35
|
+
--space-section-body-to-gallery-mobile: 1.25rem;
|
|
36
|
+
--space-section-body-to-gallery: var(--space-section-body-to-gallery-desktop);
|
|
37
|
+
--space-gallery-item-desktop: clamp(1.5rem, 3.5vw, 2.75rem);
|
|
38
|
+
--space-gallery-item-mobile: 2.75rem;
|
|
39
|
+
--space-gallery-item: var(--space-gallery-item-desktop);
|
|
40
|
+
--space-section-to-section-desktop: clamp(2.55rem, 4.2vw, 3.9rem);
|
|
41
|
+
--space-section-to-section-mobile: 2.25rem;
|
|
42
|
+
--space-section-to-section: var(--space-section-to-section-desktop);
|
|
43
|
+
--space-final-section-bottom-desktop: var(--space-section-to-section-desktop);
|
|
44
|
+
--space-final-section-bottom-mobile: var(--space-section-to-section-mobile);
|
|
45
|
+
--space-final-section-bottom: var(--space-final-section-bottom-desktop);
|
|
45
46
|
|
|
46
47
|
--content-gutter-desktop: clamp(1.25rem, 4vw, 3rem);
|
|
47
48
|
--content-gutter-mobile: 1rem;
|
|
@@ -316,7 +317,7 @@ a {
|
|
|
316
317
|
}
|
|
317
318
|
|
|
318
319
|
.site-section:last-child {
|
|
319
|
-
padding-bottom: var(--space-section-
|
|
320
|
+
padding-bottom: var(--space-final-section-bottom);
|
|
320
321
|
}
|
|
321
322
|
|
|
322
323
|
.section-card {
|
|
@@ -327,19 +328,19 @@ a {
|
|
|
327
328
|
.section-header {
|
|
328
329
|
width: min(100%, var(--section-heading-width-desktop, 760px));
|
|
329
330
|
margin: 0 auto;
|
|
330
|
-
padding: 0 0 var(--section-heading-spacing,
|
|
331
|
+
padding: 0 0 var(--section-heading-spacing-after, 0.55em);
|
|
331
332
|
text-align: var(--section-heading-align-desktop, center);
|
|
332
333
|
}
|
|
333
334
|
|
|
334
|
-
h1,
|
|
335
|
-
h2,
|
|
335
|
+
.section-header h1,
|
|
336
|
+
.section-header h2,
|
|
336
337
|
p,
|
|
337
338
|
ul {
|
|
338
339
|
margin-top: 0;
|
|
339
340
|
}
|
|
340
341
|
|
|
341
|
-
h1,
|
|
342
|
-
h2 {
|
|
342
|
+
.section-header h1,
|
|
343
|
+
.section-header h2 {
|
|
343
344
|
margin-bottom: 0;
|
|
344
345
|
color: inherit;
|
|
345
346
|
font-size: var(--section-heading-font-size-desktop, clamp(1.35rem, 1.1rem + 0.9vw, 2rem));
|
|
@@ -350,11 +351,6 @@ h2 {
|
|
|
350
351
|
overflow-wrap: anywhere;
|
|
351
352
|
}
|
|
352
353
|
|
|
353
|
-
.site-section-primary h1 {
|
|
354
|
-
font-size: var(--section-heading-font-size-desktop, clamp(1.6rem, 1.22rem + 1.45vw, 2.75rem));
|
|
355
|
-
font-weight: 700;
|
|
356
|
-
}
|
|
357
|
-
|
|
358
354
|
.section-body {
|
|
359
355
|
width: 100%;
|
|
360
356
|
margin: 0 auto;
|
|
@@ -375,6 +371,31 @@ h2 {
|
|
|
375
371
|
margin: 0 0 var(--section-body-paragraph-spacing, 1em);
|
|
376
372
|
}
|
|
377
373
|
|
|
374
|
+
.section-markdown blockquote {
|
|
375
|
+
margin: 0 0 var(--section-body-paragraph-spacing, 1em);
|
|
376
|
+
border-left: 1px solid color-mix(in srgb, currentColor 34%, transparent);
|
|
377
|
+
padding-left: 1rem;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
.section-markdown blockquote > :last-child {
|
|
381
|
+
margin-bottom: 0;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
.section-markdown pre.astro-code {
|
|
385
|
+
margin: 0 0 var(--section-body-paragraph-spacing, 1em);
|
|
386
|
+
border: 1px solid color-mix(in srgb, currentColor 18%, transparent);
|
|
387
|
+
border-radius: 6px;
|
|
388
|
+
padding: 0.78rem 0.9rem;
|
|
389
|
+
background: #0d1117 !important;
|
|
390
|
+
font-size: 0.92em;
|
|
391
|
+
line-height: 1.45;
|
|
392
|
+
text-align: left;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
.section-markdown pre.astro-code code {
|
|
396
|
+
font: inherit;
|
|
397
|
+
}
|
|
398
|
+
|
|
378
399
|
.section-markdown .inline-style {
|
|
379
400
|
color: var(--inline-style-color);
|
|
380
401
|
}
|
|
@@ -388,24 +409,40 @@ h2 {
|
|
|
388
409
|
margin-bottom: 0;
|
|
389
410
|
}
|
|
390
411
|
|
|
412
|
+
.section-markdown > pre.astro-code:last-child {
|
|
413
|
+
margin-bottom: 0;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
.section-markdown blockquote > pre.astro-code:last-child {
|
|
417
|
+
margin-bottom: 0;
|
|
418
|
+
}
|
|
419
|
+
|
|
391
420
|
.section-markdown h3 {
|
|
392
|
-
margin:
|
|
421
|
+
margin:
|
|
422
|
+
var(--section-markdown-h3-spacing-before, 1.5em)
|
|
423
|
+
0
|
|
424
|
+
var(--section-markdown-h3-spacing-after, 0.5em);
|
|
393
425
|
border-top: 1px solid var(--color-soft);
|
|
394
|
-
padding-top:
|
|
426
|
+
padding-top: 0.55em;
|
|
395
427
|
font-family: var(--font-sans);
|
|
396
|
-
font-size: 0.82rem;
|
|
397
|
-
letter-spacing: 0
|
|
398
|
-
line-height: 1.35;
|
|
428
|
+
font-size: var(--section-markdown-h3-font-size-desktop, 0.82rem);
|
|
429
|
+
letter-spacing: 0;
|
|
430
|
+
line-height: var(--section-markdown-h3-line-height, 1.35);
|
|
431
|
+
text-align: var(--section-markdown-h3-align-desktop, left);
|
|
399
432
|
text-transform: uppercase;
|
|
400
433
|
}
|
|
401
434
|
|
|
402
435
|
.section-markdown h4 {
|
|
403
|
-
margin:
|
|
436
|
+
margin:
|
|
437
|
+
var(--section-markdown-h4-spacing-before, 1.1em)
|
|
438
|
+
0
|
|
439
|
+
var(--section-markdown-h4-spacing-after, 0.4em);
|
|
404
440
|
color: inherit;
|
|
405
|
-
font-size: clamp(0.96rem, 0.94rem + 0.08vw, 1rem);
|
|
441
|
+
font-size: var(--section-markdown-h4-font-size-desktop, clamp(0.96rem, 0.94rem + 0.08vw, 1rem));
|
|
406
442
|
font-weight: 400;
|
|
407
|
-
letter-spacing: 0
|
|
408
|
-
line-height: 1.5;
|
|
443
|
+
letter-spacing: 0;
|
|
444
|
+
line-height: var(--section-markdown-h4-line-height, 1.5);
|
|
445
|
+
text-align: var(--section-markdown-h4-align-desktop, left);
|
|
409
446
|
}
|
|
410
447
|
|
|
411
448
|
.section-markdown ul,
|
|
@@ -618,7 +655,7 @@ h2 {
|
|
|
618
655
|
|
|
619
656
|
.gallery-meta {
|
|
620
657
|
width: min(100%, var(--gallery-layout-width));
|
|
621
|
-
margin: var(--section-caption-spacing, 0.5em) auto 0;
|
|
658
|
+
margin: var(--section-caption-spacing-before, 0.5em) auto 0;
|
|
622
659
|
font-size: var(--section-caption-font-size-desktop, 0.95rem);
|
|
623
660
|
text-align: var(--section-caption-align-desktop, center);
|
|
624
661
|
}
|
|
@@ -639,11 +676,11 @@ h2 {
|
|
|
639
676
|
|
|
640
677
|
@media (max-width: 700px) {
|
|
641
678
|
:root {
|
|
642
|
-
--space-nav-to-first-section:
|
|
643
|
-
--space-section-
|
|
644
|
-
--space-
|
|
645
|
-
--space-
|
|
646
|
-
--space-section-
|
|
679
|
+
--space-nav-to-first-section: var(--space-nav-to-first-section-mobile);
|
|
680
|
+
--space-section-body-to-gallery: var(--space-section-body-to-gallery-mobile);
|
|
681
|
+
--space-gallery-item: var(--space-gallery-item-mobile);
|
|
682
|
+
--space-section-to-section: var(--space-section-to-section-mobile);
|
|
683
|
+
--space-final-section-bottom: var(--space-final-section-bottom-mobile);
|
|
647
684
|
--content-gutter: var(--content-gutter-mobile);
|
|
648
685
|
--gallery-max-available-width: var(--gallery-max-available-width-mobile);
|
|
649
686
|
--gallery-max-image-height: var(--gallery-max-image-height-mobile);
|
|
@@ -780,21 +817,14 @@ h2 {
|
|
|
780
817
|
line-height: 1.72;
|
|
781
818
|
}
|
|
782
819
|
|
|
783
|
-
h1,
|
|
784
|
-
h2 {
|
|
820
|
+
.section-header h1,
|
|
821
|
+
.section-header h2 {
|
|
785
822
|
font-size: var(
|
|
786
823
|
--section-heading-font-size-mobile,
|
|
787
824
|
var(--section-heading-font-size-desktop, clamp(1.3rem, 1.05rem + 1.35vw, 1.75rem))
|
|
788
825
|
);
|
|
789
826
|
}
|
|
790
827
|
|
|
791
|
-
.site-section-primary h1 {
|
|
792
|
-
font-size: var(
|
|
793
|
-
--section-heading-font-size-mobile,
|
|
794
|
-
var(--section-heading-font-size-desktop, clamp(1.5rem, 1.12rem + 2vw, 2.25rem))
|
|
795
|
-
);
|
|
796
|
-
}
|
|
797
|
-
|
|
798
828
|
.section-header {
|
|
799
829
|
width: min(100%, var(--section-heading-width-mobile, var(--section-heading-width-desktop, 760px)));
|
|
800
830
|
text-align: var(--section-heading-align-mobile, var(--section-heading-align-desktop, center));
|
|
@@ -819,12 +849,13 @@ h2 {
|
|
|
819
849
|
}
|
|
820
850
|
|
|
821
851
|
.section-markdown h3 {
|
|
822
|
-
|
|
823
|
-
|
|
852
|
+
font-size: var(--section-markdown-h3-font-size-mobile, var(--section-markdown-h3-font-size-desktop, 0.82rem));
|
|
853
|
+
text-align: var(--section-markdown-h3-align-mobile, var(--section-markdown-h3-align-desktop, left));
|
|
824
854
|
}
|
|
825
855
|
|
|
826
856
|
.section-markdown h4 {
|
|
827
|
-
|
|
857
|
+
font-size: var(--section-markdown-h4-font-size-mobile, var(--section-markdown-h4-font-size-desktop, clamp(0.96rem, 0.94rem + 0.08vw, 1rem)));
|
|
858
|
+
text-align: var(--section-markdown-h4-align-mobile, var(--section-markdown-h4-align-desktop, left));
|
|
828
859
|
}
|
|
829
860
|
|
|
830
861
|
.section-markdown ul,
|
package/starters/basic/README.md
CHANGED
|
@@ -6,7 +6,7 @@ This is a minimal site repository starter for `@janga/norna`.
|
|
|
6
6
|
|
|
7
7
|
```sh
|
|
8
8
|
npm install
|
|
9
|
-
npm run
|
|
9
|
+
npm run dev
|
|
10
10
|
```
|
|
11
11
|
|
|
12
12
|
If the standard local port is blocked, start with:
|
|
@@ -15,9 +15,9 @@ If the standard local port is blocked, start with:
|
|
|
15
15
|
npm run dev -- --kill
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
Edit site-wide
|
|
19
|
-
overrides in `site/content.md`, technical settings such as URL
|
|
20
|
-
|
|
18
|
+
Edit site-wide visual defaults in `site/theme.md`, homepage content and section
|
|
19
|
+
overrides in `site/content.md`, technical settings such as URL and locale
|
|
20
|
+
labels in `site/config.mjs`, source images under
|
|
21
21
|
`site/images/<section-id>/`, and static public files under `site/public/`.
|
|
22
22
|
|
|
23
23
|
Use `site.basePath: '/'` for a custom domain or root-hosted site. For a GitHub
|
|
@@ -32,14 +32,10 @@ site: {
|
|
|
32
32
|
|
|
33
33
|
In GitHub repository settings, configure Pages to build from GitHub Actions.
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
`
|
|
38
|
-
`
|
|
39
|
-
|
|
40
|
-
The site font is configured with `typography.fontFamily` in `site/config.mjs`.
|
|
41
|
-
Site-wide text presentation is configured with typography presets and overrides
|
|
42
|
-
in `site/theme.md`; page and section overrides live in `site/content.md`.
|
|
35
|
+
Page width, side gutters, layout density, typography rhythm, image width, image
|
|
36
|
+
height limits, font, colors, and site-wide typography are configured in
|
|
37
|
+
`site/theme.md`. Page and section presentation overrides live in
|
|
38
|
+
`site/content.md`.
|
|
43
39
|
|
|
44
40
|
Commit `package-lock.json` after the first install so GitHub Actions can use
|
|
45
41
|
`npm ci`.
|
|
@@ -54,9 +50,9 @@ npm run norna:typography:show
|
|
|
54
50
|
npm run norna:build
|
|
55
51
|
```
|
|
56
52
|
|
|
57
|
-
This keeps
|
|
58
|
-
|
|
59
|
-
|
|
53
|
+
This keeps Norna commands separate from repository-specific build or publishing
|
|
54
|
+
commands in projects that embed a Norna site inside a larger GitHub project.
|
|
55
|
+
This pure starter also keeps `npm run build` as an alias for
|
|
60
56
|
`npm run norna:build`.
|
|
61
57
|
|
|
62
58
|
Use `npm run norna:engine:version` to inspect the installed engine and
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
"norna:config:check": "norna config:check",
|
|
15
15
|
"norna:content:check": "norna content:check",
|
|
16
16
|
"norna:sync": "norna content:sync",
|
|
17
|
-
"norna:typography:presets": "norna typography
|
|
18
|
-
"norna:typography:show": "norna typography
|
|
17
|
+
"norna:typography:presets": "norna typography presets",
|
|
18
|
+
"norna:typography:show": "norna typography show",
|
|
19
19
|
"norna:public": "norna site:public",
|
|
20
20
|
"norna:images": "norna images",
|
|
21
21
|
"norna:build": "norna build",
|
|
@@ -3,27 +3,6 @@ export default {
|
|
|
3
3
|
url: 'https://example.com/',
|
|
4
4
|
basePath: '/',
|
|
5
5
|
},
|
|
6
|
-
layout: {
|
|
7
|
-
pageWidth: '1180px',
|
|
8
|
-
gutter: {
|
|
9
|
-
desktop: 'clamp(1.25rem, 4vw, 3rem)',
|
|
10
|
-
mobile: '1rem',
|
|
11
|
-
},
|
|
12
|
-
},
|
|
13
|
-
gallery: {
|
|
14
|
-
width: '900px',
|
|
15
|
-
maxAvailableWidthPercent: {
|
|
16
|
-
desktop: 100,
|
|
17
|
-
mobile: 100,
|
|
18
|
-
},
|
|
19
|
-
maxAvailableHeightPercent: {
|
|
20
|
-
desktop: 74,
|
|
21
|
-
mobile: 68,
|
|
22
|
-
},
|
|
23
|
-
},
|
|
24
|
-
typography: {
|
|
25
|
-
fontFamily: "Arial, 'Helvetica Neue', Helvetica, sans-serif",
|
|
26
|
-
},
|
|
27
6
|
navigation: {
|
|
28
7
|
smoothScroll: {
|
|
29
8
|
enabled: true,
|
|
@@ -37,11 +16,11 @@ export default {
|
|
|
37
16
|
labels: {
|
|
38
17
|
skipToContent: 'Skip to content',
|
|
39
18
|
sectionNavigation: 'Sections',
|
|
40
|
-
gallery: '
|
|
19
|
+
gallery: 'Images',
|
|
41
20
|
},
|
|
42
21
|
},
|
|
43
22
|
footer: {
|
|
44
|
-
copyrightMessage: '(c) Example
|
|
23
|
+
copyrightMessage: '(c) Example Owner.',
|
|
45
24
|
buildInfo: {
|
|
46
25
|
enabled: true,
|
|
47
26
|
text: 'Built',
|
|
@@ -54,7 +33,7 @@ export default {
|
|
|
54
33
|
},
|
|
55
34
|
},
|
|
56
35
|
github: {
|
|
57
|
-
repo: 'owner/example-
|
|
36
|
+
repo: 'owner/example-site',
|
|
58
37
|
branch: 'main',
|
|
59
38
|
pagesWorkflow: 'Deploy to GitHub Pages',
|
|
60
39
|
},
|