@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.
Files changed (88) hide show
  1. package/README.md +16 -16
  2. package/bin/norna-cli.mjs +12 -7
  3. package/docs/README.md +11 -12
  4. package/docs/commands.md +9 -9
  5. package/docs/configuration.md +14 -145
  6. package/docs/content.md +17 -15
  7. package/docs/design/command-organization.md +58 -57
  8. package/docs/design/site-examples-structure.md +6 -5
  9. package/docs/engine-development.md +5 -5
  10. package/docs/getting-started.md +20 -26
  11. package/docs/images-and-metadata.md +1 -1
  12. package/docs/local-development.md +2 -2
  13. package/docs/publishing.md +1 -1
  14. package/docs/routes.md +2 -2
  15. package/docs/site-structure.md +8 -7
  16. package/docs/theme.md +133 -7
  17. package/docs/typography.md +78 -43
  18. package/examples/dog-gallery/node_modules/.vite/deps/_metadata.json +74 -0
  19. package/examples/dog-gallery/node_modules/.vite/deps/astro-D_GIpJIE.js +434 -0
  20. package/examples/dog-gallery/node_modules/.vite/deps/astro-D_GIpJIE.js.map +1 -0
  21. package/examples/dog-gallery/node_modules/.vite/deps/astro_n_aria-query.js +6390 -0
  22. package/examples/dog-gallery/node_modules/.vite/deps/astro_n_aria-query.js.map +1 -0
  23. package/examples/dog-gallery/node_modules/.vite/deps/astro_n_axobject-query.js +2702 -0
  24. package/examples/dog-gallery/node_modules/.vite/deps/astro_n_axobject-query.js.map +1 -0
  25. package/examples/dog-gallery/node_modules/.vite/deps/astro_n_html-escaper.js +65 -0
  26. package/examples/dog-gallery/node_modules/.vite/deps/astro_n_html-escaper.js.map +1 -0
  27. package/examples/dog-gallery/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +455 -0
  28. package/examples/dog-gallery/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +1 -0
  29. package/examples/dog-gallery/node_modules/.vite/deps/audit-DM9Hkl5B.js +1534 -0
  30. package/examples/dog-gallery/node_modules/.vite/deps/audit-DM9Hkl5B.js.map +1 -0
  31. package/examples/dog-gallery/node_modules/.vite/deps/highlight-DnZ1ULOL.js +48 -0
  32. package/examples/dog-gallery/node_modules/.vite/deps/highlight-DnZ1ULOL.js.map +1 -0
  33. package/examples/dog-gallery/node_modules/.vite/deps/icons-RyebKi45.js +42 -0
  34. package/examples/dog-gallery/node_modules/.vite/deps/icons-RyebKi45.js.map +1 -0
  35. package/examples/dog-gallery/node_modules/.vite/deps/package.json +3 -0
  36. package/examples/dog-gallery/node_modules/.vite/deps/rolldown-runtime-BvCyGRYZ.js +4 -0
  37. package/examples/dog-gallery/node_modules/.vite/deps/settings-DaU-gNnK.js +190 -0
  38. package/examples/dog-gallery/node_modules/.vite/deps/settings-DaU-gNnK.js.map +1 -0
  39. package/examples/dog-gallery/node_modules/.vite/deps/toolbar-63PA619H.js +458 -0
  40. package/examples/dog-gallery/node_modules/.vite/deps/toolbar-63PA619H.js.map +1 -0
  41. package/examples/dog-gallery/node_modules/.vite/deps/ui-library--bkcNSYz.js +1016 -0
  42. package/examples/dog-gallery/node_modules/.vite/deps/ui-library--bkcNSYz.js.map +1 -0
  43. package/examples/dog-gallery/node_modules/.vite/deps/window-jPkpKVcy.js +32 -0
  44. package/examples/dog-gallery/node_modules/.vite/deps/window-jPkpKVcy.js.map +1 -0
  45. package/examples/dog-gallery/node_modules/.vite/deps/xray-BXCHpEJP.js +111 -0
  46. package/examples/dog-gallery/node_modules/.vite/deps/xray-BXCHpEJP.js.map +1 -0
  47. package/examples/dog-gallery/site/config.mjs +12 -42
  48. package/examples/dog-gallery/site/content.md +28 -6
  49. package/examples/dog-gallery/site/routes/dog-care/route-content.md +17 -3
  50. package/examples/dog-gallery/site/theme.md +83 -26
  51. package/fixtures/basic/site/config.mjs +2 -22
  52. package/fixtures/basic/site/content.md +1 -1
  53. package/fixtures/basic/site/theme.md +7 -3
  54. package/package.json +3 -3
  55. package/scripts/check-config.mjs +4 -3
  56. package/scripts/deploy-site.mjs +3 -3
  57. package/scripts/init-site.mjs +10 -8
  58. package/scripts/lib/frontmatter-yaml.mjs +85 -0
  59. package/scripts/lib/presentation.mjs +10 -11
  60. package/scripts/lib/project-config.mjs +146 -45
  61. package/scripts/lib/site-content.mjs +21 -3
  62. package/scripts/lib/theme-config.mjs +34 -0
  63. package/scripts/lib/typography.mjs +155 -35
  64. package/scripts/show-typography.mjs +127 -159
  65. package/scripts/sync-content-sections.mjs +6 -6
  66. package/scripts/test-content-check.mjs +3 -4
  67. package/scripts/test-engine-commands.mjs +21 -3
  68. package/scripts/test-package-check.mjs +34 -17
  69. package/scripts/test-temporary-visibility.mjs +2 -3
  70. package/src/components/SitePage.astro +2 -3
  71. package/src/components/SiteSection.astro +115 -19
  72. package/src/content.config.ts +66 -6
  73. package/src/layouts/BaseLayout.astro +11 -0
  74. package/src/styles/global.css +83 -52
  75. package/starters/basic/README.md +11 -15
  76. package/starters/basic/package.json +2 -2
  77. package/starters/basic/site/config.mjs +3 -24
  78. package/starters/basic/site/content.md +40 -7
  79. package/starters/basic/site/theme.md +84 -18
  80. package/starters/project/.github/workflows/deploy.yml +65 -0
  81. package/starters/project/README.md +72 -0
  82. package/starters/project/package-lock.json +4296 -0
  83. package/starters/project/package.json +35 -0
  84. package/starters/project/site/config.mjs +43 -0
  85. package/starters/project/site/content.md +128 -0
  86. package/starters/project/site/public/robots.txt +2 -0
  87. package/starters/project/site/routes/guide/route-content.md +85 -0
  88. package/starters/project/site/theme.md +43 -0
