@janga/norna 0.7.3 → 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 (87) hide show
  1. package/docs/README.md +3 -3
  2. package/docs/commands.md +2 -2
  3. package/docs/configuration.md +2 -1
  4. package/docs/design/command-organization.md +4 -3
  5. package/docs/design/site-examples-structure.md +4 -3
  6. package/docs/getting-started.md +8 -7
  7. package/docs/theme.md +27 -43
  8. package/docs/typography.md +47 -31
  9. package/examples/dog-gallery/site/content.md +1 -1
  10. package/examples/dog-gallery/site/theme.md +12 -14
  11. package/package.json +1 -1
  12. package/scripts/check-config.mjs +1 -0
  13. package/scripts/lib/project-config.mjs +87 -32
  14. package/scripts/lib/site-content.mjs +4 -4
  15. package/scripts/lib/typography.mjs +79 -39
  16. package/scripts/show-typography.mjs +75 -11
  17. package/scripts/test-engine-commands.mjs +18 -0
  18. package/scripts/test-package-check.mjs +19 -3
  19. package/src/components/SiteSection.astro +82 -20
  20. package/src/content.config.ts +11 -9
  21. package/src/layouts/BaseLayout.astro +0 -6
  22. package/src/styles/global.css +44 -17
  23. package/starters/basic/README.md +4 -3
  24. package/starters/basic/site/theme.md +19 -46
  25. package/starters/project/README.md +7 -7
  26. package/starters/project/package-lock.json +4 -4
  27. package/starters/project/package.json +1 -1
  28. package/starters/project/site/content.md +86 -46
  29. package/starters/project/site/routes/guide/route-content.md +52 -25
  30. package/starters/project/site/theme.md +29 -1
  31. package/examples/dog-gallery/.astro/collections/site.schema.json +0 -802
  32. package/examples/dog-gallery/.astro/collections/theme.schema.json +0 -714
  33. package/examples/dog-gallery/.astro/content-assets.mjs +0 -4
  34. package/examples/dog-gallery/.astro/content-modules.mjs +0 -1
  35. package/examples/dog-gallery/.astro/content.d.ts +0 -175
  36. package/examples/dog-gallery/.astro/data-store.json +0 -1
  37. package/examples/dog-gallery/.astro/dev-local.json +0 -8
  38. package/examples/dog-gallery/.astro/dev.json +0 -13
  39. package/examples/dog-gallery/.astro/dev.log +0 -49
  40. package/examples/dog-gallery/.astro/settings.json +0 -5
  41. package/examples/dog-gallery/.astro/types.d.ts +0 -2
  42. package/examples/dog-gallery/dist/_astro/GalleryGrid.astro_astro_type_script_index_0_lang.B0kxDyGz.js +0 -1
  43. package/examples/dog-gallery/dist/_astro/SitePage.BFHZm5UG.css +0 -1
  44. package/examples/dog-gallery/dist/dog-care/index.html +0 -15
  45. package/examples/dog-gallery/dist/favicon.svg +0 -7
  46. package/examples/dog-gallery/dist/images/generated/black-dogs/black-puppy-meadow-7255e3d9-1080.webp +0 -0
  47. package/examples/dog-gallery/dist/images/generated/black-dogs/black-puppy-meadow-7255e3d9-1440.webp +0 -0
  48. package/examples/dog-gallery/dist/images/generated/black-dogs/black-puppy-meadow-7255e3d9-1536.webp +0 -0
  49. package/examples/dog-gallery/dist/images/generated/black-dogs/black-puppy-meadow-7255e3d9-480.webp +0 -0
  50. package/examples/dog-gallery/dist/images/generated/black-dogs/black-puppy-meadow-7255e3d9-768.webp +0 -0
  51. package/examples/dog-gallery/dist/images/generated/black-dogs/photo-of-a-black-dog-5cf614d2-480.webp +0 -0
  52. package/examples/dog-gallery/dist/images/generated/black-dogs/photo-of-a-black-dog-5cf614d2-768.webp +0 -0
  53. package/examples/dog-gallery/dist/images/generated/black-dogs/photo-of-a-black-dog-5cf614d2-960.webp +0 -0
  54. package/examples/dog-gallery/dist/images/generated/brown-dogs/brown-dog-50ef114e-1080.webp +0 -0
  55. package/examples/dog-gallery/dist/images/generated/brown-dogs/brown-dog-50ef114e-1440.webp +0 -0
  56. package/examples/dog-gallery/dist/images/generated/brown-dogs/brown-dog-50ef114e-1920.webp +0 -0
  57. package/examples/dog-gallery/dist/images/generated/brown-dogs/brown-dog-50ef114e-3939.webp +0 -0
  58. package/examples/dog-gallery/dist/images/generated/brown-dogs/brown-dog-50ef114e-480.webp +0 -0
  59. package/examples/dog-gallery/dist/images/generated/brown-dogs/brown-dog-50ef114e-768.webp +0 -0
  60. package/examples/dog-gallery/dist/images/generated/brown-dogs/dog-accompanies-master-7ef804d5-1080.webp +0 -0
  61. package/examples/dog-gallery/dist/images/generated/brown-dogs/dog-accompanies-master-7ef804d5-1440.webp +0 -0
  62. package/examples/dog-gallery/dist/images/generated/brown-dogs/dog-accompanies-master-7ef804d5-1920.webp +0 -0
  63. package/examples/dog-gallery/dist/images/generated/brown-dogs/dog-accompanies-master-7ef804d5-2400.webp +0 -0
  64. package/examples/dog-gallery/dist/images/generated/brown-dogs/dog-accompanies-master-7ef804d5-480.webp +0 -0
  65. package/examples/dog-gallery/dist/images/generated/brown-dogs/dog-accompanies-master-7ef804d5-768.webp +0 -0
  66. package/examples/dog-gallery/dist/images/generated/golden-dogs/golden-retriever-f797fd88-1080.webp +0 -0
  67. package/examples/dog-gallery/dist/images/generated/golden-dogs/golden-retriever-f797fd88-1440.webp +0 -0
  68. package/examples/dog-gallery/dist/images/generated/golden-dogs/golden-retriever-f797fd88-1600.webp +0 -0
  69. package/examples/dog-gallery/dist/images/generated/golden-dogs/golden-retriever-f797fd88-480.webp +0 -0
  70. package/examples/dog-gallery/dist/images/generated/golden-dogs/golden-retriever-f797fd88-768.webp +0 -0
  71. package/examples/dog-gallery/dist/images/generated/golden-dogs/toller-puppy-a2a8fbc0-1080.webp +0 -0
  72. package/examples/dog-gallery/dist/images/generated/golden-dogs/toller-puppy-a2a8fbc0-1440.webp +0 -0
  73. package/examples/dog-gallery/dist/images/generated/golden-dogs/toller-puppy-a2a8fbc0-1920.webp +0 -0
  74. package/examples/dog-gallery/dist/images/generated/golden-dogs/toller-puppy-a2a8fbc0-2576.webp +0 -0
  75. package/examples/dog-gallery/dist/images/generated/golden-dogs/toller-puppy-a2a8fbc0-480.webp +0 -0
  76. package/examples/dog-gallery/dist/images/generated/golden-dogs/toller-puppy-a2a8fbc0-768.webp +0 -0
  77. package/examples/dog-gallery/dist/images/generated/white-dogs/white-cute-dog-077a0689-1080.webp +0 -0
  78. package/examples/dog-gallery/dist/images/generated/white-dogs/white-cute-dog-077a0689-1277.webp +0 -0
  79. package/examples/dog-gallery/dist/images/generated/white-dogs/white-cute-dog-077a0689-480.webp +0 -0
  80. package/examples/dog-gallery/dist/images/generated/white-dogs/white-cute-dog-077a0689-768.webp +0 -0
  81. package/examples/dog-gallery/dist/images/generated/white-dogs/white-puppy-garden-d3ab2ca0-1080.webp +0 -0
  82. package/examples/dog-gallery/dist/images/generated/white-dogs/white-puppy-garden-d3ab2ca0-1440.webp +0 -0
  83. package/examples/dog-gallery/dist/images/generated/white-dogs/white-puppy-garden-d3ab2ca0-1536.webp +0 -0
  84. package/examples/dog-gallery/dist/images/generated/white-dogs/white-puppy-garden-d3ab2ca0-480.webp +0 -0
  85. package/examples/dog-gallery/dist/images/generated/white-dogs/white-puppy-garden-d3ab2ca0-768.webp +0 -0
  86. package/examples/dog-gallery/dist/index.html +0 -43
  87. package/examples/dog-gallery/dist/robots.txt +0 -2
