@cogenta/theme-portfolio 1.1.3 → 1.3.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 (128) hide show
  1. package/dist/src/index.d.ts +9 -0
  2. package/dist/src/index.d.ts.map +1 -1
  3. package/dist/src/index.js +10 -1
  4. package/dist/src/index.js.map +1 -1
  5. package/dist/src/render/blocks/accordion.d.ts.map +1 -1
  6. package/dist/src/render/blocks/accordion.js +10 -18
  7. package/dist/src/render/blocks/accordion.js.map +1 -1
  8. package/dist/src/render/blocks/collection-list.d.ts +0 -20
  9. package/dist/src/render/blocks/collection-list.d.ts.map +1 -1
  10. package/dist/src/render/blocks/collection-list.js +59 -66
  11. package/dist/src/render/blocks/collection-list.js.map +1 -1
  12. package/dist/src/render/blocks/cta.d.ts +4 -4
  13. package/dist/src/render/blocks/cta.d.ts.map +1 -1
  14. package/dist/src/render/blocks/cta.js +15 -10
  15. package/dist/src/render/blocks/cta.js.map +1 -1
  16. package/dist/src/render/blocks/embed.d.ts.map +1 -1
  17. package/dist/src/render/blocks/embed.js +16 -31
  18. package/dist/src/render/blocks/embed.js.map +1 -1
  19. package/dist/src/render/blocks/faq.d.ts.map +1 -1
  20. package/dist/src/render/blocks/faq.js +12 -17
  21. package/dist/src/render/blocks/faq.js.map +1 -1
  22. package/dist/src/render/blocks/feature-grid.d.ts.map +1 -1
  23. package/dist/src/render/blocks/feature-grid.js +26 -23
  24. package/dist/src/render/blocks/feature-grid.js.map +1 -1
  25. package/dist/src/render/blocks/gallery.d.ts +0 -14
  26. package/dist/src/render/blocks/gallery.d.ts.map +1 -1
  27. package/dist/src/render/blocks/gallery.js +29 -21
  28. package/dist/src/render/blocks/gallery.js.map +1 -1
  29. package/dist/src/render/blocks/hero.d.ts +8 -8
  30. package/dist/src/render/blocks/hero.d.ts.map +1 -1
  31. package/dist/src/render/blocks/hero.js +17 -18
  32. package/dist/src/render/blocks/hero.js.map +1 -1
  33. package/dist/src/render/blocks/logo-strip.d.ts.map +1 -1
  34. package/dist/src/render/blocks/logo-strip.js +9 -14
  35. package/dist/src/render/blocks/logo-strip.js.map +1 -1
  36. package/dist/src/render/blocks/logos.d.ts.map +1 -1
  37. package/dist/src/render/blocks/logos.js +14 -19
  38. package/dist/src/render/blocks/logos.js.map +1 -1
  39. package/dist/src/render/blocks/media-figure.d.ts +8 -12
  40. package/dist/src/render/blocks/media-figure.d.ts.map +1 -1
  41. package/dist/src/render/blocks/media-figure.js +20 -20
  42. package/dist/src/render/blocks/media-figure.js.map +1 -1
  43. package/dist/src/render/blocks/pricing-table.d.ts.map +1 -1
  44. package/dist/src/render/blocks/pricing-table.js +17 -20
  45. package/dist/src/render/blocks/pricing-table.js.map +1 -1
  46. package/dist/src/render/blocks/prose.d.ts +10 -9
  47. package/dist/src/render/blocks/prose.d.ts.map +1 -1
  48. package/dist/src/render/blocks/prose.js +29 -10
  49. package/dist/src/render/blocks/prose.js.map +1 -1
  50. package/dist/src/render/blocks/quote.d.ts +5 -12
  51. package/dist/src/render/blocks/quote.d.ts.map +1 -1
  52. package/dist/src/render/blocks/quote.js +9 -15
  53. package/dist/src/render/blocks/quote.js.map +1 -1
  54. package/dist/src/render/blocks/stat-counter.d.ts.map +1 -1
  55. package/dist/src/render/blocks/stat-counter.js +10 -12
  56. package/dist/src/render/blocks/stat-counter.js.map +1 -1
  57. package/dist/src/render/blocks/stats.d.ts.map +1 -1
  58. package/dist/src/render/blocks/stats.js +7 -15
  59. package/dist/src/render/blocks/stats.js.map +1 -1
  60. package/dist/src/render/blocks/testimonial.d.ts +4 -14
  61. package/dist/src/render/blocks/testimonial.d.ts.map +1 -1
  62. package/dist/src/render/blocks/testimonial.js +9 -18
  63. package/dist/src/render/blocks/testimonial.js.map +1 -1
  64. package/dist/src/render/chrome.d.ts.map +1 -1
  65. package/dist/src/render/chrome.js +93 -90
  66. package/dist/src/render/chrome.js.map +1 -1
  67. package/dist/src/render/layout.d.ts +61 -0
  68. package/dist/src/render/layout.d.ts.map +1 -0
  69. package/dist/src/render/layout.js +134 -0
  70. package/dist/src/render/layout.js.map +1 -0
  71. package/dist/src/render/project.d.ts +27 -0
  72. package/dist/src/render/project.d.ts.map +1 -0
  73. package/dist/src/render/project.js +82 -0
  74. package/dist/src/render/project.js.map +1 -0
  75. package/dist/src/render/render-block.d.ts +17 -7
  76. package/dist/src/render/render-block.d.ts.map +1 -1
  77. package/dist/src/render/render-block.js +47 -34
  78. package/dist/src/render/render-block.js.map +1 -1
  79. package/dist/src/render/term-archive.d.ts +0 -11
  80. package/dist/src/render/term-archive.d.ts.map +1 -1
  81. package/dist/src/render/term-archive.js +36 -24
  82. package/dist/src/render/term-archive.js.map +1 -1
  83. package/dist/src/render/widget-areas.d.ts +14 -0
  84. package/dist/src/render/widget-areas.d.ts.map +1 -0
  85. package/dist/src/render/widget-areas.js +21 -0
  86. package/dist/src/render/widget-areas.js.map +1 -0
  87. package/dist/src/render/work.d.ts +37 -0
  88. package/dist/src/render/work.d.ts.map +1 -0
  89. package/dist/src/render/work.js +49 -0
  90. package/dist/src/render/work.js.map +1 -0
  91. package/dist/theme.config.d.ts +4 -7
  92. package/dist/theme.config.d.ts.map +1 -1
  93. package/dist/theme.config.js +6 -9
  94. package/dist/theme.config.js.map +1 -1
  95. package/package.json +4 -4
  96. package/src/index.ts +10 -1
  97. package/src/render/blocks/accordion.ts +22 -42
  98. package/src/render/blocks/collection-list.ts +83 -137
  99. package/src/render/blocks/cta.ts +24 -12
  100. package/src/render/blocks/embed.ts +39 -47
  101. package/src/render/blocks/faq.ts +24 -33
  102. package/src/render/blocks/feature-grid.ts +38 -41
  103. package/src/render/blocks/gallery.ts +34 -28
  104. package/src/render/blocks/hero.ts +31 -28
  105. package/src/render/blocks/logo-strip.ts +18 -20
  106. package/src/render/blocks/logos.ts +22 -43
  107. package/src/render/blocks/media-figure.ts +28 -20
  108. package/src/render/blocks/pricing-table.ts +31 -32
  109. package/src/render/blocks/prose.ts +38 -13
  110. package/src/render/blocks/quote.ts +18 -18
  111. package/src/render/blocks/stat-counter.ts +19 -37
  112. package/src/render/blocks/stats.ts +18 -33
  113. package/src/render/blocks/testimonial.ts +16 -22
  114. package/src/render/chrome.ts +99 -97
  115. package/src/render/layout.ts +177 -0
  116. package/src/render/project.ts +140 -0
  117. package/src/render/render-block.ts +49 -45
  118. package/src/render/term-archive.ts +92 -70
  119. package/src/render/widget-areas.ts +25 -0
  120. package/src/render/work.ts +124 -0
  121. package/src/styles/archive.css +61 -21
  122. package/src/styles/base.css +763 -648
  123. package/src/styles/blocks.css +697 -1093
  124. package/src/styles/theme.css +22 -10
  125. package/src/styles/tokens.css +174 -252
  126. package/src/styles/widgets.css +948 -0
  127. package/src/styles/work.css +405 -0
  128. package/tokens.json +19 -19