@@ -18,42 +18,60 @@ const explicitHeadingIdRegex = /\s*\{#([a-z0-9-]+)\}\s*$/;
18
18
  const inlineStyleReferenceRegex = /\[[^\]\n]+\]\{\.([a-z][a-z0-9-]*)\}/g;
19
19
  const frontmatterDelimiterRegex = /^---\s*$/;
20
20
  const knownContentTopLevelFrontmatterKeys = new Set(['title', 'description', 'slug', 'navigation', 'presentation', 'frame', 'sections']);
21
- const knownThemeTopLevelFrontmatterKeys = new Set(['navigation', 'presentation', 'frame']);
21
+ const knownThemeTopLevelFrontmatterKeys = new Set(['navigation', 'layout', 'gallery', 'typography', 'presentation', 'frame']);
22
22
  const knownNestedFrontmatterKeys = new Set([
23
23
  'align',
24
24
  'alt',
25
25
  'backgroundColor',
26
26
  'body',
27
+ 'bodyToImages',
27
28
  'brand',
28
29
  'caption',
29
30
  'carousel',
30
31
  'color',
31
32
  'colors',
33
+ 'density',
32
34
  'desktop',
35
+ 'fontFamily',
33
36
  'from',
37
+ 'finalSectionBottom',
34
38
  'gallery',
39
+ 'gutter',
40
+ 'h1',
41
+ 'h2',
42
+ 'h3',
43
+ 'h4',
35
44
  'heading',
45
+ 'headings',
36
46
  'id',
37
47
  'include',
38
48
  'image',
49
+ 'imageGap',
39
50
  'inlineStyles',
40
51
  'label',
41
52
  'lineHeight',
53
+ 'maxAvailableHeightPercent',
54
+ 'maxAvailableWidthPercent',
42
55
  'mobile',
43
56
  'navigation',
44
57
  'overrides',
45
58
  'order',
46
59
  'paragraphSpacing',
60
+ 'pageWidth',
47
61
  'preset',
48
62
  'presentation',
63
+ 'rhythm',
49
64
  'sections',
65
+ 'sectionGap',
50
66
  'size',
51
- 'spacing',
67
+ 'spacingAfter',
68
+ 'spacingBefore',
52
69
  'textColor',
53
70
  'theme',
54
71
  'typography',
55
72
  'until',
56
73
  'visible',
74
+ 'width',
57
75
  ]);