@@ -35,22 +35,78 @@ const HeadingTag = headingLevel === 1 ? 'h1' : 'h2';
35
35
  // Sections are authored with Markdown ## headings even when the first one renders as HTML h1.
36
36
  const sectionHeadingVisualLevel = 'h2';
37
37
  type HeadingLevel = 'h1' | 'h2' | 'h3' | 'h4';
38
- const headingSizeValues: Record<TextSize, ResponsiveValue<string>> = {
39
- small: {
40
- desktop: 'clamp(1.1rem, 1.02rem + 0.35vw, 1.35rem)',
41
- mobile: 'clamp(1.12rem, 1rem + 0.85vw, 1.42rem)',
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
+ },
42
56
  },
43
- medium: {
44
- desktop: 'clamp(1.35rem, 1.1rem + 0.9vw, 2rem)',
45
- mobile: 'clamp(1.3rem, 1.05rem + 1.35vw, 1.75rem)',
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
+ },
46
74
  },
47
- large: {
48
- desktop: 'clamp(1.6rem, 1.22rem + 1.45vw, 2.75rem)',
49
- mobile: 'clamp(1.5rem, 1.12rem + 2vw, 2.25rem)',
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
+ },
50
92
  },
51
- xlarge: {
52
- desktop: 'clamp(2rem, 1.35rem + 2.4vw, 3.75rem)',
53
- mobile: 'clamp(1.8rem, 1.25rem + 3vw, 2.85rem)',
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
+ },
54
110
  },
