@cogenta/theme-restaurant 0.3.3 → 0.5.3

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 (138) hide show
  1. package/dist/src/index.d.ts +3 -0
  2. package/dist/src/index.d.ts.map +1 -1
  3. package/dist/src/index.js +3 -0
  4. package/dist/src/index.js.map +1 -1
  5. package/dist/src/render/blocks/accordion.d.ts +7 -0
  6. package/dist/src/render/blocks/accordion.d.ts.map +1 -1
  7. package/dist/src/render/blocks/accordion.js +10 -16
  8. package/dist/src/render/blocks/accordion.js.map +1 -1
  9. package/dist/src/render/blocks/collection-list.d.ts +4 -11
  10. package/dist/src/render/blocks/collection-list.d.ts.map +1 -1
  11. package/dist/src/render/blocks/collection-list.js +96 -96
  12. package/dist/src/render/blocks/collection-list.js.map +1 -1
  13. package/dist/src/render/blocks/cta.d.ts +7 -4
  14. package/dist/src/render/blocks/cta.d.ts.map +1 -1
  15. package/dist/src/render/blocks/cta.js +9 -7
  16. package/dist/src/render/blocks/cta.js.map +1 -1
  17. package/dist/src/render/blocks/embed.d.ts.map +1 -1
  18. package/dist/src/render/blocks/embed.js +30 -28
  19. package/dist/src/render/blocks/embed.js.map +1 -1
  20. package/dist/src/render/blocks/faq.d.ts +13 -1
  21. package/dist/src/render/blocks/faq.d.ts.map +1 -1
  22. package/dist/src/render/blocks/faq.js +15 -11
  23. package/dist/src/render/blocks/faq.js.map +1 -1
  24. package/dist/src/render/blocks/feature-grid.d.ts.map +1 -1
  25. package/dist/src/render/blocks/feature-grid.js +43 -16
  26. package/dist/src/render/blocks/feature-grid.js.map +1 -1
  27. package/dist/src/render/blocks/gallery.d.ts +0 -7
  28. package/dist/src/render/blocks/gallery.d.ts.map +1 -1
  29. package/dist/src/render/blocks/gallery.js +28 -10
  30. package/dist/src/render/blocks/gallery.js.map +1 -1
  31. package/dist/src/render/blocks/hero.d.ts +16 -9
  32. package/dist/src/render/blocks/hero.d.ts.map +1 -1
  33. package/dist/src/render/blocks/hero.js +27 -20
  34. package/dist/src/render/blocks/hero.js.map +1 -1
  35. package/dist/src/render/blocks/logo-strip.d.ts +6 -0
  36. package/dist/src/render/blocks/logo-strip.d.ts.map +1 -1
  37. package/dist/src/render/blocks/logo-strip.js +7 -9
  38. package/dist/src/render/blocks/logo-strip.js.map +1 -1
  39. package/dist/src/render/blocks/logos.d.ts +8 -0
  40. package/dist/src/render/blocks/logos.d.ts.map +1 -1
  41. package/dist/src/render/blocks/logos.js +18 -18
  42. package/dist/src/render/blocks/logos.js.map +1 -1
  43. package/dist/src/render/blocks/media-figure.d.ts +14 -3
  44. package/dist/src/render/blocks/media-figure.d.ts.map +1 -1
  45. package/dist/src/render/blocks/media-figure.js +34 -16
  46. package/dist/src/render/blocks/media-figure.js.map +1 -1
  47. package/dist/src/render/blocks/pricing-table.d.ts.map +1 -1
  48. package/dist/src/render/blocks/pricing-table.js +17 -22
  49. package/dist/src/render/blocks/pricing-table.js.map +1 -1
  50. package/dist/src/render/blocks/prose.d.ts +11 -6
  51. package/dist/src/render/blocks/prose.d.ts.map +1 -1
  52. package/dist/src/render/blocks/prose.js +13 -7
  53. package/dist/src/render/blocks/prose.js.map +1 -1
  54. package/dist/src/render/blocks/quote.d.ts +6 -2
  55. package/dist/src/render/blocks/quote.d.ts.map +1 -1
  56. package/dist/src/render/blocks/quote.js +14 -12
  57. package/dist/src/render/blocks/quote.js.map +1 -1
  58. package/dist/src/render/blocks/stat-counter.d.ts +6 -0
  59. package/dist/src/render/blocks/stat-counter.d.ts.map +1 -1
  60. package/dist/src/render/blocks/stat-counter.js +7 -10
  61. package/dist/src/render/blocks/stat-counter.js.map +1 -1
  62. package/dist/src/render/blocks/stats.d.ts +7 -0
  63. package/dist/src/render/blocks/stats.d.ts.map +1 -1
  64. package/dist/src/render/blocks/stats.js +8 -16
  65. package/dist/src/render/blocks/stats.js.map +1 -1
  66. package/dist/src/render/blocks/testimonial.d.ts +5 -11
  67. package/dist/src/render/blocks/testimonial.d.ts.map +1 -1
  68. package/dist/src/render/blocks/testimonial.js +9 -17
  69. package/dist/src/render/blocks/testimonial.js.map +1 -1
  70. package/dist/src/render/chrome.d.ts.map +1 -1
  71. package/dist/src/render/chrome.js +101 -84
  72. package/dist/src/render/chrome.js.map +1 -1
  73. package/dist/src/render/dish.d.ts +13 -0
  74. package/dist/src/render/dish.d.ts.map +1 -0
  75. package/dist/src/render/dish.js +84 -0
  76. package/dist/src/render/dish.js.map +1 -0
  77. package/dist/src/render/layout.d.ts +23 -0
  78. package/dist/src/render/layout.d.ts.map +1 -0
  79. package/dist/src/render/layout.js +33 -0
  80. package/dist/src/render/layout.js.map +1 -0
  81. package/dist/src/render/menu.d.ts +53 -0
  82. package/dist/src/render/menu.d.ts.map +1 -0
  83. package/dist/src/render/menu.js +99 -0
  84. package/dist/src/render/menu.js.map +1 -0
  85. package/dist/src/render/render-block.d.ts +19 -0
  86. package/dist/src/render/render-block.d.ts.map +1 -1
  87. package/dist/src/render/render-block.js +50 -39
  88. package/dist/src/render/render-block.js.map +1 -1
  89. package/dist/src/render/strings.d.ts +15 -0
  90. package/dist/src/render/strings.d.ts.map +1 -0
  91. package/dist/src/render/strings.js +23 -0
  92. package/dist/src/render/strings.js.map +1 -0
  93. package/dist/src/render/term-archive.d.ts +10 -4
  94. package/dist/src/render/term-archive.d.ts.map +1 -1
  95. package/dist/src/render/term-archive.js +25 -16
  96. package/dist/src/render/term-archive.js.map +1 -1
  97. package/dist/src/render/widget-areas.d.ts +13 -0
  98. package/dist/src/render/widget-areas.d.ts.map +1 -0
  99. package/dist/src/render/widget-areas.js +20 -0
  100. package/dist/src/render/widget-areas.js.map +1 -0
  101. package/dist/theme.config.d.ts +1 -1
  102. package/dist/theme.config.js +2 -2
  103. package/dist/theme.config.js.map +1 -1
  104. package/package.json +5 -5
  105. package/src/index.ts +3 -0
  106. package/src/render/blocks/accordion.ts +18 -47
  107. package/src/render/blocks/collection-list.ts +245 -133
  108. package/src/render/blocks/cta.ts +20 -11
  109. package/src/render/blocks/embed.ts +45 -36
  110. package/src/render/blocks/faq.ts +32 -25
  111. package/src/render/blocks/feature-grid.ts +83 -33
  112. package/src/render/blocks/gallery.ts +39 -21
  113. package/src/render/blocks/hero.ts +41 -34
  114. package/src/render/blocks/logo-strip.ts +27 -25
  115. package/src/render/blocks/logos.ts +34 -43
  116. package/src/render/blocks/media-figure.ts +49 -24
  117. package/src/render/blocks/pricing-table.ts +30 -42
  118. package/src/render/blocks/prose.ts +18 -9
  119. package/src/render/blocks/quote.ts +29 -19
  120. package/src/render/blocks/stat-counter.ts +24 -29
  121. package/src/render/blocks/stats.ts +30 -40
  122. package/src/render/blocks/testimonial.ts +17 -22
  123. package/src/render/chrome.ts +113 -95
  124. package/src/render/dish.ts +195 -0
  125. package/src/render/layout.ts +64 -0
  126. package/src/render/menu.ts +117 -0
  127. package/src/render/render-block.ts +51 -40
  128. package/src/render/strings.ts +44 -0
  129. package/src/render/term-archive.ts +78 -44
  130. package/src/render/widget-areas.ts +23 -0
  131. package/src/styles/archive.css +46 -56
  132. package/src/styles/base.css +803 -550
  133. package/src/styles/blocks.css +723 -641
  134. package/src/styles/menu.css +482 -0
  135. package/src/styles/theme.css +24 -25
  136. package/src/styles/tokens.css +239 -225
  137. package/src/styles/widgets.css +853 -0
  138. package/tokens.json +16 -16