58
76
 
59
77
  export const toPosixPath = (filePath) => filePath.split(path.sep).join('/');
@@ -224,7 +242,7 @@ export const validateFrontmatterStructure = (frontmatter, addIssue, {
224
242
  if (knownTopLevelFrontmatterKeys.has(key)) continue;
225
243
 
226
244
  const fix = knownNestedFrontmatterKeys.has(key)
227
- ? `Indent "${key}:" under the section or object it belongs to. For gallery rows, "gallery:" normally belongs under a "sections" item.`
245
+ ? `Indent "${key}:" under the section or object it belongs to. For image rows, "gallery:" normally belongs under a "sections" item.`
228
246
  : `Move "${key}:" under the correct parent key, or remove it if it is not part of the ${fileKind} schema.`;
229
247
 
230
248
  addIssue({
@@ -0,0 +1,34 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { parseYamlMapping } from './frontmatter-yaml.mjs';
3
+ import {
4
+ splitSiteFile,
5
+ validateFrontmatterIndentation,
6
+ validateThemeFrontmatterStructure,
7
+ } from './site-content.mjs';
8
+ import { siteThemeLabel, siteThemePath } from './site-paths.mjs';
9
+
10
+ const emptyThemeFile = '---\n---\n';
11
+
12
+ export const readThemeConfig = async () => {
13
+ const themeFile = await readFile(siteThemePath, 'utf8').catch((error) => {
14
+ if (error?.code === 'ENOENT') {
15
+ return emptyThemeFile;
16
+ }
17
+
18
+ throw error;
19
+ });
20
+ const { frontmatter, frontmatterBody } = splitSiteFile(themeFile, siteThemeLabel);
21
+ const issues = [];
22
+
23
+ validateFrontmatterIndentation(frontmatter, (issue) => issues.push(issue));
24
+ validateThemeFrontmatterStructure(frontmatter, (issue) => issues.push(issue));
25
+
26
+ if (issues.length > 0) {
27
+ throw new Error([
28
+ `${siteThemeLabel} has invalid frontmatter.`,
29
+ ...issues.map((issue) => `- ${issue.message}`),
30
+ ].join('\n'));
31
+ }
32
+
33
+ return parseYamlMapping(frontmatterBody);
34
+ };
@@ -5,91 +5,199 @@ export const typographyPresetNames = [
5
5
  'statement',
6
6
  ];
7
7
 
8
+ export const typographyRhythmNames = [
9
+ 'compact',
10
+ 'normal',
11
+ 'airy',
12
+ ];
13
+
8
14
  export const typographyPresets = {
9
15
  'quiet-gallery': {
10
- heading: {
11
- align: { desktop: 'left', mobile: 'left' },
12
- size: 'medium',
13
- lineHeight: 1.08,
14
- spacing: '0.65em',
16
+ headings: {
17
+ h1: {
18
+ align: { desktop: 'left', mobile: 'left' },
19
+ size: 'medium',
20
+ lineHeight: 1.04,
21
+ },
22
+ h2: {
23
+ align: { desktop: 'left', mobile: 'left' },
24
+ size: 'medium',
25
+ lineHeight: 1.08,
26
+ },
27
+ h3: {
28
+ align: { desktop: 'left', mobile: 'left' },
29
+ size: 'medium',
30
+ lineHeight: 1.35,
31
+ },
32
+ h4: {
33
+ align: { desktop: 'left', mobile: 'left' },
34
+ size: 'medium',
35
+ lineHeight: 1.5,
36
+ },
15
37
  },
16
38
  body: {
17
39
  align: { desktop: 'left', mobile: 'left' },
18
40
  size: 'medium',
19
41
  lineHeight: 1.5,
20
- paragraphSpacing: '0.85em',
21
42
  },
22
43
  caption: {
23
44
  align: { desktop: 'center', mobile: 'center' },
24
- size: 'small',
45
+ size: 'medium',
25
46
  lineHeight: 1.35,
26
- spacing: '0.5em',
27
47
  },
28
48
  },
29
49
  'compact-gallery': {
30
- heading: {
31
- align: { desktop: 'left', mobile: 'left' },
32
- size: 'medium',
33
- lineHeight: 1.08,
34
- spacing: '0.45em',
50
+ headings: {
51
+ h1: {
52
+ align: { desktop: 'left', mobile: 'left' },
53
+ size: 'medium',
54
+ lineHeight: 1.04,
55
+ },
56
+ h2: {
57
+ align: { desktop: 'left', mobile: 'left' },
58
+ size: 'medium',
59
+ lineHeight: 1.08,
60
+ },
61
+ h3: {
62
+ align: { desktop: 'left', mobile: 'left' },
63
+ size: 'medium',
64
+ lineHeight: 1.3,
65
+ },
66
+ h4: {
67
+ align: { desktop: 'left', mobile: 'left' },
68
+ size: 'medium',
69
+ lineHeight: 1.42,
70
+ },
35
71
  },
36
72
  body: {
37
73
  align: { desktop: 'left', mobile: 'left' },
38
74
  size: 'medium',
39
75
  lineHeight: 1.42,
40
- paragraphSpacing: '0.6em',
41
76
  },
42
77
  caption: {
43
78
  align: { desktop: 'center', mobile: 'center' },
44
- size: 'small',
79
+ size: 'medium',
45
80
  lineHeight: 1.25,
46
- spacing: '0.35em',
47
81
  },
48
82
  },
49
83
  'text-forward': {
50
- heading: {
51
- align: { desktop: 'left', mobile: 'left' },
52
- size: 'medium',
53
- lineHeight: 1.12,
54
- spacing: '0.8em',
84
+ headings: {
85
+ h1: {
86
+ align: { desktop: 'left', mobile: 'left' },
87
+ size: 'medium',
88
+ lineHeight: 1.06,
89
+ },
90
+ h2: {
91
+ align: { desktop: 'left', mobile: 'left' },
92
+ size: 'medium',
93
+ lineHeight: 1.12,
94
+ },
95
+ h3: {
96
+ align: { desktop: 'left', mobile: 'left' },
97
+ size: 'medium',
98
+ lineHeight: 1.35,
99
+ },
100
+ h4: {
101
+ align: { desktop: 'left', mobile: 'left' },
102
+ size: 'medium',
103
+ lineHeight: 1.55,
104
+ },
55
105
  },
56
106
  body: {
57
107
  align: { desktop: 'left', mobile: 'left' },
58
108
  size: 'medium',
59
109
  lineHeight: 1.62,
60
- paragraphSpacing: '1em',
61
110
  },
62
111
  caption: {
63
112
  align: { desktop: 'left', mobile: 'left' },
64
- size: 'small',
113
+ size: 'medium',
65
114
  lineHeight: 1.4,
66
- spacing: '0.6em',
67
115
  },
68
116
  },
69
117
  statement: {
70
- heading: {
71
- align: { desktop: 'left', mobile: 'left' },
72
- size: 'medium',
73
- lineHeight: 1.04,
74
- spacing: '0.5em',
118
+ headings: {
119
+ h1: {
120
+ align: { desktop: 'left', mobile: 'left' },
121
+ size: 'medium',
122
+ lineHeight: 1.02,
123
+ },
124
+ h2: {
125
+ align: { desktop: 'left', mobile: 'left' },
126
+ size: 'medium',
127
+ lineHeight: 1.04,
128
+ },
129
+ h3: {
130
+ align: { desktop: 'left', mobile: 'left' },
131
+ size: 'medium',
132
+ lineHeight: 1.18,
133
+ },
134
+ h4: {
135
+ align: { desktop: 'left', mobile: 'left' },
136
+ size: 'medium',
137
+ lineHeight: 1.42,
138
+ },
75
139
  },
76
140
  body: {
77
141
  align: { desktop: 'left', mobile: 'left' },
78
142
  size: 'medium',
79
143
  lineHeight: 1.42,
80
- paragraphSpacing: '0.75em',
81
144
  },
82
145
  caption: {
83
146
  align: { desktop: 'center', mobile: 'center' },
84
- size: 'small',
147
+ size: 'medium',
85
148
  lineHeight: 1.3,
86
- spacing: '0.45em',
149
+ },
150
+ },
151
+ };
152
+
153
+ export const typographyRhythms = {
154
+ compact: {
155
+ headings: {
156
+ h1: { spacingBefore: '0', spacingAfter: '0.4em' },
157
+ h2: { spacingBefore: '0', spacingAfter: '0.45em' },
158
+ h3: { spacingBefore: '1.2em', spacingAfter: '0.4em' },
159
+ h4: { spacingBefore: '0.9em', spacingAfter: '0.35em' },
160
+ },
161
+ body: {
162
+ paragraphSpacing: '0.65em',
163
+ },
164
+ caption: {
165
+ spacingBefore: '0.35em',
166
+ },
167
+ },
168
+ normal: {
169
+ headings: {
170
+ h1: { spacingBefore: '0', spacingAfter: '0.5em' },
171
+ h2: { spacingBefore: '0', spacingAfter: '0.55em' },
172
+ h3: { spacingBefore: '1.5em', spacingAfter: '0.5em' },
173
+ h4: { spacingBefore: '1.1em', spacingAfter: '0.4em' },
174
+ },
175
+ body: {
176
+ paragraphSpacing: '0.85em',
177
+ },
178
+ caption: {
179
+ spacingBefore: '0.5em',
180
+ },
181
+ },
182
+ airy: {
183
+ headings: {
184
+ h1: { spacingBefore: '0', spacingAfter: '0.65em' },
185
+ h2: { spacingBefore: '0', spacingAfter: '0.7em' },
186
+ h3: { spacingBefore: '1.9em', spacingAfter: '0.6em' },
187
+ h4: { spacingBefore: '1.35em', spacingAfter: '0.5em' },
188
+ },
189
+ body: {
190
+ paragraphSpacing: '1em',
191
+ },
192
+ caption: {
193
+ spacingBefore: '0.65em',
87
194
  },
88
195
  },
89
196
  };
90
197
 
91
198
  export const defaultTypography = {
92
199
  preset: 'quiet-gallery',
200
+ rhythm: 'normal',
93
201
  };
94
202
 
95
203
  const isPlainObject = (value) => (
@@ -116,26 +224,38 @@ export const mergeDeep = (base, override) => {
116
224
 
117
225
  export const resolveTypographyConfig = (typography = defaultTypography) => {
118
226
  const presetName = typography?.preset ?? defaultTypography.preset;
227
+ const rhythmName = typography?.rhythm ?? defaultTypography.rhythm;
119
228
  const preset = typographyPresets[presetName];
229
+ const rhythm = typographyRhythms[rhythmName];
120
230
 
121
231
  if (!preset) {
122
232
  throw new Error(`Unknown typography preset: ${presetName}`);
123
233
  }
124
234
 
235
+ if (!rhythm) {
236
+ throw new Error(`Unknown typography rhythm: ${rhythmName}`);
237
+ }
238
+
125
239
  return {
126
240
  preset: presetName,
127
- values: mergeDeep(preset, typography?.overrides),
241
+ rhythm: rhythmName,
242
+ values: mergeDeep(mergeDeep(preset, rhythm), typography?.overrides),
128
243
  };
129
244
  };
130
245
 
131
246
  export const resolveTypographyOverride = (baseResolved, typographyConfig) => {
132
- if (typographyConfig?.preset) {
133
- return resolveTypographyConfig(typographyConfig);
247
+ if (typographyConfig?.preset || typographyConfig?.rhythm) {
248
+ return resolveTypographyConfig({
249
+ preset: typographyConfig.preset ?? baseResolved.preset,
250
+ rhythm: typographyConfig.rhythm ?? baseResolved.rhythm,
251
+ overrides: typographyConfig.overrides,
252
+ });
134
253
  }
135
254
 
136
255
  if (typographyConfig?.overrides) {
137
256
  return {
138
257
  preset: baseResolved.preset,
258
+ rhythm: baseResolved.rhythm,
139
259
  values: mergeDeep(baseResolved.values, typographyConfig.overrides),
140
260
  };
141
261
  }