@@ -1,16 +1,17 @@
1
1
  import type { ProseBlock } from '@cogenta/blocks';
2
2
  import { type HtmlElement, type RenderContext } from '@cogenta/theme-kit';
3
3
  /**
4
- * `prose` declares `headingLevel: 'none'`: it contributes no heading of its
5
- * own. Whatever headings appear come from the rich text document, whose
6
- * vocabulary starts at `h2`.
4
+ * Running text on the studio grid. When the text opens with a subhead, the
5
+ * subhead moves into the first three columns as the section's label and the
6
+ * text starts at the fourth, level with it: the asymmetric page of a case
7
+ * study. Without a subhead the text simply takes the reading column.
7
8
  *
8
- * The wrapper is deliberately plain — a rich text document is arbitrary
9
- * editorial content, and the only honest DOM shape for arbitrary content is
10
- * "here it is". The distinctive editorial treatment (a dropped first
11
- * capital, an oversized `h2`, a hairline-and-number rule before each
12
- * heading) lives entirely in `blocks.css`, driven off the very structure
13
- * `renderRichText` already produces.
9
+ * On a project page a long text sets in two columns on a wide screen
10
+ * (`work.css`). A short one stays in one: two columns of four lines each
11
+ * read as a mistake, not as a page. Everywhere else the text keeps one
12
+ * column at the reading measure.
14
13
  */
14
+ /** Below this many words, a text is kept in one column. */
15
+ export declare const TWO_COLUMN_MIN_WORDS = 110;
15
16
  export declare function renderProse(block: ProseBlock, ctx: RenderContext): HtmlElement;
16
17
  //# sourceMappingURL=prose.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"prose.d.ts","sourceRoot":"","sources":["../../../../src/render/blocks/prose.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,EAAE,KAAK,WAAW,EAAK,KAAK,aAAa,EAAkB,MAAM,oBAAoB,CAAA;AAE5F;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,aAAa,GAAG,WAAW,CAM9E"}