@@ -5,8 +5,8 @@ import {
5
5
  h,
6
6
  type PageContent,
7
7
  pageHasOwnHeading,
8
+ providedBlockNode,
8
9
  type RenderContext,
9
- renderEntryHeader,
10
10
  resolveBlockForRender,
11
11
  withBlockKey,
12
12
  withBlockVariant,
@@ -28,6 +28,7 @@ import { renderQuote } from './blocks/quote.js'
28
28
  import { renderStatCounter } from './blocks/stat-counter.js'
29
29
  import { renderStats } from './blocks/stats.js'
30
30
  import { renderTestimonial } from './blocks/testimonial.js'
31
+ import { isDishPage, renderDishHeader, renderPageHeader } from './dish.js'
31
32
 
32
33
  export type { FetchedEntries, PageContent }
33
34
 
@@ -37,13 +38,19 @@ export function renderBlock(
37
38
  entries: FetchedEntries = {},
38
39
  registry?: BlockRegistry,
39
40
  ): HtmlElement | null {
40
- // `block`'s type says `VocabularyBlock`, but the value crossing this
41
- // boundary from stored content is not always literally one of the shared
42
- // vocabulary — resolving here is what turns an unimplemented theme-private
43
- // block into its declared fallback instead of a silently blank slot.
41
+ // A block a plugin provides arrives already rendered by the host, in a
42
+ // process of its own (contract D theme@1.7). Nothing else in this theme
43
+ // needs to know such blocks exist.
44
+ const provided = providedBlockNode(block, ctx)
45
+ if (provided !== null) return withBlockVariant(provided, block.variant)
46
+ // A stored block is not always literally one of the shared vocabulary:
47
+ // resolving here turns an unimplemented theme-private block into its
48
+ // declared fallback instead of a silently blank slot.
44
49
  const resolved = resolveBlockForRender(block, VOCABULARY_NAMES, registry)
45
50
  if (resolved === null) return null
46
51
  const known = resolved as unknown as VocabularyBlock
52
+ // `variant` is envelope data every block carries identically, applied once
53
+ // here rather than by each of the seventeen renderers.
47
54
  return withBlockVariant(renderKnownBlock(known, ctx, entries), known.variant)
48
55
  }
49
56
 
@@ -88,9 +95,9 @@ function renderKnownBlock(
88
95
  case 'logoStrip':
89
96
  return renderLogoStrip(known, ctx)
90
97
  default: {
91
- // Exhaustive over contract B's vocabulary: `known` is `never` here, so
92
- // a block this package does not implement stops it compiling until it
93
- // is.
98
+ // Exhaustive over contract B: `known` is `never` here, so a block this
99
+ // package does not implement stops it compiling until it is. `null`
100
+ // rather than a throw: choosing a fallback is the render layer's job.
94
101
  const unreachable: never = known
95
102
  void unreachable
96
103
  return null
@@ -99,53 +106,57 @@ function renderKnownBlock(
99
106
  }
100
107
 
101
108
  /**
102
- * `<main id="cg-main">` is mandatory: it is the skip-link's target, written
103
- * once by `@cogenta/cli`'s `theme-render.ts` outside any theme's control.
104
- *
105
- * `withBlockKey` stamps every rendered block with its contract-B `_key` —
106
- * required so the visual page builder (L16) can map a clicked element in
107
- * the rendered iframe back to the block that produced it.
108
- *
109
- * A routed collection with no `blocks` field of its own — this theme's
110
- * `menu_item`, in particular — reaches here with an empty block list, so
111
- * `page.entry` (contract D `theme@1.4`) is what carries its furniture:
112
- * `renderEntryHeader` draws the title, excerpt (the dish's own
113
- * `description`, via `entryExcerpt`'s field-name convention) and cover
114
- * photo. `PageEntryMeta` has no room for a schema-specific field such as
115
- * `price`/`category`, so a menu item's own page cannot show them without a
116
- * contract D change — the theme's real, honest limit; the price and
117
- * category chip the brief also asks for are exactly what the home page's
118
- * `collectionList` menu already renders for every dish, and the item page
119
- * still links back there.
120
- */
121
- /**
122
- * A second, theme-local stamp beside `withBlockKey`'s own `data-block-key`:
123
- * a real `id` attribute, so this theme's single-page navigation (the
124
- * blueprint's header/footer links into `#home-menu`/`#home-story`/etc.) has
125
- * something to scroll to. Contract B's `_key` is already guaranteed unique
126
- * within one page's block list (the reorder/diff identity every block
127
- * carries), so reusing it as the DOM id can never collide on a page this
128
- * theme rendered — safe to do generically, for every block, not only the
129
- * ones a blueprint happens to link to.
109
+ * A real `id` beside `withBlockKey`'s `data-block-key`, so a menu link to
110
+ * `/#<key>` still has somewhere to land: sites built on this theme before its
111
+ * multi-page blueprint linked their header straight into home page blocks.
112
+ * Contract B's `_key` is unique within one page's block list, so it can never
113
+ * collide on a page this theme rendered.
130
114
  */
131
115
  function withAnchorId(element: HtmlElement | null, key: string): HtmlElement | null {
132
116
  if (element === null) return null
133
117
  return { ...element, attrs: { ...element.attrs, id: key } }
134
118
  }
135
119
 
120
+ /**
121
+ * `<main id="cg-main">` is mandatory: it is the skip link's target, written
122
+ * by the host outside any theme's control.
123
+ *
124
+ * Three openings, and exactly one `h1` in each:
125
+ *
126
+ * - a page whose blocks include a `hero` lets the hero carry the title;
127
+ * - a dish (`isDishPage`: its `theme@1.5` fields carry a price) gets the dish
128
+ * sheet: photograph, section, name, description, price, details;
129
+ * - anything else gets its title set large on the grid.
130
+ *
131
+ * A host older than `theme@1.5` sends no fields, so its dish pages open like
132
+ * any other page: title and description, never a price read from somewhere
133
+ * it was not given. A host older than `theme@1.4` sends no entry at all, and
134
+ * the page opens on its bare title.
135
+ *
136
+ * `withBlockKey` stamps every rendered block with its contract-B `_key`, so
137
+ * the visual page builder (L16) can map a clicked element back to its block.
138
+ */
136
139
  export function renderPage(
137
140
  page: PageContent,
138
141
  ctx: RenderContext,
139
142
  entries: FetchedEntries = {},
140
143
  registry?: BlockRegistry,
141
144
  ): HtmlElement {
142
- const heading = pageHasOwnHeading(page.blocks)
145
+ const ownHeading = pageHasOwnHeading(page.blocks)
146
+ const dish = !ownHeading && isDishPage(page.entry) ? page.entry : undefined
147
+ const opening = ownHeading
143
148
  ? null
144
- : (renderEntryHeader(page, ctx) ?? h('h1', { class: 'cg-page__title' }, page.title))
149
+ : dish !== undefined
150
+ ? renderDishHeader(page, dish, ctx)
151
+ : renderPageHeader(page, ctx)
152
+
145
153
  return h(
146
154
  'main',
147
- { class: 'cg-main', id: 'cg-main' },
148
- heading,
155
+ {
156
+ class: dish === undefined ? 'cg-main cr-main' : 'cg-main cr-main cr-main--dish',
157
+ id: 'cg-main',
158
+ },
159
+ opening,
149
160
  page.blocks.map((block) =>
150
161
  withAnchorId(
151
162
  withBlockKey(renderBlock(block, ctx, entries, registry), block._key),
@@ -0,0 +1,44 @@
1
+ import { interpolate, themeLocaleFor } from '@cogenta/theme-kit'
2
+
3
+ /**
4
+ * The few words a restaurant's own pages need that the shared vocabulary
5
+ * does not: the labels of a dish's details, and the word a menu prints beside
6
+ * a vegetarian dish.
7
+ *
8
+ * `@cogenta/theme-kit`'s `THEME_STRINGS` carries the dozen strings every
9
+ * theme's blocks share; these belong to one register (a menu and a dish
10
+ * page), so they live with the theme that draws them, in the two languages
11
+ * the shared table knows. A locale the table does not know reads English, the
12
+ * same rule `createThemeTranslator` follows.
13
+ */
14
+
15
+ type MenuStringKey = 'price' | 'vegetarian' | 'details' | 'sourcing' | 'allergens' | 'pairing'
16
+
17
+ const STRINGS: Readonly<Record<'en' | 'fr', Readonly<Record<MenuStringKey, string>>>> = {
18
+ en: {
19
+ price: 'Price',
20
+ vegetarian: 'Vegetarian',
21
+ details: 'About the dish',
22
+ sourcing: 'From',
23
+ allergens: 'Allergens',
24
+ pairing: 'To drink',
25
+ },
26
+ fr: {
27
+ price: 'Prix',
28
+ vegetarian: 'Végétarien',
29
+ details: 'À propos du plat',
30
+ sourcing: 'Provenance',
31
+ allergens: 'Allergènes',
32
+ pairing: 'À boire',
33
+ },
34
+ }
35
+
36
+ export function menuString(
37
+ locale: string,
38
+ key: MenuStringKey,
39
+ values?: Readonly<Record<string, string | number>>,
40
+ ): string {
41
+ return interpolate(STRINGS[themeLocaleFor(locale)][key], values)
42
+ }
43
+
44
+ export type { MenuStringKey }
@@ -1,65 +1,86 @@
1
1
  import { type HtmlElement, h, type TermArchiveInput } from '@cogenta/theme-kit'
2
2
 
3
3
  /**
4
- * The taxonomy-term archive (contract D `theme@1.3`) — a resource index.
4
+ * The taxonomy-term archive (contract D `theme@1.3`), set in the dining
5
+ * room's own register: a breadcrumb in small words, the term as the page
6
+ * title in the display serif, its sub-terms as a ruled line of links, then
7
+ * the classified entries as ruled rows of titles and summaries, and a pager
8
+ * of two arrow links.
5
9
  *
6
- * Built from the same `cg-index__row` rows this theme's `collectionList`
7
- * dishes would sit in, minus the price column: a date rail on the left,
8
- * the title and its summary on the right.
10
+ * `TermArchiveEntry` carries no picture and no price, so an archive cannot be
11
+ * a menu or a band of plates; it is an index, drawn like one, rather than a
12
+ * menu of dishes with their prices missing. A restaurant that wants a priced
13
+ * section of its menu on a page of its own builds it from a
14
+ * `collectionList`, which carries both.
9
15
  */
10
16
  export function renderTermArchive(input: TermArchiveInput): HtmlElement {
11
17
  const rows = input.entries.map((entry) =>
12
18
  h(
13
19
  'li',
14
- { class: 'cg-index__row' },
15
- entry.publishedAt === null
16
- ? null
17
- : h(
18
- 'time',
19
- { class: 'cg-index__date', datetime: entry.publishedAt },
20
- entry.publishedAt.slice(0, 10),
21
- ),
20
+ { class: 'cr-index__item' },
22
21
  h(
23
- 'div',
24
- { class: 'cg-index__body' },
25
- h(
26
- 'h2',
27
- { class: 'cg-index__title' },
28
- entry.href === null
29
- ? entry.title
30
- : h('a', { class: 'cg-index__link', href: entry.href }, entry.title),
31
- ),
32
- entry.summary === null ? null : h('p', { class: 'cg-index__excerpt' }, entry.summary),
22
+ 'h2',
23
+ { class: 'cr-index__name' },
24
+ entry.href === null
25
+ ? entry.title
26
+ : h('a', { class: 'cr-index__link', href: entry.href }, entry.title),
33
27
  ),
28
+ entry.summary === null ? null : h('p', { class: 'cr-index__text' }, entry.summary),
34
29
  ),
35
30
  )
36
31
 
37
32
  return h(
38
33
  'main',
39
- { class: 'cg-main cg-archive', id: 'cg-main' },
40
- input.ancestors.length === 0
34
+ { class: 'cg-main cr-main cr-archive', id: 'cg-main' },
35
+ h(
36
+ 'header',
37
+ { class: 'cr-page-head' },
38
+ h(
39
+ 'div',
40
+ { class: 'cr-container cr-page-head__inner' },
41
+ input.ancestors.length === 0
42
+ ? null
43
+ : h(
44
+ 'nav',
45
+ { class: 'cr-archive__breadcrumb', 'aria-label': input.labels.breadcrumb },
46
+ h(
47
+ 'ol',
48
+ { class: 'cr-archive__trail' },
49
+ input.ancestors.map((link) => h('li', {}, h('a', { href: link.href }, link.label))),
50
+ ),
51
+ ),
52
+ h('h1', { class: 'cr-page-head__title' }, input.term.label),
53
+ ),
54
+ ),
55
+ input.children.length === 0
41
56
  ? null
42
57
  : h(
43
- 'nav',
44
- { class: 'cg-archive__breadcrumb', 'aria-label': input.labels.breadcrumb },
58
+ 'div',
59
+ { class: 'cr-section cr-archive__children' },
45
60
  h(
46
- 'ol',
47
- {},
48
- ...input.ancestors.map((l) => h('li', {}, h('a', { href: l.href }, l.label))),
61
+ 'div',
62
+ { class: 'cr-container' },
63
+ h(
64
+ 'ul',
65
+ { class: 'cr-archive__terms', 'aria-label': input.labels.subterms },
66
+ input.children.map((child) =>
67
+ h('li', { class: 'cr-archive__term' }, h('a', { href: child.href }, child.label)),
68
+ ),
69
+ ),
49
70
  ),
50
71
  ),
51
- h('h1', { class: 'cg-index__title-heading' }, input.term.label),
52
- input.children.length === 0
53
- ? null
54
- : h(
55
- 'ul',
56
- { class: 'cg-archive__children', 'aria-label': input.labels.subterms },
57
- ...input.children.map((c) => h('li', {}, h('a', { href: c.href }, c.label))),
58
- ),
59
- rows.length === 0
60
- ? h('p', { class: 'cg-index__empty' }, input.labels.empty)
61
- : h('ul', { class: 'cg-index__items' }, ...rows),
62
- pager(input),
72
+ h(
73
+ 'section',
74
+ { class: 'cr-section cr-list', 'data-shape': rows.length === 0 ? 'empty' : 'index' },
75
+ h(
76
+ 'div',
77
+ { class: 'cr-container cr-list__inner' },
78
+ rows.length === 0
79
+ ? h('p', { class: 'cr-empty' }, input.labels.empty)
80
+ : h('ul', { class: 'cr-list__items cr-index' }, rows),
81
+ pager(input),
82
+ ),
83
+ ),
63
84
  )
64
85
  }
65
86
 
@@ -67,12 +88,25 @@ function pager(input: TermArchiveInput): HtmlElement | null {
67
88
  if (input.page.previousHref === null && input.page.nextHref === null) return null
68
89
  return h(
69
90
  'nav',
70
- { class: 'cg-archive__pager', 'aria-label': input.labels.pagination },
91
+ { class: 'cr-archive__pager', 'aria-label': input.labels.pagination },
71
92
  input.page.previousHref === null
72
93
  ? null
73
- : h('a', { rel: 'prev', href: input.page.previousHref }, input.labels.previous),
94
+ : h(
95
+ 'a',
96
+ {
97
+ class: 'cr-arrow-link',
98
+ 'data-direction': 'back',
99
+ rel: 'prev',
100
+ href: input.page.previousHref,
101
+ },
102
+ input.labels.previous,
103
+ ),
74
104
  input.page.nextHref === null
75
105
  ? null
76
- : h('a', { rel: 'next', href: input.page.nextHref }, input.labels.next),
106
+ : h(
107
+ 'a',
108
+ { class: 'cr-arrow-link', rel: 'next', href: input.page.nextHref },
109
+ input.labels.next,
110
+ ),
77
111
  )
78
112
  }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * The widget areas this theme names (contract D `theme@1.6`). Declaring them
3
+ * is what tells the host this theme draws the footer columns inside its own
4
+ * charcoal footer, on the footer's own twelve columns; the page areas are
5
+ * placed by the host in the shared `cg-sidebar-layout` markup, which
6
+ * `styles/widgets.css` sets as the margin of a printed menu card.
7
+ */
8
+ export const widgetAreas: readonly {
9
+ readonly id: string
10
+ readonly label: string
11
+ readonly description?: string
12
+ }[] = [
13
+ {
14
+ id: 'sidebar',
15
+ label: 'Beside the page',
16
+ description: 'Beside a dish, a page that opens on its title, and search results.',
17
+ },
18
+ {
19
+ id: 'content-after',
20
+ label: 'Under the page',
21
+ description: 'After a dish or a page, before the footer.',
22
+ },
23
+ ]
@@ -1,84 +1,74 @@
1
1
  /*
2
- * The taxonomy-term archive (contract D `theme@1.3`).
3
- *
4
- * Only the three things an archive adds beyond what `collectionList`
5
- * already styles — a breadcrumb, a sub-term row, a prev/next pager. The
6
- * entry list reuses its own `cg-index__*` rules rather than a second set,
7
- * so an archive looks like the rest of this theme's reading lists.
2
+ * The taxonomy-term archive (contract D `theme@1.3`): the page title and the
3
+ * index rows come from `base.css` and `menu.css`; this sheet adds the
4
+ * breadcrumb, the sub-terms and the pager.
8
5
  */
9
6
 
10
- .cg-archive {
11
- max-inline-size: var(--cg-page-narrow);
12
- margin-inline: auto;
13
- padding: var(--cg-s10) var(--cg-gutter);
7
+ .cr-archive__breadcrumb {
8
+ margin-block-end: var(--cr-s2);
14
9
  }
15
10
 
16
- .cg-index__title-heading {
17
- margin: 0 0 var(--cg-s8);
18
- font-size: var(--cg-text-2xl);
19
- text-align: center;
20
- }
21
-
22
- .cg-archive__breadcrumb ol,
23
- .cg-archive__children {
11
+ .cr-archive__trail {
24
12
  display: flex;
25
13
  flex-wrap: wrap;
26
- justify-content: center;
27
- gap: var(--cg-s3);
28
- margin: 0 0 var(--cg-s4);
14
+ gap: 0 var(--cr-s2);
15
+ margin: 0;
29
16
  padding: 0;
30
17
  list-style: none;
31
- font-size: var(--cg-text-sm);
18
+ color: var(--cr-ink-muted);
19
+ font-size: var(--cr-text-caption);
32
20
  }
33
21
 
34
- .cg-archive__children {
35
- margin-block: var(--cg-s4) var(--cg-s8);
22
+ .cr-archive__trail li + li::before {
23
+ content: "/" / "";
24
+ margin-inline-end: var(--cr-s2);
25
+ color: var(--cr-ink-subtle);
36
26
  }
37
27
 
38
- .cg-index__items {
39
- margin: 0;
40
- padding: 0;
41
- list-style: none;
28
+ .cr-archive__trail a {
29
+ color: var(--cr-ink-muted);
42
30
  }
43
31
 
44
- .cg-index__row {
45
- display: flex;
46
- gap: var(--cg-s4);
47
- padding-block: var(--cg-s5);
48
- border-block-end: 1px solid var(--cg-line-soft);
32
+ .cr-archive__children {
33
+ padding-block: 0;
49
34
  }
50
35
 
51
- .cg-index__date {
52
- flex: 0 0 auto;
53
- font-size: var(--cg-text-sm);
54
- color: var(--cg-ink-muted);
36
+ .cr-archive__terms {
37
+ display: flex;
38
+ flex-wrap: wrap;
39
+ gap: var(--cr-s2) var(--cr-s8);
40
+ margin: 0;
41
+ padding-block: var(--cr-s4);
42
+ padding-inline: 0;
43
+ border-block: var(--cr-rule) solid var(--cr-line);
44
+ list-style: none;
55
45
  }
56
46
 
57
- .cg-index__title {
58
- margin: 0 0 var(--cg-s1);
59
- font-size: var(--cg-text-lg);
47
+ .cr-archive__term a {
48
+ font-family: var(--cr-font-display);
49
+ font-size: var(--cr-text-xl);
50
+ text-decoration-color: transparent;
60
51
  }
61
52
 
62
- .cg-index__link {
63
- color: var(--cg-ink);
64
- text-decoration: none;
53
+ .cr-archive__term a:hover {
54
+ text-decoration-color: currentColor;
65
55
  }
66
56
 
67
- .cg-index__excerpt {
68
- margin: 0;
69
- color: var(--cg-ink-muted);
57
+ .cr-archive__pager {
58
+ display: flex;
59
+ justify-content: space-between;
60
+ gap: var(--cr-s8);
61
+ margin-block-start: var(--cr-s10);
70
62
  }
71
63
 
72
- .cg-index__empty {
73
- text-align: center;
74
- color: var(--cg-ink-muted);
64
+ .cr-archive__pager a[rel="next"] {
65
+ margin-inline-start: auto;
75
66
  }
76
67
 
77
- .cg-archive__pager {
78
- display: flex;
79
- justify-content: space-between;
80
- gap: var(--cg-s4);
81
- margin-block-start: var(--cg-s10);
82
- padding-block-start: var(--cg-s5);
83
- border-block-start: 1px solid var(--cg-line-soft);
68
+ @media (min-width: 64rem) {
69
+ .cr-archive .cr-index,
70
+ .cr-archive .cr-empty,
71
+ .cr-archive__pager {
72
+ grid-column: 1 / span 9;
73
+ }
84
74
  }