55
111
  };
56
112
  const bodySizeValues: Record<TextSize, ResponsiveValue<string>> = {
@@ -94,6 +150,9 @@ const getSizeValue = (
94
150
  sizeValues: Record<TextSize, ResponsiveValue<string>>,
95
151
  size: TextSize,
96
152
  ) => sizeValues[size];
153
+ const getHeadingSizeValue = (level: HeadingLevel, size: TextSize) => (
154
+ headingSizeValues[level][size]
155
+ );
97
156
  const backgroundColor = sectionOverride?.backgroundColor
98
157
  ?? pagePresentation.backgroundColor;
99
158
  const textColor = sectionOverride?.textColor
@@ -104,7 +163,8 @@ const heading = typography.headings[sectionHeadingVisualLevel] as {
104
163
  align: ResponsiveValue<TextAlign>;
105
164
  size: TextSize;
106
165
  lineHeight: number;
107
- spacing: string;
166
+ spacingBefore: string;
167
+ spacingAfter: string;
108
168
  };
109
169
  const markdownHeadingLevels = ['h3', 'h4'] as const;
110
170
  const markdownHeadings = Object.fromEntries(markdownHeadingLevels.map((level) => [
@@ -113,13 +173,14 @@ const markdownHeadings = Object.fromEntries(markdownHeadingLevels.map((level) =>
113
173
  align: ResponsiveValue<TextAlign>;
114
174
  size: TextSize;
115
175
  lineHeight: number;
116
- spacing: string;
176
+ spacingBefore: string;
177
+ spacingAfter: string;
117
178
  },
118
179
  ]));
119
180
  const headingAlign = heading.align;
120
181
  const bodyAlign = typography.body.align as ResponsiveValue<TextAlign>;
121
182
  const captionAlign = typography.caption.align as ResponsiveValue<TextAlign>;
122
- const headingFontSize = getSizeValue(headingSizeValues, heading.size);
183
+ const headingFontSize = getHeadingSizeValue(sectionHeadingVisualLevel, heading.size);
123
184
  const bodyFontSize = getSizeValue(bodySizeValues, typography.body.size as TextSize);
124
185
  const captionFontSize = getSizeValue(captionSizeValues, typography.caption.size as TextSize);
125
186
  const justifyByAlign: Record<TextAlign, string> = {
@@ -139,7 +200,7 @@ const bodyWidth = getAlignedTextWidth(bodyAlign, 'var(--text-width)');
139
200
  const hasGallery = section.gallery.length > 0;
140
201
  const markdownHeadingStyle = (level: 'h3' | 'h4') => {
141
202
  const headingConfig = markdownHeadings[level];
142
- const fontSize = getSizeValue(headingSizeValues, headingConfig.size);
203
+ const fontSize = getHeadingSizeValue(level, headingConfig.size);
143
204
 
144
205
  return [
145
206
  `--section-markdown-${level}-align-desktop: ${headingConfig.align.desktop}`,
@@ -147,7 +208,8 @@ const markdownHeadingStyle = (level: 'h3' | 'h4') => {
147
208
  `--section-markdown-${level}-font-size-desktop: ${fontSize.desktop}`,
148
209
  `--section-markdown-${level}-font-size-mobile: ${fontSize.mobile}`,
149
210
  `--section-markdown-${level}-line-height: ${headingConfig.lineHeight}`,
150
- `--section-markdown-${level}-spacing: ${headingConfig.spacing}`,
211
+ `--section-markdown-${level}-spacing-before: ${headingConfig.spacingBefore}`,
212
+ `--section-markdown-${level}-spacing-after: ${headingConfig.spacingAfter}`,
151
213
  ];
152
214
  };
153
215
  const sectionStyle = [
@@ -158,7 +220,7 @@ const sectionStyle = [
158
220
  `--section-heading-font-size-desktop: ${headingFontSize.desktop}`,
159
221
  `--section-heading-font-size-mobile: ${headingFontSize.mobile}`,
160
222
  `--section-heading-line-height: ${heading.lineHeight}`,
161
- `--section-heading-spacing: ${heading.spacing}`,
223
+ `--section-heading-spacing-after: ${heading.spacingAfter}`,
162
224
  `--section-heading-width-desktop: ${headingWidth.desktop}`,
163
225
  `--section-heading-width-mobile: ${headingWidth.mobile}`,
164
226
  ...markdownHeadingStyle('h3'),
@@ -176,7 +238,7 @@ const sectionStyle = [
176
238
  `--section-caption-font-size-desktop: ${captionFontSize.desktop}`,
177
239
  `--section-caption-font-size-mobile: ${captionFontSize.mobile}`,
178
240
  `--section-caption-line-height: ${typography.caption.lineHeight}`,
179
- `--section-caption-spacing: ${typography.caption.spacing}`,
241
+ `--section-caption-spacing-before: ${typography.caption.spacingBefore}`,
180
242
  `--section-caption-justify-desktop: ${justifyByAlign[captionAlign.desktop]}`,
181
243
  `--section-caption-justify-mobile: ${justifyByAlign[captionAlign.mobile]}`,
182
244
  ].join('; ');
@@ -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.');
@@ -59,7 +60,8 @@ const commonTextPresentationOverride = {
59
60
  };
60
61
  const headingPresentationOverride = z.object({
61
62
  ...commonTextPresentationOverride,
62
- spacing: cssLength.optional(),
63
+ spacingBefore: cssLength.optional(),
64
+ spacingAfter: cssLength.optional(),
63
65
  }).strict();
64
66
  const headingLevelsPresentationOverride = z.object({
65
67
  h1: headingPresentationOverride.optional(),
@@ -73,7 +75,7 @@ const bodyPresentationOverride = z.object({
73
75
  }).strict();
74
76
  const captionPresentationOverride = z.object({
75
77
  ...commonTextPresentationOverride,
76
- spacing: cssLength.optional(),
78
+ spacingBefore: cssLength.optional(),
77
79
  }).strict();
78
80
  const typographyOverrides = z.object({
79
81
  headings: headingLevelsPresentationOverride.optional(),
@@ -82,20 +84,22 @@ const typographyOverrides = z.object({
82
84
  }).strict();
83
85
  const typography = z.object({
84
86
  preset: typographyPreset.optional(),
87
+ rhythm: spacingDensity.optional(),
85
88
  overrides: typographyOverrides.optional(),
86
89
  }).strict().refine(
87
- (value) => value.preset !== undefined || value.overrides !== undefined,
88
- 'Specify preset, overrides, or both.',
90
+ (value) => value.preset !== undefined || value.rhythm !== undefined || value.overrides !== undefined,
91
+ 'Specify preset, rhythm, overrides, or both.',
89
92
  );
90
93
  const themeTypography = z.object({
91
94
  fontFamily: z.string()
92
95
  .min(1)
93
96
  .refine((value) => !/[\n\r;{}]/.test(value), 'Do not use semicolons, braces, or line breaks.').optional(),
94
97
  preset: typographyPreset.optional(),
98
+ rhythm: spacingDensity.optional(),
95
99
  overrides: typographyOverrides.optional(),
96
100
  }).strict().refine(
97
- (value) => value.fontFamily !== undefined || value.preset !== undefined || value.overrides !== undefined,
98
- 'Specify fontFamily, 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.',
99
103
  );
100
104
  const responsiveCssLength = z.union([
101
105
  visualCssLength,
@@ -117,11 +121,9 @@ const themeLayoutSpacing = z.object({
117
121
  firstSectionTop: responsiveCssLength.optional(),
118
122
  imageGap: responsiveCssLength.optional(),
119
123
  sectionGap: responsiveCssLength.optional(),
120
- sectionHeadingToBody: responsiveCssLength.optional(),
121
- subheadingRuleTop: responsiveCssLength.optional(),
122
- subheadingTop: responsiveCssLength.optional(),
123
124
  }).strict();
124
125
  const themeLayout = z.object({
126
+ density: spacingDensity.optional(),
125
127
  pageWidth: visualCssLength.optional(),
126
128
  gutter: responsiveCssLength.optional(),
127
129
  spacing: themeLayoutSpacing.optional(),
@@ -38,8 +38,6 @@ const documentStyle = [
38
38
  `--content-gutter-mobile: ${projectConfig.layout.gutter.mobile}`,
39
39
  `--space-nav-to-first-section-desktop: ${layoutSpacing.firstSectionTop.desktop}`,
40
40
  `--space-nav-to-first-section-mobile: ${layoutSpacing.firstSectionTop.mobile}`,
41
- `--space-section-title-to-body-desktop: ${layoutSpacing.sectionHeadingToBody.desktop}`,
42
- `--space-section-title-to-body-mobile: ${layoutSpacing.sectionHeadingToBody.mobile}`,
43
41
  `--space-section-body-to-gallery-desktop: ${layoutSpacing.bodyToImages.desktop}`,
44
42
  `--space-section-body-to-gallery-mobile: ${layoutSpacing.bodyToImages.mobile}`,
45
43
  `--space-gallery-item-desktop: ${layoutSpacing.imageGap.desktop}`,
@@ -48,10 +46,6 @@ const documentStyle = [
48
46
  `--space-section-to-section-mobile: ${layoutSpacing.sectionGap.mobile}`,
49
47
  `--space-final-section-bottom-desktop: ${layoutSpacing.finalSectionBottom.desktop}`,
50
48
  `--space-final-section-bottom-mobile: ${layoutSpacing.finalSectionBottom.mobile}`,
51
- `--space-subheading-top-desktop: ${layoutSpacing.subheadingTop.desktop}`,
52
- `--space-subheading-top-mobile: ${layoutSpacing.subheadingTop.mobile}`,
53
- `--space-subheading-rule-top-desktop: ${layoutSpacing.subheadingRuleTop.desktop}`,
54
- `--space-subheading-rule-top-mobile: ${layoutSpacing.subheadingRuleTop.mobile}`,
55
49
  `--gallery-width: ${projectConfig.gallery.width}`,
56
50
  `--gallery-max-available-width-desktop: ${formatPercent(projectConfig.gallery.maxAvailableWidthPercent.desktop)}`,
57
51
  `--gallery-max-available-width-mobile: ${formatPercent(projectConfig.gallery.maxAvailableWidthPercent.mobile)}`,
@@ -31,9 +31,6 @@
31
31
  --space-nav-to-first-section-desktop: clamp(1.875rem, 3vw, 2.75rem);
32
32
  --space-nav-to-first-section-mobile: 1.375rem;
33
33
  --space-nav-to-first-section: var(--space-nav-to-first-section-desktop);
34
- --space-section-title-to-body-desktop: clamp(0.5625rem, 1.25vw, 0.75rem);
35
- --space-section-title-to-body-mobile: 0.4375rem;
36
- --space-section-title-to-body: var(--space-section-title-to-body-desktop);
37
34
  --space-section-body-to-gallery-desktop: clamp(1.25rem, 2.5vw, 2rem);
38
35
  --space-section-body-to-gallery-mobile: 1.25rem;
39
36
  --space-section-body-to-gallery: var(--space-section-body-to-gallery-desktop);
@@ -46,12 +43,6 @@
46
43
  --space-final-section-bottom-desktop: var(--space-section-to-section-desktop);
47
44
  --space-final-section-bottom-mobile: var(--space-section-to-section-mobile);
48
45
  --space-final-section-bottom: var(--space-final-section-bottom-desktop);
49
- --space-subheading-top-desktop: 2rem;
50
- --space-subheading-top-mobile: 1.6rem;
51
- --space-subheading-top: var(--space-subheading-top-desktop);
52
- --space-subheading-rule-top-desktop: 1rem;
53
- --space-subheading-rule-top-mobile: 0.8rem;
54
- --space-subheading-rule-top: var(--space-subheading-rule-top-desktop);
55
46
 
56
47
  --content-gutter-desktop: clamp(1.25rem, 4vw, 3rem);
57
48
  --content-gutter-mobile: 1rem;
@@ -337,7 +328,7 @@ a {
337
328
  .section-header {
338
329
  width: min(100%, var(--section-heading-width-desktop, 760px));
339
330
  margin: 0 auto;
340
- padding: 0 0 var(--section-heading-spacing, var(--space-section-title-to-body));
331
+ padding: 0 0 var(--section-heading-spacing-after, 0.55em);
341
332
  text-align: var(--section-heading-align-desktop, center);
342
333
  }
343
334
 
@@ -380,6 +371,31 @@ ul {
380
371
  margin: 0 0 var(--section-body-paragraph-spacing, 1em);
381
372
  }
382
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
+
383
399
  .section-markdown .inline-style {
384
400
  color: var(--inline-style-color);
385
401
  }
@@ -393,10 +409,21 @@ ul {
393
409
  margin-bottom: 0;
394
410
  }
395
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
+
396
420
  .section-markdown h3 {
397
- margin: var(--space-subheading-top) 0 var(--section-markdown-h3-spacing, 0.9rem);
421
+ margin:
422
+ var(--section-markdown-h3-spacing-before, 1.5em)
423
+ 0
424
+ var(--section-markdown-h3-spacing-after, 0.5em);
398
425
  border-top: 1px solid var(--color-soft);
399
- padding-top: var(--space-subheading-rule-top);
426
+ padding-top: 0.55em;
400
427
  font-family: var(--font-sans);
401
428
  font-size: var(--section-markdown-h3-font-size-desktop, 0.82rem);
402
429
  letter-spacing: 0;
@@ -406,7 +433,10 @@ ul {
406
433
  }
407
434
 
408
435
  .section-markdown h4 {
409
- margin: 0 0 var(--section-markdown-h4-spacing, 0.85rem);
436
+ margin:
437
+ var(--section-markdown-h4-spacing-before, 1.1em)
438
+ 0
439
+ var(--section-markdown-h4-spacing-after, 0.4em);
410
440
  color: inherit;
411
441
  font-size: var(--section-markdown-h4-font-size-desktop, clamp(0.96rem, 0.94rem + 0.08vw, 1rem));
412
442
  font-weight: 400;
@@ -625,7 +655,7 @@ ul {
625
655
 
626
656
  .gallery-meta {
627
657
  width: min(100%, var(--gallery-layout-width));
628
- margin: var(--section-caption-spacing, 0.5em) auto 0;
658
+ margin: var(--section-caption-spacing-before, 0.5em) auto 0;
629
659
  font-size: var(--section-caption-font-size-desktop, 0.95rem);
630
660
  text-align: var(--section-caption-align-desktop, center);
631
661
  }
@@ -647,13 +677,10 @@ ul {
647
677
  @media (max-width: 700px) {
648
678
  :root {
649
679
  --space-nav-to-first-section: var(--space-nav-to-first-section-mobile);
650
- --space-section-title-to-body: var(--space-section-title-to-body-mobile);
651
680
  --space-section-body-to-gallery: var(--space-section-body-to-gallery-mobile);
652
681
  --space-gallery-item: var(--space-gallery-item-mobile);
653
682
  --space-section-to-section: var(--space-section-to-section-mobile);
654
683
  --space-final-section-bottom: var(--space-final-section-bottom-mobile);
655
- --space-subheading-top: var(--space-subheading-top-mobile);
656
- --space-subheading-rule-top: var(--space-subheading-rule-top-mobile);
657
684
  --content-gutter: var(--content-gutter-mobile);
658
685
  --gallery-max-available-width: var(--gallery-max-available-width-mobile);
659
686
  --gallery-max-image-height: var(--gallery-max-image-height-mobile);
@@ -32,9 +32,10 @@ site: {
32
32
 
33
33
  In GitHub repository settings, configure Pages to build from GitHub Actions.
34
34
 
35
- Page width, side gutters, vertical spacing, image width, image height limits,
36
- font, colors, and site-wide typography are configured in `site/theme.md`. Page
37
- and section presentation 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`.
38
39
 
39
40
  Commit `package-lock.json` after the first install so GitHub Actions can use
40
41
  `npm ci`.
@@ -7,6 +7,7 @@
7
7
  # navigation:
8
8
  # brand: site-wide brand/home text; defaults to the homepage title
9
9
  # layout:
10
+ # density: compact | normal | airy
10
11
  # pageWidth: CSS length, e.g. 1180px
11
12
  # gutter:
12
13
  # desktop: CSS length or clamp(), e.g. clamp(1.25rem, 4vw, 3rem)
@@ -16,26 +17,17 @@
16
17
  # desktop: CSS length or clamp(), e.g. clamp(1.875rem, 3vw, 2.75rem)
17
18
  # mobile: CSS length, e.g. 1.375rem
18
19
  # sectionGap:
19
- # desktop: CSS length or clamp(), e.g. clamp(2.55rem, 4.2vw, 3.9rem)
20
- # mobile: CSS length, e.g. 2.25rem
20
+ # desktop: CSS length or clamp(), e.g. clamp(1.4rem, 3vw, 2.75rem)
21
+ # mobile: CSS length, e.g. 1.5rem
21
22
  # finalSectionBottom:
22
- # desktop: CSS length or clamp(), e.g. clamp(2.55rem, 4.2vw, 3.9rem)
23
- # mobile: CSS length, e.g. 2.25rem
24
- # sectionHeadingToBody:
25
- # desktop: CSS length or clamp(), e.g. clamp(0.5625rem, 1.25vw, 0.75rem)
26
- # mobile: CSS length, e.g. 0.4375rem
23
+ # desktop: CSS length or clamp(), e.g. clamp(1.4rem, 3vw, 2.75rem)
24
+ # mobile: CSS length, e.g. 1.5rem
27
25
  # bodyToImages:
28
- # desktop: CSS length or clamp(), e.g. clamp(1.25rem, 2.5vw, 2rem)
29
- # mobile: CSS length, e.g. 1.25rem
26
+ # desktop: CSS length, e.g. 1.25rem
27
+ # mobile: CSS length, e.g. 1rem
30
28
  # imageGap:
31
- # desktop: CSS length or clamp(), e.g. clamp(1.5rem, 3.5vw, 2.75rem)
32
- # mobile: CSS length, e.g. 2.75rem
33
- # subheadingTop:
34
- # desktop: CSS length, e.g. 2rem
35
- # mobile: CSS length, e.g. 1.6rem
36
- # subheadingRuleTop:
37
- # desktop: CSS length, e.g. 1rem
38
- # mobile: CSS length, e.g. 0.8rem
29
+ # desktop: CSS length or clamp(), e.g. clamp(1.25rem, 2.8vw, 2rem)
30
+ # mobile: CSS length, e.g. 1.5rem
39
31
  # gallery:
40
32
  # width: CSS length, e.g. 900px
41
33
  # maxAvailableWidthPercent:
@@ -47,6 +39,7 @@
47
39
  # typography:
48
40
  # fontFamily: CSS font-family stack
49
41
  # preset: quiet-gallery | compact-gallery | text-forward | statement
42
+ # rhythm: compact | normal | airy
50
43
  # overrides:
51
44
  # headings:
52
45
  # h2:
@@ -55,21 +48,24 @@
55
48
  # mobile: left | center | right
56
49
  # size: small | medium | large | xlarge
57
50
  # lineHeight: number from 1 through 3
58
- # spacing: CSS length, e.g. 0.65em
51
+ # spacingBefore: CSS length, e.g. 0
52
+ # spacingAfter: CSS length, e.g. 0.55em
59
53
  # h3:
60
54
  # align:
61
55
  # desktop: left | center | right
62
56
  # mobile: left | center | right
63
57
  # size: small | medium | large | xlarge
64
58
  # lineHeight: number from 1 through 3
65
- # spacing: CSS length, e.g. 0.9rem
59
+ # spacingBefore: CSS length, e.g. 1.5em
60
+ # spacingAfter: CSS length, e.g. 0.5em
66
61
  # h4:
67
62
  # align:
68
63
  # desktop: left | center | right
69
64
  # mobile: left | center | right
70
65
  # size: small | medium | large | xlarge
71
66
  # lineHeight: number from 1 through 3
72
- # spacing: CSS length, e.g. 0.85rem
67
+ # spacingBefore: CSS length, e.g. 1.1em
68
+ # spacingAfter: CSS length, e.g. 0.4em
73
69
  # body:
74
70
  # align:
75
71
  # desktop: left | center | right
@@ -83,7 +79,7 @@
83
79
  # mobile: left | center | right
84
80
  # size: small | medium | large | xlarge
85
81
  # lineHeight: number from 1 through 3
86
- # spacing: CSS length, e.g. 0.5em
82
+ # spacingBefore: CSS length, e.g. 0.5em
87
83
  # presentation:
88
84
  # backgroundColor: quoted hex color, e.g. "#000000"
89
85
  # textColor: quoted hex color, e.g. "#f7f4ee"
@@ -99,35 +95,11 @@
99
95
  navigation:
100
96
  brand: Example Site
101
97
  layout:
98
+ density: normal
102
99
  pageWidth: 1180px
103
100
  gutter:
104
101
  desktop: clamp(1.25rem, 4vw, 3rem)
105
102
  mobile: 1rem
106
- spacing:
107
- firstSectionTop:
108
- desktop: clamp(1.875rem, 3vw, 2.75rem)
109
- mobile: 1.375rem
110
- sectionGap:
111
- desktop: clamp(2.55rem, 4.2vw, 3.9rem)
112
- mobile: 2.25rem
113
- finalSectionBottom:
114
- desktop: clamp(2.55rem, 4.2vw, 3.9rem)
115
- mobile: 2.25rem
116
- sectionHeadingToBody:
117
- desktop: clamp(0.5625rem, 1.25vw, 0.75rem)
118
- mobile: 0.4375rem
119
- bodyToImages:
120
- desktop: clamp(1.25rem, 2.5vw, 2rem)
121
- mobile: 1.25rem
122
- imageGap:
123
- desktop: clamp(1.5rem, 3.5vw, 2.75rem)
124
- mobile: 2.75rem
125
- subheadingTop:
126
- desktop: 2rem
127
- mobile: 1.6rem
128
- subheadingRuleTop:
129
- desktop: 1rem
130
- mobile: 0.8rem
131
103
  gallery:
132
104
  width: 900px
133
105
  maxAvailableWidthPercent:
@@ -139,6 +111,7 @@ gallery:
139
111
  typography:
140
112
  fontFamily: "Arial, 'Helvetica Neue', Helvetica, sans-serif"
141
113
  preset: quiet-gallery
114
+ rhythm: normal
142
115
  presentation:
143
116
  backgroundColor: "#000000"
144
117
  textColor: "#f7f4ee"
@@ -20,10 +20,10 @@ npm run dev -- --kill
20
20
 
21
21
  - `site/config.mjs`: technical settings such as URL, language labels, GitHub
22
22
  repository, and deploy workflow.
23
- - `site/theme.md`: site-wide visual settings such as brand text, colors,
24
- frame colors, and inline text styles.
25
- - `site/content.md`: homepage title, sections, project summary, links,
26
- install command, example usage, features, and next steps.
23
+ - `site/theme.md`: site-wide visual settings such as brand text, layout
24
+ density, typography rhythm, colors, frame colors, and inline text styles.
25
+ - `site/content.md`: homepage title, sections, placeholders, project summary,
26
+ links, install command, example usage, benefits, next steps, and license.
27
27
  - `site/routes/guide/route-content.md`: short secondary page with realistic
28
28
  project guide content.
29
29
  - `site/public/robots.txt`: static public file copied into the built site.
@@ -35,11 +35,11 @@ GitHub Pages workflow uses `npm ci`.
35
35
  ## Adapt The Starter
36
36
 
37
37
  1. Replace the project name, tagline, links, install command, example usage,
38
- features, and use cases in `site/content.md`.
38
+ benefits, use cases, and license in `site/content.md`.
39
39
  2. Replace the guide examples in `site/routes/guide/route-content.md`, or
40
40
  delete the route if the homepage is enough.
41
- 3. Edit `site/theme.md` for brand name, colors, frame colors, and inline text
42
- styles.
41
+ 3. Edit `site/theme.md` for brand name, layout density, typography rhythm,
42
+ colors, frame colors, and inline text styles.
43
43
  4. Edit `site/config.mjs` for public URL, GitHub repository, footer text, and
44
44
  deploy workflow name.
45
45
  5. Update `package.json` with the site's package name and keep
@@ -6,7 +6,7 @@
6
6
  "": {
7
7
  "name": "project-site",
8
8
  "dependencies": {
9
- "@janga/norna": "0.7.2"
9
+ "@janga/norna": "0.7.3"
10
10
  }
11
11
  },
12
12
  "node_modules/@astrojs/compiler-binding": {
@@ -1503,9 +1503,9 @@
1503
1503
  }
1504
1504
  },
1505
1505
  "node_modules/@janga/norna": {
1506
- "version": "0.7.2",
1507
- "resolved": "https://registry.npmjs.org/@janga/norna/-/norna-0.7.2.tgz",
1508
- "integrity": "sha512-N0KPzTZmrBv+eABMqniJY8UQwDh4wM4dupsuahadUzum9bYFZxN1Mi0TWfV8rKOMHzzqA0gaiRYCY4ldIOQ9cw==",
1506
+ "version": "0.7.3",
1507
+ "resolved": "https://registry.npmjs.org/@janga/norna/-/norna-0.7.3.tgz",
1508
+ "integrity": "sha512-2Op2fgDpdZWBarVswRgw8oyKQmssbYMcA8fXv2n2Y1UH8h1YDSbSt7l1WNzXXDlYkelOLAmlJAkgZ9rGIIN8xA==",
1509
1509
  "license": "GPL-3.0-only",
1510
1510
  "dependencies": {
1511
1511
  "astro": "^7.1.3",
@@ -30,6 +30,6 @@
30
30
  "build": "npm run norna:build"
31
31
  },
32
32
  "dependencies": {
33
- "@janga/norna": "0.7.2"
33
+ "@janga/norna": "0.7.3"
34
34
  }
35
35
  }