1
+ {"version":3,"file":"prose.d.ts","sourceRoot":"","sources":["../../../../src/render/blocks/prose.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAoB,MAAM,iBAAiB,CAAA;AACnE,OAAO,EAAE,KAAK,WAAW,EAAK,KAAK,aAAa,EAAkB,MAAM,oBAAoB,CAAA;AAG5F;;;;;;;;;;GAUG;AAEH,2DAA2D;AAC3D,eAAO,MAAM,oBAAoB,MAAM,CAAA;AAavC,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,aAAa,GAAG,WAAW,CAgB9E"}
@@ -1,17 +1,36 @@
1
1
  import { h, renderRichText } from '@cogenta/theme-kit';
2
+ import { section } from '../layout.js';
2
3
  /**
3
- * `prose` declares `headingLevel: 'none'`: it contributes no heading of its
4
- * own. Whatever headings appear come from the rich text document, whose
5
- * vocabulary starts at `h2`.
4
+ * Running text on the studio grid. When the text opens with a subhead, the
5
+ * subhead moves into the first three columns as the section's label and the
6
+ * text starts at the fourth, level with it: the asymmetric page of a case
7
+ * study. Without a subhead the text simply takes the reading column.
6
8
  *
7
- * The wrapper is deliberately plain — a rich text document is arbitrary
8
- * editorial content, and the only honest DOM shape for arbitrary content is
9
- * "here it is". The distinctive editorial treatment (a dropped first
10
- * capital, an oversized `h2`, a hairline-and-number rule before each
11
- * heading) lives entirely in `blocks.css`, driven off the very structure
12
- * `renderRichText` already produces.
9
+ * On a project page a long text sets in two columns on a wide screen
10
+ * (`work.css`). A short one stays in one: two columns of four lines each
11
+ * read as a mistake, not as a page. Everywhere else the text keeps one
12
+ * column at the reading measure.
13
13
  */
14
+ /** Below this many words, a text is kept in one column. */
15
+ export const TWO_COLUMN_MIN_WORDS = 110;
16
+ function wordCount(document) {
17
+ let words = 0;
18
+ for (const node of document) {
19
+ if (node._type !== 'block')
20
+ continue;
21
+ for (const span of node.children) {
22
+ words += span.text.split(/\s+/u).filter((word) => word !== '').length;
23
+ }
24
+ }
25
+ return words;
26
+ }
14
27
  export function renderProse(block, ctx) {
15
- return h('div', { class: 'cg-block cg-prose', 'data-block': 'prose' }, renderRichText(ctx, block.body));
28
+ const [first, ...rest] = block.body;
29
+ const opensOnSubhead = first !== undefined && first._type === 'block' && first.style === 'h2';
30
+ const body = opensOnSubhead ? rest : block.body;
31
+ return section('div', 'prose', 'cg-prose', {
32
+ 'data-label': opensOnSubhead ? 'true' : 'false',
33
+ 'data-length': wordCount(body) >= TWO_COLUMN_MIN_WORDS ? 'long' : 'short',
34
+ }, 'div', opensOnSubhead ? h('div', { class: 'cg-prose__label' }, renderRichText(ctx, [first])) : null, h('div', { class: 'cg-prose__body' }, renderRichText(ctx, body)));
16
35
  }
17
36
  //# sourceMappingURL=prose.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"prose.js","sourceRoot":"","sources":["../../../../src/render/blocks/prose.ts"],"names":[],"mappings":"AACA,OAAO,EAAoB,CAAC,EAAsB,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAE5F;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,WAAW,CAAC,KAAiB,EAAE,GAAkB;IAC/D,OAAO,CAAC,CACN,KAAK,EACL,EAAE,KAAK,EAAE,mBAAmB,EAAE,YAAY,EAAE,OAAO,EAAE,EACrD,cAAc,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAChC,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"prose.js","sourceRoot":"","sources":["../../../../src/render/blocks/prose.ts"],"names":[],"mappings":"AACA,OAAO,EAAoB,CAAC,EAAsB,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAC5F,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAEtC;;;;;;;;;;GAUG;AAEH,2DAA2D;AAC3D,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAG,CAAA;AAEvC,SAAS,SAAS,CAAC,QAA0B;IAC3C,IAAI,KAAK,GAAG,CAAC,CAAA;IACb,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;QAC5B,IAAI,IAAI,CAAC,KAAK,KAAK,OAAO;YAAE,SAAQ;QACpC,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACjC,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC,MAAM,CAAA;QACvE,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAiB,EAAE,GAAkB;IAC/D,MAAM,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,GAAG,KAAK,CAAC,IAAI,CAAA;IACnC,MAAM,cAAc,GAAG,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,KAAK,KAAK,OAAO,IAAI,KAAK,CAAC,KAAK,KAAK,IAAI,CAAA;IAC7F,MAAM,IAAI,GAAG,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAA;IAC/C,OAAO,OAAO,CACZ,KAAK,EACL,OAAO,EACP,UAAU,EACV;QACE,YAAY,EAAE,cAAc,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO;QAC/C,aAAa,EAAE,SAAS,CAAC,IAAI,CAAC,IAAI,oBAAoB,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO;KAC1E,EACD,KAAK,EACL,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,iBAAiB,EAAE,EAAE,cAAc,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAC5F,CAAC,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE,EAAE,cAAc,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CACjE,CAAA;AACH,CAAC"}
@@ -1,18 +1,11 @@
1
1
  import type { QuoteBlock } from '@cogenta/blocks';
2
2
  import { type HtmlElement, type RenderContext } from '@cogenta/theme-kit';
3
3
  /**
4
- * `<figure><blockquote>…</blockquote><figcaption>` is the attribution
5
- * pattern the HTML spec prescribes: putting the author inside the
6
- * `<blockquote>` would claim the author's name is part of what was said.
7
- *
8
- * Set left-aligned at display scale rather than centred — a pull quote that
9
- * fills the column edge to edge, the way a magazine breaks one out of a
10
- * feature. The attribution is set in the mono register, the theme's
11
- * register for credits and metadata.
12
- *
13
- * The avatar is decorative here — the name is right beside it in text — so
14
- * its media entity's alt text is expected to be empty. `image` still writes
15
- * the attribute either way.
4
+ * A client's words, set large in the text width from the fourth column, with
5
+ * typographic quotation marks from the stylesheet (`quotes`), and the speaker
6
+ * under them in the caption size: their name, then their role. The portrait,
7
+ * when there is one, is a small square: it is decorative (the name is beside
8
+ * it in text), so it keeps the media library's own empty `alt`.
16
9
  */
17
10
  export declare function renderQuote(block: QuoteBlock, ctx: RenderContext): HtmlElement;
18
11
  //# sourceMappingURL=quote.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"quote.d.ts","sourceRoot":"","sources":["../../../../src/render/blocks/quote.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,EAAE,KAAK,WAAW,EAAY,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAEnF;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,aAAa,GAAG,WAAW,CA8B9E"}
1
+ {"version":3,"file":"quote.d.ts","sourceRoot":"","sources":["../../../../src/render/blocks/quote.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,EAAE,KAAK,WAAW,EAAY,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAGnF;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,aAAa,GAAG,WAAW,CAoC9E"}
@@ -1,27 +1,21 @@
1
1
  import { h, image } from '@cogenta/theme-kit';
2
+ import { section } from '../layout.js';
2
3
  /**
3
- * `<figure><blockquote>…</blockquote><figcaption>` is the attribution
4
- * pattern the HTML spec prescribes: putting the author inside the
5
- * `<blockquote>` would claim the author's name is part of what was said.
6
- *
7
- * Set left-aligned at display scale rather than centred — a pull quote that
8
- * fills the column edge to edge, the way a magazine breaks one out of a
9
- * feature. The attribution is set in the mono register, the theme's
10
- * register for credits and metadata.
11
- *
12
- * The avatar is decorative here — the name is right beside it in text — so
13
- * its media entity's alt text is expected to be empty. `image` still writes
14
- * the attribute either way.
4
+ * A client's words, set large in the text width from the fourth column, with
5
+ * typographic quotation marks from the stylesheet (`quotes`), and the speaker
6
+ * under them in the caption size: their name, then their role. The portrait,
7
+ * when there is one, is a small square: it is decorative (the name is beside
8
+ * it in text), so it keeps the media library's own empty `alt`.
15
9
  */
16
10
  export function renderQuote(block, ctx) {
17
- const hasAttribution = block.author !== undefined || block.role !== undefined || block.avatar !== undefined;
18
- return h('figure', { class: 'cg-block cg-quote', 'data-block': 'quote' }, h('blockquote', { class: 'cg-quote__text' }, h('p', { 'data-field': 'text' }, block.text)), hasAttribution
11
+ const hasAttribution = block.author !== undefined || block.role !== undefined;
12
+ return section('div', 'quote', 'cg-quote', {}, 'figure', h('blockquote', { class: 'cg-quote__quote' }, h('p', { class: 'cg-quote__text', 'data-field': 'text' }, block.text)), hasAttribution
19
13
  ? h('figcaption', { class: 'cg-quote__attribution' }, block.avatar === undefined
20
14
  ? null
21
15
  : image(ctx, block.avatar, {
22
16
  className: 'cg-quote__avatar',
23
17
  variant: { width: 96, height: 96, fit: 'cover' },
24
- }), h('span', { class: 'cg-quote__names' }, block.author === undefined
18
+ }), h('span', { class: 'cg-quote__who' }, block.author === undefined
25
19
  ? null
26
20
  : h('span', { class: 'cg-quote__author', 'data-field': 'author' }, block.author), block.role === undefined
27
21
  ? null
@@ -1 +1 @@
1
- {"version":3,"file":"quote.js","sourceRoot":"","sources":["../../../../src/render/blocks/quote.ts"],"names":[],"mappings":"AACA,OAAO,EAAoB,CAAC,EAAE,KAAK,EAAsB,MAAM,oBAAoB,CAAA;AAEnF;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,WAAW,CAAC,KAAiB,EAAE,GAAkB;IAC/D,MAAM,cAAc,GAClB,KAAK,CAAC,MAAM,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,CAAA;IACtF,OAAO,CAAC,CACN,QAAQ,EACR,EAAE,KAAK,EAAE,mBAAmB,EAAE,YAAY,EAAE,OAAO,EAAE,EACrD,CAAC,CAAC,YAAY,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,EAC1F,cAAc;QACZ,CAAC,CAAC,CAAC,CACC,YAAY,EACZ,EAAE,KAAK,EAAE,uBAAuB,EAAE,EAClC,KAAK,CAAC,MAAM,KAAK,SAAS;YACxB,CAAC,CAAC,IAAI;YACN,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,MAAM,EAAE;gBACvB,SAAS,EAAE,kBAAkB;gBAC7B,OAAO,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE;aACjD,CAAC,EACN,CAAC,CACC,MAAM,EACN,EAAE,KAAK,EAAE,iBAAiB,EAAE,EAC5B,KAAK,CAAC,MAAM,KAAK,SAAS;YACxB,CAAC,CAAC,IAAI;YACN,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,kBAAkB,EAAE,YAAY,EAAE,QAAQ,EAAE,EAAE,KAAK,CAAC,MAAM,CAAC,EAClF,KAAK,CAAC,IAAI,KAAK,SAAS;YACtB,CAAC,CAAC,IAAI;YACN,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,EAAE,EAAE,KAAK,CAAC,IAAI,CAAC,CAC7E,CACF;QACH,CAAC,CAAC,IAAI,CACT,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"quote.js","sourceRoot":"","sources":["../../../../src/render/blocks/quote.ts"],"names":[],"mappings":"AACA,OAAO,EAAoB,CAAC,EAAE,KAAK,EAAsB,MAAM,oBAAoB,CAAA;AACnF,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAEtC;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,KAAiB,EAAE,GAAkB;IAC/D,MAAM,cAAc,GAAG,KAAK,CAAC,MAAM,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,CAAA;IAC7E,OAAO,OAAO,CACZ,KAAK,EACL,OAAO,EACP,UAAU,EACV,EAAE,EACF,QAAQ,EACR,CAAC,CACC,YAAY,EACZ,EAAE,KAAK,EAAE,iBAAiB,EAAE,EAC5B,CAAC,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,EAAE,EAAE,KAAK,CAAC,IAAI,CAAC,CACtE,EACD,cAAc;QACZ,CAAC,CAAC,CAAC,CACC,YAAY,EACZ,EAAE,KAAK,EAAE,uBAAuB,EAAE,EAClC,KAAK,CAAC,MAAM,KAAK,SAAS;YACxB,CAAC,CAAC,IAAI;YACN,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,MAAM,EAAE;gBACvB,SAAS,EAAE,kBAAkB;gBAC7B,OAAO,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE;aACjD,CAAC,EACN,CAAC,CACC,MAAM,EACN,EAAE,KAAK,EAAE,eAAe,EAAE,EAC1B,KAAK,CAAC,MAAM,KAAK,SAAS;YACxB,CAAC,CAAC,IAAI;YACN,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,kBAAkB,EAAE,YAAY,EAAE,QAAQ,EAAE,EAAE,KAAK,CAAC,MAAM,CAAC,EAClF,KAAK,CAAC,IAAI,KAAK,SAAS;YACtB,CAAC,CAAC,IAAI;YACN,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,EAAE,EAAE,KAAK,CAAC,IAAI,CAAC,CAC7E,CACF;QACH,CAAC,CAAC,IAAI,CACT,CAAA;AACH,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"stat-counter.d.ts","sourceRoot":"","sources":["../../../../src/render/blocks/stat-counter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAmB,MAAM,iBAAiB,CAAA;AACxE,OAAO,EAEL,KAAK,WAAW,EAGhB,KAAK,aAAa,EACnB,MAAM,oBAAoB,CAAA;AAwB3B,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,gBAAgB,EAAE,IAAI,EAAE,aAAa,GAAG,WAAW,CAiB3F"}
1
+ {"version":3,"file":"stat-counter.d.ts","sourceRoot":"","sources":["../../../../src/render/blocks/stat-counter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAmB,MAAM,iBAAiB,CAAA;AACxE,OAAO,EAAE,KAAK,WAAW,EAAK,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAmB5E,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,gBAAgB,EAAE,IAAI,EAAE,aAAa,GAAG,WAAW,CAU3F"}
@@ -1,18 +1,16 @@
1
- import { blockHeadingTag, h, heading, } from '@cogenta/theme-kit';
1
+ import { h } from '@cogenta/theme-kit';
2
+ import { section, sectionHead } from '../layout.js';
2
3
  /**
3
- * Narrower than `stats` (no `unit`, RFC 0001's own reasoning), so this is not
4
- * `stats.ts`'s description list of labelled figures again with a field
5
- * dropped — it is set as a single running strip of oversized digits
6
- * separated by hairlines, the way a spec sheet's headline numbers run across
7
- * a masthead, each with its own zero-padded index the way `logos.ts` numbers
8
- * its marks. `stats.ts` stays the fuller, boxed "by the numbers" panel.
4
+ * A ruled table of figures from the fourth column, one row per figure: what
5
+ * it counts on the left in the text face, the value on the right in the
6
+ * display width with tabular numerals, so a column of numbers lines up.
7
+ * Where `stats` sets figures side by side, this sets them as a list read
8
+ * down the page. It carries no unit, the narrower shape contract B gives it.
9
9
  */
10
- function renderItem(item, index) {
11
- return h('li', { class: 'cg-counter__item' }, h('span', { class: 'cg-counter__index', 'aria-hidden': 'true' }, String(index + 1).padStart(2, '0')), h('span', { class: 'cg-counter__value' }, item.value), h('span', { class: 'cg-counter__label' }, item.label));
10
+ function renderItem(item) {
11
+ return h('div', { class: 'cg-tally__row' }, h('dt', { class: 'cg-tally__label' }, item.label), h('dd', { class: 'cg-tally__value' }, item.value));
12
12
  }
13
13
  export function renderStatCounter(block, _ctx) {
14
- return h('section', { class: 'cg-block cg-counter', 'data-block': 'statCounter' }, block.title === undefined
15
- ? null
16
- : heading(blockHeadingTag('statCounter') ?? 'h2', { class: 'cg-counter__title', 'data-field': 'title' }, block.title), h('ul', { class: 'cg-counter__items' }, block.stats.map((item, index) => renderItem(item, index))));
14
+ return section('section', 'statCounter', 'cg-tally cg-split', { 'data-titled': block.title === undefined ? 'false' : 'true' }, 'div', sectionHead('statCounter', block.title), h('dl', { class: 'cg-tally__rows' }, block.stats.map(renderItem)));
17
15
  }
18
16
  //# sourceMappingURL=stat-counter.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"stat-counter.js","sourceRoot":"","sources":["../../../../src/render/blocks/stat-counter.ts"],"names":[],"mappings":"AACA,OAAO,EACL,eAAe,EAEf,CAAC,EACD,OAAO,GAER,MAAM,oBAAoB,CAAA;AAE3B;;;;;;;GAOG;AACH,SAAS,UAAU,CAAC,IAAqB,EAAE,KAAa;IACtD,OAAO,CAAC,CACN,IAAI,EACJ,EAAE,KAAK,EAAE,kBAAkB,EAAE,EAC7B,CAAC,CACC,MAAM,EACN,EAAE,KAAK,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,EAAE,EACrD,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CACnC,EACD,CAAC,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,mBAAmB,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,EACrD,CAAC,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,mBAAmB,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,CACtD,CAAA;AACH,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,KAAuB,EAAE,IAAmB;IAC5E,OAAO,CAAC,CACN,SAAS,EACT,EAAE,KAAK,EAAE,qBAAqB,EAAE,YAAY,EAAE,aAAa,EAAE,EAC7D,KAAK,CAAC,KAAK,KAAK,SAAS;QACvB,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,OAAO,CACL,eAAe,CAAC,aAAa,CAAC,IAAI,IAAI,EACtC,EAAE,KAAK,EAAE,mBAAmB,EAAE,YAAY,EAAE,OAAO,EAAE,EACrD,KAAK,CAAC,KAAK,CACZ,EACL,CAAC,CACC,IAAI,EACJ,EAAE,KAAK,EAAE,mBAAmB,EAAE,EAC9B,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAC1D,CACF,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"stat-counter.js","sourceRoot":"","sources":["../../../../src/render/blocks/stat-counter.ts"],"names":[],"mappings":"AACA,OAAO,EAAoB,CAAC,EAAsB,MAAM,oBAAoB,CAAA;AAC5E,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAEnD;;;;;;GAMG;AACH,SAAS,UAAU,CAAC,IAAqB;IACvC,OAAO,CAAC,CACN,KAAK,EACL,EAAE,KAAK,EAAE,eAAe,EAAE,EAC1B,CAAC,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,iBAAiB,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,EACjD,CAAC,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,iBAAiB,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,CAClD,CAAA;AACH,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,KAAuB,EAAE,IAAmB;IAC5E,OAAO,OAAO,CACZ,SAAS,EACT,aAAa,EACb,mBAAmB,EACnB,EAAE,aAAa,EAAE,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,EAAE,EAC/D,KAAK,EACL,WAAW,CAAC,aAAa,EAAE,KAAK,CAAC,KAAK,CAAC,EACvC,CAAC,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE,EAAE,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAClE,CAAA;AACH,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"stats.d.ts","sourceRoot":"","sources":["../../../../src/render/blocks/stats.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAY,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC3D,OAAO,EAEL,KAAK,WAAW,EAGhB,KAAK,aAAa,EACnB,MAAM,oBAAoB,CAAA;AA4B3B,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,aAAa,GAAG,WAAW,CAa/E"}
1
+ {"version":3,"file":"stats.d.ts","sourceRoot":"","sources":["../../../../src/render/blocks/stats.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAY,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC3D,OAAO,EAAE,KAAK,WAAW,EAAK,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAsB5E,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,aAAa,GAAG,WAAW,CAU/E"}
@@ -1,22 +1,14 @@
1
- import { blockHeadingTag, h, heading, } from '@cogenta/theme-kit';
1
+ import { h } from '@cogenta/theme-kit';
2
+ import { section, sectionHead } from '../layout.js';
2
3
  /**
3
- * A description list: each figure is the description of its label, which is
4
- * exactly what `<dt>`/`<dd>` mean, and it survives being read linearly.
5
- *
6
- * The label comes first in the markup and the skin puts the figure above it
7
- * — reading order and visual order stay independent, which is what WCAG
8
- * 1.3.2 asks for.
9
- *
10
- * Figures are set at the theme's biggest display scale and in tabular mono
11
- * numerals — a portfolio's "by the numbers" row reads like a spec sheet,
12
- * not a dashboard widget.
4
+ * Figures side by side, the way a studio states its record: each figure very
5
+ * large in the display width with tabular numerals, its unit after it at the
6
+ * text size, and what it counts under it, a hairline over every column.
13
7
  */
14
8
  function renderItem(item) {
15
- return h('div', { class: 'cg-stat' }, h('dt', { class: 'cg-stat__label' }, item.label), h('dd', { class: 'cg-stat__value' }, item.value, item.unit === undefined ? null : h('span', { class: 'cg-stat__unit' }, item.unit)));
9
+ return h('div', { class: 'cg-figures__item' }, h('dt', { class: 'cg-figures__figure' }, h('span', { class: 'cg-figures__value' }, item.value), item.unit === undefined ? null : h('span', { class: 'cg-figures__unit' }, item.unit)), h('dd', { class: 'cg-figures__label' }, item.label));
16
10
  }
17
11
  export function renderStats(block, _ctx) {
18
- return h('section', { class: 'cg-block cg-stats', 'data-block': 'stats' }, block.title === undefined
19
- ? null
20
- : heading(blockHeadingTag('stats') ?? 'h2', { class: 'cg-stats__title', 'data-field': 'title' }, block.title), h('dl', { class: 'cg-stats__items' }, block.items.map(renderItem)));
12
+ return section('section', 'stats', 'cg-figures', { 'data-count': String(Math.min(block.items.length, 4)) }, 'div', sectionHead('stats', block.title), h('dl', { class: 'cg-figures__items' }, block.items.map(renderItem)));
21
13
  }
22
14
  //# sourceMappingURL=stats.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"stats.js","sourceRoot":"","sources":["../../../../src/render/blocks/stats.ts"],"names":[],"mappings":"AACA,OAAO,EACL,eAAe,EAEf,CAAC,EACD,OAAO,GAER,MAAM,oBAAoB,CAAA;AAE3B;;;;;;;;;;;GAWG;AACH,SAAS,UAAU,CAAC,IAAc;IAChC,OAAO,CAAC,CACN,KAAK,EACL,EAAE,KAAK,EAAE,SAAS,EAAE,EACpB,CAAC,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,EAChD,CAAC,CACC,IAAI,EACJ,EAAE,KAAK,EAAE,gBAAgB,EAAE,EAC3B,IAAI,CAAC,KAAK,EACV,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,eAAe,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,CAClF,CACF,CAAA;AACH,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAiB,EAAE,IAAmB;IAChE,OAAO,CAAC,CACN,SAAS,EACT,EAAE,KAAK,EAAE,mBAAmB,EAAE,YAAY,EAAE,OAAO,EAAE,EACrD,KAAK,CAAC,KAAK,KAAK,SAAS;QACvB,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,OAAO,CACL,eAAe,CAAC,OAAO,CAAC,IAAI,IAAI,EAChC,EAAE,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,OAAO,EAAE,EACnD,KAAK,CAAC,KAAK,CACZ,EACL,CAAC,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,iBAAiB,EAAE,EAAE,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CACnE,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"stats.js","sourceRoot":"","sources":["../../../../src/render/blocks/stats.ts"],"names":[],"mappings":"AACA,OAAO,EAAoB,CAAC,EAAsB,MAAM,oBAAoB,CAAA;AAC5E,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAEnD;;;;GAIG;AACH,SAAS,UAAU,CAAC,IAAc;IAChC,OAAO,CAAC,CACN,KAAK,EACL,EAAE,KAAK,EAAE,kBAAkB,EAAE,EAC7B,CAAC,CACC,IAAI,EACJ,EAAE,KAAK,EAAE,oBAAoB,EAAE,EAC/B,CAAC,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,mBAAmB,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,EACrD,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,kBAAkB,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,CACrF,EACD,CAAC,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,mBAAmB,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,CACpD,CAAA;AACH,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAiB,EAAE,IAAmB;IAChE,OAAO,OAAO,CACZ,SAAS,EACT,OAAO,EACP,YAAY,EACZ,EAAE,YAAY,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EACzD,KAAK,EACL,WAAW,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,EACjC,CAAC,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,mBAAmB,EAAE,EAAE,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CACrE,CAAA;AACH,CAAC"}
@@ -1,20 +1,10 @@
1
1
  import type { TestimonialBlock } from '@cogenta/blocks';
2
2
  import { type HtmlElement, type RenderContext } from '@cogenta/theme-kit';
3
3
  /**
4
- * Distinct from `quote` on purpose (RFC 0001): `quote` is this theme's full-
5
- * bleed pull quote, set left-aligned at display scale with no frame. A wall
6
- * of testimonials needs to sit several-up without each one reading as a
7
- * headline, so this is a bordered card instead — the same "cut paper"
8
- * language `feature-grid.ts` uses for its items, at a quieter type scale.
9
- *
10
- * `attribution` arrives as one grouped field (RFC 0001's own reasoning for
11
- * shaping it that way), so its `name`/`role` are not marked `data-field`:
12
- * that attribute is reserved for a plain-text field the block schema
13
- * declares at the top level, and neither of these is one — only `attribution`
14
- * itself is, and it is not plain text.
15
- *
16
- * The avatar is decorative, exactly like `quote.ts`'s own — the name sits
17
- * right beside it in text, so its media entity's alt text is expected empty.
4
+ * A longer word from a client, quieter than `quote`: the words at the lead
5
+ * size from the fourth column, a hairline above them, and the client under
6
+ * them with their role. Where `quote` is a line set large, a testimonial is a
7
+ * paragraph meant to be read.
18
8
  */
19
9
  export declare function renderTestimonial(block: TestimonialBlock, ctx: RenderContext): HtmlElement;
20
10
  //# sourceMappingURL=testimonial.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"testimonial.d.ts","sourceRoot":"","sources":["../../../../src/render/blocks/testimonial.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AACvD,OAAO,EAAE,KAAK,WAAW,EAAY,KAAK,aAAa,EAAkB,MAAM,oBAAoB,CAAA;AAEnG;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,gBAAgB,EAAE,GAAG,EAAE,aAAa,GAAG,WAAW,CAyB1F"}
1
+ {"version":3,"file":"testimonial.d.ts","sourceRoot":"","sources":["../../../../src/render/blocks/testimonial.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AACvD,OAAO,EAAE,KAAK,WAAW,EAAY,KAAK,aAAa,EAAkB,MAAM,oBAAoB,CAAA;AAGnG;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,gBAAgB,EAAE,GAAG,EAAE,aAAa,GAAG,WAAW,CA4B1F"}
@@ -1,29 +1,20 @@
1
1
  import { h, image, renderRichText } from '@cogenta/theme-kit';
2
+ import { section } from '../layout.js';
2
3
  /**
3
- * Distinct from `quote` on purpose (RFC 0001): `quote` is this theme's full-
4
- * bleed pull quote, set left-aligned at display scale with no frame. A wall
5
- * of testimonials needs to sit several-up without each one reading as a
6
- * headline, so this is a bordered card instead — the same "cut paper"
7
- * language `feature-grid.ts` uses for its items, at a quieter type scale.
8
- *
9
- * `attribution` arrives as one grouped field (RFC 0001's own reasoning for
10
- * shaping it that way), so its `name`/`role` are not marked `data-field`:
11
- * that attribute is reserved for a plain-text field the block schema
12
- * declares at the top level, and neither of these is one — only `attribution`
13
- * itself is, and it is not plain text.
14
- *
15
- * The avatar is decorative, exactly like `quote.ts`'s own — the name sits
16
- * right beside it in text, so its media entity's alt text is expected empty.
4
+ * A longer word from a client, quieter than `quote`: the words at the lead
5
+ * size from the fourth column, a hairline above them, and the client under
6
+ * them with their role. Where `quote` is a line set large, a testimonial is a
7
+ * paragraph meant to be read.
17
8
  */
18
9
  export function renderTestimonial(block, ctx) {
19
10
  const { attribution } = block;
20
- return h('figure', { class: 'cg-block cg-testimonial', 'data-block': 'testimonial' }, h('blockquote', { class: 'cg-testimonial__quote' }, renderRichText(ctx, block.quote)), h('figcaption', { class: 'cg-testimonial__attribution' }, attribution.avatar === undefined
11
+ return section('div', 'testimonial', 'cg-word', {}, 'figure', h('blockquote', { class: 'cg-word__quote' }, renderRichText(ctx, block.quote)), h('figcaption', { class: 'cg-word__attribution' }, attribution.avatar === undefined
21
12
  ? null
22
13
  : image(ctx, attribution.avatar, {
23
- className: 'cg-testimonial__avatar',
14
+ className: 'cg-word__avatar',
24
15
  variant: { width: 96, height: 96, fit: 'cover' },
25
- }), h('span', { class: 'cg-testimonial__names' }, h('span', { class: 'cg-testimonial__name' }, attribution.name), attribution.role === undefined
16
+ }), h('span', { class: 'cg-word__who' }, h('span', { class: 'cg-word__name' }, attribution.name), attribution.role === undefined
26
17
  ? null
27
- : h('span', { class: 'cg-testimonial__role' }, attribution.role))));
18
+ : h('span', { class: 'cg-word__role' }, attribution.role))));
28
19
  }
29
20
  //# sourceMappingURL=testimonial.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"testimonial.js","sourceRoot":"","sources":["../../../../src/render/blocks/testimonial.ts"],"names":[],"mappings":"AACA,OAAO,EAAoB,CAAC,EAAE,KAAK,EAAsB,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAEnG;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAuB,EAAE,GAAkB;IAC3E,MAAM,EAAE,WAAW,EAAE,GAAG,KAAK,CAAA;IAC7B,OAAO,CAAC,CACN,QAAQ,EACR,EAAE,KAAK,EAAE,yBAAyB,EAAE,YAAY,EAAE,aAAa,EAAE,EACjE,CAAC,CAAC,YAAY,EAAE,EAAE,KAAK,EAAE,uBAAuB,EAAE,EAAE,cAAc,CAAC,GAAG,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,EACrF,CAAC,CACC,YAAY,EACZ,EAAE,KAAK,EAAE,6BAA6B,EAAE,EACxC,WAAW,CAAC,MAAM,KAAK,SAAS;QAC9B,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,WAAW,CAAC,MAAM,EAAE;YAC7B,SAAS,EAAE,wBAAwB;YACnC,OAAO,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE;SACjD,CAAC,EACN,CAAC,CACC,MAAM,EACN,EAAE,KAAK,EAAE,uBAAuB,EAAE,EAClC,CAAC,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,sBAAsB,EAAE,EAAE,WAAW,CAAC,IAAI,CAAC,EAC9D,WAAW,CAAC,IAAI,KAAK,SAAS;QAC5B,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,sBAAsB,EAAE,EAAE,WAAW,CAAC,IAAI,CAAC,CACnE,CACF,CACF,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"testimonial.js","sourceRoot":"","sources":["../../../../src/render/blocks/testimonial.ts"],"names":[],"mappings":"AACA,OAAO,EAAoB,CAAC,EAAE,KAAK,EAAsB,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACnG,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAEtC;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAuB,EAAE,GAAkB;IAC3E,MAAM,EAAE,WAAW,EAAE,GAAG,KAAK,CAAA;IAC7B,OAAO,OAAO,CACZ,KAAK,EACL,aAAa,EACb,SAAS,EACT,EAAE,EACF,QAAQ,EACR,CAAC,CAAC,YAAY,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE,EAAE,cAAc,CAAC,GAAG,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,EAC9E,CAAC,CACC,YAAY,EACZ,EAAE,KAAK,EAAE,sBAAsB,EAAE,EACjC,WAAW,CAAC,MAAM,KAAK,SAAS;QAC9B,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,WAAW,CAAC,MAAM,EAAE;YAC7B,SAAS,EAAE,iBAAiB;YAC5B,OAAO,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE;SACjD,CAAC,EACN,CAAC,CACC,MAAM,EACN,EAAE,KAAK,EAAE,cAAc,EAAE,EACzB,CAAC,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,eAAe,EAAE,EAAE,WAAW,CAAC,IAAI,CAAC,EACvD,WAAW,CAAC,IAAI,KAAK,SAAS;QAC5B,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,eAAe,EAAE,EAAE,WAAW,CAAC,IAAI,CAAC,CAC5D,CACF,CACF,CAAA;AACH,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"chrome.d.ts","sourceRoot":"","sources":["../../../src/render/chrome.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,WAAW,EAEhB,KAAK,YAAY,EAOlB,MAAM,oBAAoB,CAAA;AAwE3B,wBAAgB,YAAY,CAAC,KAAK,EAAE,WAAW,GAAG,YAAY,CA2D7D"}
1
+ {"version":3,"file":"chrome.d.ts","sourceRoot":"","sources":["../../../src/render/chrome.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,WAAW,EAEhB,KAAK,YAAY,EAQlB,MAAM,oBAAoB,CAAA;AAuD3B,wBAAgB,YAAY,CAAC,KAAK,EAAE,WAAW,GAAG,YAAY,CA6E7D"}
@@ -1,114 +1,117 @@
1
- import { escapeAttribute, escapeText, renderBrandMark, renderSocialLinks, renderThemeToggle, serialize, } from '@cogenta/theme-kit';
1
+ import { escapeAttribute, escapeText, renderBrandMark, renderFooterWidgets, renderSocialLinks, renderThemeToggle, serialize, } from '@cogenta/theme-kit';
2
2
  /**
3
- * The chrome this theme ships (`theme@1.4`): a thin, sticky index bar up
4
- * top — the site name treated as a typographic mark rather than a logo
5
- * slot, a running index number, a real desktop `<nav>`, `headerAction`
6
- * (`"Let's talk"`, a flat filled button) and a CSS-only mobile menu — and a
7
- * footer that becomes a second headline: the site name set at display
8
- * scale, repeated as a closing statement, the `tagline` beneath it, then a
9
- * real row of footer navigation, social icons (`renderSocialLinks`) and the
10
- * `footerNote` beside the branding fragment.
3
+ * The header and the footer (`theme@1.4`), as quiet as a studio's own.
11
4
  *
12
- * The mobile menu is the same checkbox/label trick every L25 theme uses —
13
- * a visually-hidden `<input type="checkbox">` paired with a `<label>`
14
- * styled as a three-bar button; `base.css`'s sibling combinator is what
15
- * reveals the panel when the box is checked, so there is exactly one real
16
- * `<nav>` in the markup (the same one desktop and mobile both use, retiled
17
- * by CSS at the breakpoint), never two copies of the same links.
5
+ * Header, one row on the twelve columns: the studio's name on the left (or
6
+ * its uploaded logo), the `tagline` in the middle columns on a wide screen,
7
+ * the navigation on the right with the `headerAction` as its last link, and
8
+ * the light/dark control. No bar, no button, no sticky shadow. Below the
9
+ * breakpoint the navigation becomes a full-height panel of large links opened
10
+ * by a CSS-only toggle: a visually hidden checkbox paired with a `<label>`,
11
+ * placed before the `<nav>` so the sibling combinator in `base.css` reaches
12
+ * it. There is exactly one `<nav>` in the markup, retiled by CSS.
18
13
  *
19
- * Both fragments are built with the same string-escaping discipline
20
- * `theme-kit`'s own reference chrome uses (`escapeText`/`escapeAttribute`) —
21
- * `headerNav`/`footerNav` arrive pre-resolved and are rendered as given,
22
- * never invented or dropped, and `brandingHtml` is placed, never altered.
23
- * Every `theme@1.4` field (`tagline`/`social`/`footerNote`/`headerAction`)
24
- * is optional and additive: a host or a site that never sets one gets
25
- * exactly the `1.3` markup in that spot, byte for byte (`chrome.test.ts`'s
26
- * "without the new fields" cases).
27
- *
28
- * The manual light/dark/system toggle (`renderThemeToggle`, `@cogenta/
29
- * theme-kit`, L26) is unconditional — every render carries it, `theme@1.4`
30
- * or not — placed as its own sibling after the nav/hamburger pair so it
31
- * stays visible whether or not the mobile menu is open, styled in this
32
- * theme's own hairline-bordered, sharp-cornered register in `base.css`
33
- * rather than canonical's pill button.
14
+ * Footer, a hairline and three columns: the name, the tagline and the
15
+ * `footerNote`; the footer navigation; the social profiles with their names.
16
+ * Before them, the footer widget columns (`theme@1.6`) when a site has placed
17
+ * any: a tier of their own right under the footer's hairline, on the same
18
+ * twelve columns, so an invitation to get in touch opens the footer the way a
19
+ * studio's own does. Then the legal line: the copyright year and the studio's
20
+ * name beside Cogenta's credit (`brandingHtml`, placed once, as received). No
21
+ * column carries a heading of the theme's own: a heading is a word, and a
22
+ * theme has no translation for it (a widget's title is the site's own words).
23
+ * Every `theme@1.4` field renders only when present, so a host that predates
24
+ * them still gets the name and the navigation.
34
25
  */
35
- function renderNavLinks(links, className) {
36
- if (links.length === 0)
37
- return '';
38
- const items = links
26
+ function navItems(links, className) {
27
+ return links
39
28
  .filter((link) => link.href !== null || link.kind === 'submenu-placeholder')
40
- .map((link, index) => {
29
+ .map((link) => {
41
30
  const label = escapeText(link.label);
42
31
  const titleAttr = link.title === null ? '' : ` title="${escapeAttribute(link.title)}"`;
43
- const number = String(index + 1).padStart(2, '0');
44
- if (link.href === null) {
45
- return `<li><span class="cg-nav__index" aria-hidden="true">${number}</span><span${titleAttr}>${label}</span></li>`;
46
- }
32
+ if (link.href === null)
33
+ return `<li class="${className}"><span${titleAttr}>${label}</span></li>`;
47
34
  const href = escapeAttribute(link.href);
48
35
  const target = link.openInNewTab ? ' target="_blank" rel="noopener"' : '';
49
- return (`<li><span class="cg-nav__index" aria-hidden="true">${number}</span>` +
50
- `<a href="${href}"${target}${titleAttr}>${label}</a></li>`);
36
+ return `<li class="${className}"><a href="${href}"${target}${titleAttr}>${label}</a></li>`;
51
37
  })
52
38
  .join('');
53
- return items === '' ? '' : `<ul class="${className}">${items}</ul>`;
54
39
  }
55
- /** The header's own call-to-action link (`theme@1.4`) — a flat filled button, the theme's one accent-coloured control, at the tail of the nav list. */
56
- function renderHeaderAction(action) {
57
- if (action === undefined)
40
+ /** `footerNote` as paragraphs: a blank line starts a new one. */
41
+ function renderFooterNote(note) {
42
+ const paragraphs = note
43
+ .replace(/\r\n?/g, '\n')
44
+ .split(/\n\s*\n/)
45
+ .map((paragraph) => paragraph.replace(/\s*\n\s*/g, ' ').trim())
46
+ .filter((paragraph) => paragraph !== '');
47
+ if (paragraphs.length === 0)
58
48
  return '';
59
- return (`<a class="cg-action cg-site-header__action" data-emphasis="primary" ` +
60
- `href="${escapeAttribute(action.href)}">${escapeText(action.label)}</a>`);
61
- }
62
- /** A hamburger mark drawn from three stacked bars — no icon font, no glyph a font might not ship. */
63
- function menuGlyph() {
64
- return '<span class="cg-nav-toggle-bar"></span><span class="cg-nav-toggle-bar"></span><span class="cg-nav-toggle-bar"></span>';
49
+ return `<div class="cg-colophon__note">${paragraphs
50
+ .map((paragraph) => `<p>${escapeText(paragraph)}</p>`)
51
+ .join('')}</div>`;
65
52
  }
66
53
  export function renderChrome(input) {
67
54
  const siteName = escapeText(input.site.name);
68
55
  const home = escapeAttribute(input.homeHref);
69
- const headerNav = renderNavLinks(input.headerNav, 'cg-nav');
70
- const footerNav = renderNavLinks(input.footerNav, 'cg-nav cg-nav--footer');
71
- const headerAction = renderHeaderAction(input.headerAction);
72
- const themeToggle = serialize(renderThemeToggle(input.locale, { className: 'cg-theme-toggle' }));
73
- // The asterisk glyph is this theme's own typographic mark. It belongs to
74
- // the *wordmark* treatment, so an uploaded logo replaces both together —
75
- // an asterisk stapled to somebody else's logo is not a design decision
76
- // this theme gets to make on their behalf.
77
- const logo = renderBrandMark(input.brand, { className: 'cg-site-header__logo' });
78
- const mark = logo ?? `<span class="cg-site-header__glyph" aria-hidden="true">*</span>${siteName}`;
79
- const hasMenu = headerNav !== '' || headerAction !== '';
80
- const nav = hasMenu
81
- ? `<nav class="cg-site-header__nav" id="cg-nav" aria-label="Primary">${headerNav}${headerAction}</nav>`
82
- : '';
83
- const toggle = hasMenu
56
+ const links = navItems(input.headerNav, 'cg-masthead__item');
57
+ const action = input.headerAction === undefined
58
+ ? ''
59
+ : `<a class="cg-masthead__action" href="${escapeAttribute(input.headerAction.href)}">${escapeText(input.headerAction.label)}</a>`;
60
+ const toggle = serialize(renderThemeToggle(input.locale, { className: 'cg-theme-toggle' }));
61
+ const logo = renderBrandMark(input.brand, { className: 'cg-masthead__logo' });
62
+ const mark = logo ?? `<span class="cg-masthead__wordmark">${siteName}</span>`;
63
+ const tagline = input.tagline === undefined
64
+ ? ''
65
+ : `<p class="cg-masthead__tagline">${escapeText(input.tagline)}</p>`;
66
+ const hasMenu = links !== '' || action !== '';
67
+ const menu = hasMenu
84
68
  ? `<input type="checkbox" id="cg-nav-toggle" class="cg-nav-toggle-input" aria-label="Menu">` +
85
- `<label for="cg-nav-toggle" class="cg-nav-toggle-label" aria-hidden="true">${menuGlyph()}</label>`
69
+ `<label for="cg-nav-toggle" class="cg-nav-toggle-label" aria-hidden="true">` +
70
+ `<span class="cg-nav-toggle-bar"></span><span class="cg-nav-toggle-bar"></span>` +
71
+ `</label>` +
72
+ `<nav class="cg-masthead__nav" id="cg-nav" aria-label="Primary">` +
73
+ `${links === '' ? '' : `<ul class="cg-masthead__links">${links}</ul>`}` +
74
+ `${action}` +
75
+ `</nav>`
86
76
  : '';
87
- const header = `<header class="cg-site-header"><div class="cg-site-header__inner">` +
88
- `<a class="cg-site-header__mark" href="${home}">${mark}</a>` +
89
- `${toggle}${nav}` +
90
- `${themeToggle}` +
77
+ const header = `<header class="cg-masthead" data-nav="${hasMenu ? 'links' : 'none'}">` +
78
+ `<div class="cg-masthead__inner">` +
79
+ `<a class="cg-masthead__home" href="${home}">${mark}</a>` +
80
+ `${tagline}` +
81
+ `${menu}` +
82
+ `${toggle}` +
91
83
  `</div></header>`;
92
- const tagline = input.tagline === undefined
84
+ const footerTagline = input.tagline === undefined
93
85
  ? ''
94
- : `<p class="cg-site-footer__tagline" data-field="tagline">${escapeText(input.tagline)}</p>`;
95
- const social = input.social === undefined
96
- ? ''
97
- : serialize(renderSocialLinks(input.social, {
98
- className: 'cg-site-footer__social',
99
- itemClassName: 'cg-site-footer__social-item',
100
- }) ?? { kind: 'text', value: '' });
101
- const footerNote = input.footerNote === undefined
102
- ? ''
103
- : `<p class="cg-site-footer__note">${escapeText(input.footerNote)}</p>`;
104
- const footer = `<footer class="cg-site-footer"><div class="cg-site-footer__inner">` +
105
- `<a class="cg-site-footer__statement" href="${home}">${siteName}</a>` +
106
- `${tagline}` +
107
- `<div class="cg-site-footer__row">` +
108
- `${footerNav === '' ? '' : `<nav aria-label="Footer">${footerNav}</nav>`}` +
109
- `${social === '' ? '' : `<div class="cg-site-footer__social-col">${social}</div>`}` +
110
- `<div class="cg-site-footer__meta">${footerNote}<div class="cg-site-footer__branding">${input.brandingHtml}</div></div>` +
111
- `</div></div></footer>`;
86
+ : `<p class="cg-colophon__tagline" data-field="tagline">${escapeText(input.tagline)}</p>`;
87
+ const note = input.footerNote === undefined ? '' : renderFooterNote(input.footerNote);
88
+ const footerLinks = navItems(input.footerNav, 'cg-colophon__item');
89
+ const social = renderSocialLinks(input.social, {
90
+ className: 'cg-colophon__social',
91
+ itemClassName: 'cg-colophon__social-item',
92
+ });
93
+ const year = new Date().getFullYear();
94
+ // The footer widget columns (`theme@1.6`): a tier above the name, the pages
95
+ // and the profiles, under the footer's hairline, on the footer's own grid.
96
+ const footerWidgets = renderFooterWidgets(input.widgets, {
97
+ className: 'cg-colophon__widgets',
98
+ headingLevel: 'h2',
99
+ });
100
+ const footer = `<footer class="cg-colophon"><div class="cg-colophon__inner">` +
101
+ `${footerWidgets === null ? '' : serialize(footerWidgets)}` +
102
+ `<div class="cg-colophon__about">` +
103
+ `<a class="cg-colophon__name" href="${home}">${siteName}</a>` +
104
+ `${footerTagline}${note}` +
105
+ `</div>` +
106
+ `${footerLinks === ''
107
+ ? ''
108
+ : `<nav class="cg-colophon__nav" aria-label="Footer"><ul class="cg-colophon__links">${footerLinks}</ul></nav>`}` +
109
+ `${social === null ? '' : `<div class="cg-colophon__follow">${serialize(social)}</div>`}` +
110
+ `<div class="cg-colophon__legal">` +
111
+ `<p class="cg-colophon__copyright">© ${year} ${siteName}</p>` +
112
+ `<div class="cg-colophon__branding">${input.brandingHtml}</div>` +
113
+ `</div>` +
114
+ `</div></footer>`;
112
115
  return { header, footer };
113
116
  }
114
117
  //# sourceMappingURL=chrome.js.map