@cogenta/theme-entreprise 1.1.3 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/render/blocks/accordion.d.ts.map +1 -1
- package/dist/src/render/blocks/accordion.js +12 -15
- package/dist/src/render/blocks/accordion.js.map +1 -1
- package/dist/src/render/blocks/collection-list.d.ts +5 -7
- package/dist/src/render/blocks/collection-list.d.ts.map +1 -1
- package/dist/src/render/blocks/collection-list.js +61 -32
- package/dist/src/render/blocks/collection-list.js.map +1 -1
- package/dist/src/render/blocks/cta.d.ts +6 -6
- package/dist/src/render/blocks/cta.d.ts.map +1 -1
- package/dist/src/render/blocks/cta.js +12 -12
- package/dist/src/render/blocks/cta.js.map +1 -1
- package/dist/src/render/blocks/embed.d.ts.map +1 -1
- package/dist/src/render/blocks/embed.js +22 -27
- package/dist/src/render/blocks/embed.js.map +1 -1
- package/dist/src/render/blocks/faq.d.ts.map +1 -1
- package/dist/src/render/blocks/faq.js +13 -16
- package/dist/src/render/blocks/faq.js.map +1 -1
- package/dist/src/render/blocks/feature-grid.d.ts.map +1 -1
- package/dist/src/render/blocks/feature-grid.js +22 -22
- package/dist/src/render/blocks/feature-grid.js.map +1 -1
- package/dist/src/render/blocks/gallery.d.ts +7 -8
- package/dist/src/render/blocks/gallery.d.ts.map +1 -1
- package/dist/src/render/blocks/gallery.js +16 -14
- package/dist/src/render/blocks/gallery.js.map +1 -1
- package/dist/src/render/blocks/hero.d.ts +13 -8
- package/dist/src/render/blocks/hero.d.ts.map +1 -1
- package/dist/src/render/blocks/hero.js +27 -17
- package/dist/src/render/blocks/hero.js.map +1 -1
- package/dist/src/render/blocks/logo-strip.d.ts.map +1 -1
- package/dist/src/render/blocks/logo-strip.js +14 -11
- package/dist/src/render/blocks/logo-strip.js.map +1 -1
- package/dist/src/render/blocks/logos.d.ts.map +1 -1
- package/dist/src/render/blocks/logos.js +21 -12
- package/dist/src/render/blocks/logos.js.map +1 -1
- package/dist/src/render/blocks/media-figure.d.ts +10 -6
- package/dist/src/render/blocks/media-figure.d.ts.map +1 -1
- package/dist/src/render/blocks/media-figure.js +18 -13
- package/dist/src/render/blocks/media-figure.js.map +1 -1
- package/dist/src/render/blocks/pricing-table.d.ts.map +1 -1
- package/dist/src/render/blocks/pricing-table.js +15 -11
- package/dist/src/render/blocks/pricing-table.js.map +1 -1
- package/dist/src/render/blocks/prose.d.ts +9 -3
- package/dist/src/render/blocks/prose.d.ts.map +1 -1
- package/dist/src/render/blocks/prose.js +11 -4
- package/dist/src/render/blocks/prose.js.map +1 -1
- package/dist/src/render/blocks/quote.d.ts +10 -10
- package/dist/src/render/blocks/quote.d.ts.map +1 -1
- package/dist/src/render/blocks/quote.js +14 -13
- package/dist/src/render/blocks/quote.js.map +1 -1
- package/dist/src/render/blocks/stat-counter.d.ts.map +1 -1
- package/dist/src/render/blocks/stat-counter.js +11 -13
- package/dist/src/render/blocks/stat-counter.js.map +1 -1
- package/dist/src/render/blocks/stats.d.ts.map +1 -1
- package/dist/src/render/blocks/stats.js +12 -13
- package/dist/src/render/blocks/stats.js.map +1 -1
- package/dist/src/render/blocks/testimonial.d.ts +9 -9
- package/dist/src/render/blocks/testimonial.d.ts.map +1 -1
- package/dist/src/render/blocks/testimonial.js +15 -14
- package/dist/src/render/blocks/testimonial.js.map +1 -1
- package/dist/src/render/chrome.d.ts +3 -1
- package/dist/src/render/chrome.d.ts.map +1 -1
- package/dist/src/render/chrome.js +71 -46
- package/dist/src/render/chrome.js.map +1 -1
- package/dist/src/render/layout.d.ts +26 -0
- package/dist/src/render/layout.d.ts.map +1 -0
- package/dist/src/render/layout.js +51 -0
- package/dist/src/render/layout.js.map +1 -0
- package/dist/src/render/render-block.d.ts.map +1 -1
- package/dist/src/render/render-block.js +23 -2
- package/dist/src/render/render-block.js.map +1 -1
- package/dist/src/render/term-archive.d.ts +8 -6
- package/dist/src/render/term-archive.d.ts.map +1 -1
- package/dist/src/render/term-archive.js +22 -16
- package/dist/src/render/term-archive.js.map +1 -1
- package/dist/src/render/widget-areas.d.ts +13 -0
- package/dist/src/render/widget-areas.d.ts.map +1 -0
- package/dist/src/render/widget-areas.js +20 -0
- package/dist/src/render/widget-areas.js.map +1 -0
- package/dist/theme.config.js +2 -2
- package/dist/theme.config.js.map +1 -1
- package/package.json +5 -5
- package/src/index.ts +1 -0
- package/src/render/blocks/accordion.ts +32 -27
- package/src/render/blocks/collection-list.ts +103 -53
- package/src/render/blocks/cta.ts +27 -28
- package/src/render/blocks/embed.ts +46 -43
- package/src/render/blocks/faq.ts +25 -21
- package/src/render/blocks/feature-grid.ts +42 -35
- package/src/render/blocks/gallery.ts +21 -16
- package/src/render/blocks/hero.ts +40 -28
- package/src/render/blocks/logo-strip.ts +26 -16
- package/src/render/blocks/logos.ts +33 -17
- package/src/render/blocks/media-figure.ts +22 -14
- package/src/render/blocks/pricing-table.ts +26 -15
- package/src/render/blocks/prose.ts +18 -4
- package/src/render/blocks/quote.ts +18 -14
- package/src/render/blocks/stat-counter.ts +25 -20
- package/src/render/blocks/stats.ts +31 -21
- package/src/render/blocks/testimonial.ts +22 -18
- package/src/render/chrome.ts +84 -49
- package/src/render/layout.ts +67 -0
- package/src/render/render-block.ts +24 -1
- package/src/render/term-archive.ts +54 -50
- package/src/render/widget-areas.ts +23 -0
- package/src/styles/archive.css +137 -38
- package/src/styles/base.css +553 -426
- package/src/styles/blocks.css +1343 -1010
- package/src/styles/theme.css +18 -24
- package/src/styles/tokens.css +166 -192
- package/src/styles/widgets.css +914 -0
- package/tokens.json +17 -17
|
@@ -7,25 +7,24 @@ import {
|
|
|
7
7
|
image,
|
|
8
8
|
type RenderContext,
|
|
9
9
|
} from '@cogenta/theme-kit'
|
|
10
|
+
import { section } from '../layout.js'
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
|
-
* A
|
|
13
|
-
*
|
|
14
|
-
* text when the media entity has none, and no visually hidden duplicate is
|
|
15
|
-
* added: a link whose only content is an image with `alt` text is already
|
|
16
|
-
* named.
|
|
13
|
+
* A client register: wordmarks in greyscale, each in its own cell of a ruled
|
|
14
|
+
* table, so a dozen marks of different shapes and weights still line up.
|
|
17
15
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
16
|
+
* Contract B says the organisation's name is the accessible name of the
|
|
17
|
+
* link, so the image carries it as `alt` when the media entity has none, and
|
|
18
|
+
* no visually hidden duplicate is added. Greyscale is a `filter` in the
|
|
19
|
+
* stylesheet, never baked into the media: the same asset still renders in
|
|
20
|
+
* colour wherever a site owner uses it elsewhere.
|
|
23
21
|
*/
|
|
24
22
|
function renderItem(item: LogoItem, ctx: RenderContext): HtmlElement {
|
|
25
23
|
const logo = image(ctx, item.media, {
|
|
26
24
|
className: 'cg-clients__logo',
|
|
27
25
|
altFrom: item.name,
|
|
28
26
|
variant: { fit: 'contain' },
|
|
27
|
+
sizes: '12rem',
|
|
29
28
|
})
|
|
30
29
|
return h(
|
|
31
30
|
'li',
|
|
@@ -41,20 +40,37 @@ function renderItem(item: LogoItem, ctx: RenderContext): HtmlElement {
|
|
|
41
40
|
}
|
|
42
41
|
|
|
43
42
|
export function renderLogos(block: LogosBlock, ctx: RenderContext): HtmlElement {
|
|
44
|
-
return
|
|
43
|
+
return section(
|
|
45
44
|
'section',
|
|
46
|
-
|
|
45
|
+
'logos',
|
|
46
|
+
'cg-clients',
|
|
47
|
+
{},
|
|
48
|
+
'div',
|
|
47
49
|
block.title === undefined
|
|
48
50
|
? null
|
|
49
|
-
:
|
|
50
|
-
|
|
51
|
-
{ class: 'cg-
|
|
52
|
-
|
|
51
|
+
: h(
|
|
52
|
+
'div',
|
|
53
|
+
{ class: 'cg-head' },
|
|
54
|
+
heading(
|
|
55
|
+
blockHeadingTag('logos') ?? 'h2',
|
|
56
|
+
{ class: 'cg-head__title', 'data-field': 'title' },
|
|
57
|
+
block.title,
|
|
58
|
+
),
|
|
53
59
|
),
|
|
54
60
|
h(
|
|
55
61
|
'ul',
|
|
56
|
-
{ class: 'cg-clients__items' },
|
|
62
|
+
{ class: 'cg-clients__items', 'data-columns': String(columnsFor(block.items.length)) },
|
|
57
63
|
block.items.map((item) => renderItem(item, ctx)),
|
|
58
64
|
),
|
|
59
65
|
)
|
|
60
66
|
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* How many columns the register uses on a wide screen: four when the marks
|
|
70
|
+
* fill whole rows of four, otherwise three, so a register never ends on a
|
|
71
|
+
* row of empty ruled cells when a whole-row layout exists.
|
|
72
|
+
*/
|
|
73
|
+
function columnsFor(count: number): number {
|
|
74
|
+
if (count <= 3) return Math.max(count, 1)
|
|
75
|
+
return count % 4 === 0 ? 4 : 3
|
|
76
|
+
}
|
|
@@ -1,39 +1,47 @@
|
|
|
1
1
|
import type { MediaFigureBlock } from '@cogenta/blocks'
|
|
2
2
|
import { aspectRatio, type HtmlElement, h, image, type RenderContext } from '@cogenta/theme-kit'
|
|
3
|
+
import { section } from '../layout.js'
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
|
-
*
|
|
6
|
-
* an image and its own caption — a `<figcaption>` outside the `<figure>` would
|
|
7
|
-
* lose that association for assistive technology.
|
|
6
|
+
* An image and its caption, placed on the grid by `align`.
|
|
8
7
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* `wide` gives the image nine columns and sets the caption in the three to
|
|
9
|
+
* its right, under a hairline, aligned to the image's foot, the way an
|
|
10
|
+
* annual report places an exhibit and its note. `start` and `end` do the
|
|
11
|
+
* same on a narrower image; `center` keeps the caption under the image;
|
|
12
|
+
* `full` lets the image run the full width of the page. No frame, no shadow,
|
|
13
|
+
* no rounded corner: the picture is the object.
|
|
14
|
+
*
|
|
15
|
+
* `<figure>`/`<figcaption>` keep the caption associated with the image for
|
|
16
|
+
* assistive technology, which is why the figure is the grid container itself.
|
|
12
17
|
*/
|
|
13
18
|
export function renderMediaFigure(block: MediaFigureBlock, ctx: RenderContext): HtmlElement {
|
|
14
19
|
const ratio = aspectRatio(block.ratio)
|
|
15
20
|
const hasCaption = block.caption !== undefined || block.credit !== undefined
|
|
16
|
-
return
|
|
17
|
-
'
|
|
21
|
+
return section(
|
|
22
|
+
'div',
|
|
23
|
+
'mediaFigure',
|
|
24
|
+
'cg-figure',
|
|
18
25
|
{
|
|
19
|
-
class: 'cg-figure',
|
|
20
|
-
'data-block': 'mediaFigure',
|
|
21
26
|
'data-align': block.align ?? 'center',
|
|
22
27
|
style: ratio === undefined ? undefined : `--cg-ratio:${ratio}`,
|
|
23
28
|
},
|
|
29
|
+
'figure',
|
|
24
30
|
h(
|
|
25
31
|
'div',
|
|
26
|
-
{ class: 'cg-
|
|
32
|
+
{ class: 'cg-figure__media' },
|
|
27
33
|
image(ctx, block.media, {
|
|
28
|
-
className: 'cg-
|
|
29
|
-
sizes: '(min-width:
|
|
34
|
+
className: 'cg-figure__image',
|
|
35
|
+
sizes: block.align === 'full' ? '100vw' : '(min-width: 64rem) 75vw, 100vw',
|
|
30
36
|
}),
|
|
31
37
|
),
|
|
32
38
|
hasCaption
|
|
33
39
|
? h(
|
|
34
40
|
'figcaption',
|
|
35
41
|
{ class: 'cg-figure__caption' },
|
|
36
|
-
block.caption
|
|
42
|
+
block.caption === undefined
|
|
43
|
+
? null
|
|
44
|
+
: h('span', { class: 'cg-figure__text', 'data-field': 'caption' }, block.caption),
|
|
37
45
|
block.credit === undefined
|
|
38
46
|
? null
|
|
39
47
|
: h('span', { class: 'cg-figure__credit', 'data-field': 'credit' }, block.credit),
|
|
@@ -9,17 +9,18 @@ import {
|
|
|
9
9
|
nestedHeadingTag,
|
|
10
10
|
type RenderContext,
|
|
11
11
|
} from '@cogenta/theme-kit'
|
|
12
|
+
import { section } from '../layout.js'
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
|
-
* `blocks@2.0` (RFC 0001). A
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
15
|
+
* `blocks@2.0` (RFC 0001). A comparison set as a ruled table rather than a
|
|
16
|
+
* row of cards: tiers stand side by side between vertical hairlines, each
|
|
17
|
+
* with its name in small capitals, the price in the display serif with
|
|
18
|
+
* tabular numerals, the inclusions as a ruled list and the action at the
|
|
19
|
+
* foot, aligned across tiers.
|
|
19
20
|
*
|
|
20
21
|
* `highlighted` is an editorial signal, never a colour: it becomes
|
|
21
|
-
* `data-highlighted`
|
|
22
|
-
* emphasis is announced
|
|
22
|
+
* `data-highlighted` (a heavier rule above that tier, in ink) and
|
|
23
|
+
* `aria-current`, so the emphasis is announced as well as shown.
|
|
23
24
|
*/
|
|
24
25
|
function renderTier(tier: PricingTier, ctx: RenderContext, tag: HeadingTag): HtmlElement {
|
|
25
26
|
return h(
|
|
@@ -50,7 +51,10 @@ function renderTier(tier: PricingTier, ctx: RenderContext, tag: HeadingTag): Htm
|
|
|
50
51
|
: h(
|
|
51
52
|
'div',
|
|
52
53
|
{ class: 'cg-pricing__action' },
|
|
53
|
-
actionLink(ctx, {
|
|
54
|
+
actionLink(ctx, {
|
|
55
|
+
...tier.action,
|
|
56
|
+
emphasis: tier.action.emphasis ?? (tier.highlighted === true ? 'primary' : 'secondary'),
|
|
57
|
+
}),
|
|
54
58
|
),
|
|
55
59
|
)
|
|
56
60
|
}
|
|
@@ -58,19 +62,26 @@ function renderTier(tier: PricingTier, ctx: RenderContext, tag: HeadingTag): Htm
|
|
|
58
62
|
export function renderPricingTable(block: PricingTableBlock, ctx: RenderContext): HtmlElement {
|
|
59
63
|
const hasTitle = block.title !== undefined
|
|
60
64
|
const tierTag = nestedHeadingTag('pricingTable', hasTitle)
|
|
61
|
-
return
|
|
65
|
+
return section(
|
|
62
66
|
'section',
|
|
63
|
-
|
|
67
|
+
'pricingTable',
|
|
68
|
+
'cg-pricing',
|
|
69
|
+
{},
|
|
70
|
+
'div',
|
|
64
71
|
hasTitle
|
|
65
|
-
?
|
|
66
|
-
|
|
67
|
-
{ class: 'cg-
|
|
68
|
-
|
|
72
|
+
? h(
|
|
73
|
+
'div',
|
|
74
|
+
{ class: 'cg-head' },
|
|
75
|
+
heading(
|
|
76
|
+
blockHeadingTag('pricingTable') ?? 'h2',
|
|
77
|
+
{ class: 'cg-head__title', 'data-field': 'title' },
|
|
78
|
+
block.title ?? '',
|
|
79
|
+
),
|
|
69
80
|
)
|
|
70
81
|
: null,
|
|
71
82
|
h(
|
|
72
83
|
'ul',
|
|
73
|
-
{ class: 'cg-pricing__tiers' },
|
|
84
|
+
{ class: 'cg-pricing__tiers', 'data-count': String(Math.min(block.tiers.length, 4)) },
|
|
74
85
|
block.tiers.map((tier) => renderTier(tier, ctx, tierTag)),
|
|
75
86
|
),
|
|
76
87
|
)
|
|
@@ -1,11 +1,25 @@
|
|
|
1
1
|
import type { ProseBlock } from '@cogenta/blocks'
|
|
2
2
|
import { type HtmlElement, h, type RenderContext, renderRichText } from '@cogenta/theme-kit'
|
|
3
|
+
import { section } from '../layout.js'
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* A reading column set on the right-hand two thirds of the grid.
|
|
7
|
+
*
|
|
8
|
+
* On a wide screen the text starts at the fifth column and holds a 66ch
|
|
9
|
+
* measure; a second-level heading inside the document hangs out into the
|
|
10
|
+
* four columns to its left (`blocks.css`), so a long page reads as a report,
|
|
11
|
+
* with its section titles in the margin, rather than as one centred column.
|
|
12
|
+
*
|
|
13
|
+
* `prose` declares `headingLevel: 'none'`: whatever headings appear come
|
|
14
|
+
* from the rich text document, whose vocabulary starts at `h2`.
|
|
8
15
|
*/
|
|
9
16
|
export function renderProse(block: ProseBlock, ctx: RenderContext): HtmlElement {
|
|
10
|
-
return
|
|
17
|
+
return section(
|
|
18
|
+
'div',
|
|
19
|
+
'prose',
|
|
20
|
+
'cg-text',
|
|
21
|
+
{},
|
|
22
|
+
'div',
|
|
23
|
+
h('div', { class: 'cg-prose' }, renderRichText(ctx, block.body)),
|
|
24
|
+
)
|
|
11
25
|
}
|
|
@@ -1,35 +1,39 @@
|
|
|
1
1
|
import type { QuoteBlock } from '@cogenta/blocks'
|
|
2
2
|
import { type HtmlElement, h, image, type RenderContext } from '@cogenta/theme-kit'
|
|
3
|
+
import { section } from '../layout.js'
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* thin divider — the shape a case-study or client-testimonial section takes
|
|
8
|
-
* on a serious B2B site.
|
|
6
|
+
* An editorial quotation inside the reading column: the display serif in
|
|
7
|
+
* italic, under a hairline, with the attribution set small beneath it.
|
|
9
8
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
9
|
+
* Quieter than `testimonial`, which is this theme's large pull quote: a
|
|
10
|
+
* `quote` belongs to the flow of a page, aligned with the prose column
|
|
11
|
+
* rather than spread across the grid, and never in a shaded box.
|
|
13
12
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
13
|
+
* `<figure><blockquote>…</blockquote><figcaption>` is the attribution pattern
|
|
14
|
+
* the HTML spec prescribes: putting the name inside the `<blockquote>` would
|
|
15
|
+
* claim it was part of what was said. The portrait is decorative (the name
|
|
16
|
+
* is right beside it), so its media entity's alt text is expected to be
|
|
17
|
+
* empty; `image` writes the attribute either way (WCAG 1.1.1).
|
|
17
18
|
*/
|
|
18
19
|
export function renderQuote(block: QuoteBlock, ctx: RenderContext): HtmlElement {
|
|
19
20
|
const hasAttribution =
|
|
20
21
|
block.author !== undefined || block.role !== undefined || block.avatar !== undefined
|
|
21
|
-
return
|
|
22
|
+
return section(
|
|
23
|
+
'div',
|
|
24
|
+
'quote',
|
|
25
|
+
'cg-quote',
|
|
26
|
+
{},
|
|
22
27
|
'figure',
|
|
23
|
-
{ class: 'cg-quote', 'data-block': 'quote' },
|
|
24
28
|
h('blockquote', { class: 'cg-quote__text' }, h('p', { 'data-field': 'text' }, block.text)),
|
|
25
29
|
hasAttribution
|
|
26
30
|
? h(
|
|
27
31
|
'figcaption',
|
|
28
|
-
{ class: 'cg-
|
|
32
|
+
{ class: 'cg-quote__cite' },
|
|
29
33
|
block.avatar === undefined
|
|
30
34
|
? null
|
|
31
35
|
: image(ctx, block.avatar, {
|
|
32
|
-
className: 'cg-
|
|
36
|
+
className: 'cg-quote__portrait',
|
|
33
37
|
variant: { width: 96, height: 96, fit: 'cover' },
|
|
34
38
|
}),
|
|
35
39
|
h(
|
|
@@ -6,40 +6,45 @@ import {
|
|
|
6
6
|
heading,
|
|
7
7
|
type RenderContext,
|
|
8
8
|
} from '@cogenta/theme-kit'
|
|
9
|
+
import { section } from '../layout.js'
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
|
-
* `blocks@2.0` (RFC 0001). Narrower than `stats` (no `unit`), and
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* for the narrower, single-figure shape this block offers.
|
|
12
|
+
* `blocks@2.0` (RFC 0001). Narrower than `stats` (no `unit`), and laid out as
|
|
13
|
+
* its counterpart: where `stats` is a single row between vertical rules,
|
|
14
|
+
* `statCounter` keeps its title in the left four columns and sets its
|
|
15
|
+
* figures in a two-by-two table on the right eight, each under a horizontal
|
|
16
|
+
* hairline, the way an annual report lists its headline numbers.
|
|
17
17
|
*
|
|
18
|
-
* A `<dl>`,
|
|
19
|
-
*
|
|
20
|
-
* in markup, the same reading-order guarantee `stats` gives (WCAG 1.3.2);
|
|
21
|
-
* the stylesheet is free to paint the big figure first.
|
|
18
|
+
* A `<dl>`, with the label first in the markup for the same reading-order
|
|
19
|
+
* reason as `stats`.
|
|
22
20
|
*/
|
|
23
21
|
function renderItem(item: StatCounterItem): HtmlElement {
|
|
24
22
|
return h(
|
|
25
23
|
'div',
|
|
26
|
-
{ class: 'cg-
|
|
27
|
-
h('dt', { class: 'cg-
|
|
28
|
-
h('dd', { class: 'cg-
|
|
24
|
+
{ class: 'cg-counter' },
|
|
25
|
+
h('dt', { class: 'cg-counter__label' }, item.label),
|
|
26
|
+
h('dd', { class: 'cg-counter__value' }, item.value),
|
|
29
27
|
)
|
|
30
28
|
}
|
|
31
29
|
|
|
32
30
|
export function renderStatCounter(block: StatCounterBlock, _ctx: RenderContext): HtmlElement {
|
|
33
|
-
return
|
|
31
|
+
return section(
|
|
34
32
|
'section',
|
|
35
|
-
|
|
33
|
+
'statCounter',
|
|
34
|
+
'cg-counters',
|
|
35
|
+
{ 'data-titled': block.title === undefined ? 'false' : 'true' },
|
|
36
|
+
'div',
|
|
36
37
|
block.title === undefined
|
|
37
38
|
? null
|
|
38
|
-
:
|
|
39
|
-
|
|
40
|
-
{ class: 'cg-
|
|
41
|
-
|
|
39
|
+
: h(
|
|
40
|
+
'div',
|
|
41
|
+
{ class: 'cg-head cg-head--aside' },
|
|
42
|
+
heading(
|
|
43
|
+
blockHeadingTag('statCounter') ?? 'h2',
|
|
44
|
+
{ class: 'cg-head__title', 'data-field': 'title' },
|
|
45
|
+
block.title,
|
|
46
|
+
),
|
|
42
47
|
),
|
|
43
|
-
h('dl', { class: 'cg-
|
|
48
|
+
h('dl', { class: 'cg-counters__items' }, block.stats.map(renderItem)),
|
|
44
49
|
)
|
|
45
50
|
}
|
|
@@ -6,45 +6,55 @@ import {
|
|
|
6
6
|
heading,
|
|
7
7
|
type RenderContext,
|
|
8
8
|
} from '@cogenta/theme-kit'
|
|
9
|
+
import { section } from '../layout.js'
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* (`border-inline-start` on every item but the first) rather than as
|
|
16
|
-
* separate shadowed tiles: the "real KPI strip" read the aesthetic
|
|
17
|
-
* direction asks for.
|
|
12
|
+
* Key figures as one typographic row between vertical rules: the figure in
|
|
13
|
+
* the display serif with lining, tabular numerals, its unit set smaller in
|
|
14
|
+
* the same face, and the label in small text beneath. No coloured band and
|
|
15
|
+
* no tiles.
|
|
18
16
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
17
|
+
* A description list, because each figure is the description of its label.
|
|
18
|
+
* The label comes first in the markup, so a screen reader announces what a
|
|
19
|
+
* number means before the number (WCAG 1.3.2); the stylesheet paints the
|
|
20
|
+
* figure above it.
|
|
22
21
|
*/
|
|
23
22
|
function renderItem(item: StatItem): HtmlElement {
|
|
24
23
|
return h(
|
|
25
24
|
'div',
|
|
26
|
-
{ class: 'cg-
|
|
27
|
-
h('dt', { class: 'cg-
|
|
25
|
+
{ class: 'cg-stat' },
|
|
26
|
+
h('dt', { class: 'cg-stat__label' }, item.label),
|
|
28
27
|
h(
|
|
29
28
|
'dd',
|
|
30
|
-
{ class: 'cg-
|
|
29
|
+
{ class: 'cg-stat__value' },
|
|
31
30
|
item.value,
|
|
32
|
-
item.unit === undefined ? null : h('span', { class: 'cg-
|
|
31
|
+
item.unit === undefined ? null : h('span', { class: 'cg-stat__unit' }, item.unit),
|
|
33
32
|
),
|
|
34
33
|
)
|
|
35
34
|
}
|
|
36
35
|
|
|
37
36
|
export function renderStats(block: StatsBlock, _ctx: RenderContext): HtmlElement {
|
|
38
|
-
return
|
|
37
|
+
return section(
|
|
39
38
|
'section',
|
|
40
|
-
|
|
39
|
+
'stats',
|
|
40
|
+
'cg-stats',
|
|
41
|
+
{},
|
|
42
|
+
'div',
|
|
41
43
|
block.title === undefined
|
|
42
44
|
? null
|
|
43
|
-
:
|
|
44
|
-
|
|
45
|
-
{ class: 'cg-
|
|
46
|
-
|
|
45
|
+
: h(
|
|
46
|
+
'div',
|
|
47
|
+
{ class: 'cg-head' },
|
|
48
|
+
heading(
|
|
49
|
+
blockHeadingTag('stats') ?? 'h2',
|
|
50
|
+
{ class: 'cg-head__title', 'data-field': 'title' },
|
|
51
|
+
block.title,
|
|
52
|
+
),
|
|
47
53
|
),
|
|
48
|
-
h(
|
|
54
|
+
h(
|
|
55
|
+
'dl',
|
|
56
|
+
{ class: 'cg-stats__items', 'data-count': String(Math.min(block.items.length, 5)) },
|
|
57
|
+
block.items.map(renderItem),
|
|
58
|
+
),
|
|
49
59
|
)
|
|
50
60
|
}
|
|
@@ -1,40 +1,44 @@
|
|
|
1
1
|
import type { TestimonialBlock } from '@cogenta/blocks'
|
|
2
2
|
import { type HtmlElement, h, image, type RenderContext, renderRichText } from '@cogenta/theme-kit'
|
|
3
|
+
import { section } from '../layout.js'
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
|
-
* `blocks@2.0` (RFC 0001).
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* (a testimonial may carry a link or emphasis the shorter, plain-text `quote`
|
|
9
|
-
* block cannot) and the attribution is the block's single grouped
|
|
10
|
-
* `attribution` field rather than three loose ones.
|
|
6
|
+
* `blocks@2.0` (RFC 0001). The large pull quote: one client's words set in
|
|
7
|
+
* the display serif across ten columns, with typographic quotation marks
|
|
8
|
+
* hung in the margin, and a small square portrait beside the name.
|
|
11
9
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
10
|
+
* No tinted panel, no border on one side, no italic box: the size of the
|
|
11
|
+
* type and the space around it are the emphasis. The quote is rich text, so
|
|
12
|
+
* a testimonial may carry a link or emphasis the plain-text `quote` cannot.
|
|
13
|
+
*
|
|
14
|
+
* The portrait is decorative (the name is beside it in text), so its alt
|
|
15
|
+
* text is expected to be empty; `image` writes the attribute either way.
|
|
15
16
|
*/
|
|
16
17
|
export function renderTestimonial(block: TestimonialBlock, ctx: RenderContext): HtmlElement {
|
|
17
18
|
const { attribution } = block
|
|
18
|
-
return
|
|
19
|
+
return section(
|
|
20
|
+
'div',
|
|
21
|
+
'testimonial',
|
|
22
|
+
'cg-pullquote',
|
|
23
|
+
{},
|
|
19
24
|
'figure',
|
|
20
|
-
{ class: 'cg-
|
|
21
|
-
h('blockquote', { class: 'cg-testimonial__quote' }, renderRichText(ctx, block.quote)),
|
|
25
|
+
h('blockquote', { class: 'cg-pullquote__quote' }, renderRichText(ctx, block.quote)),
|
|
22
26
|
h(
|
|
23
27
|
'figcaption',
|
|
24
|
-
{ class: 'cg-
|
|
28
|
+
{ class: 'cg-pullquote__cite' },
|
|
25
29
|
attribution.avatar === undefined
|
|
26
30
|
? null
|
|
27
31
|
: image(ctx, attribution.avatar, {
|
|
28
|
-
className: 'cg-
|
|
29
|
-
variant: { width:
|
|
32
|
+
className: 'cg-pullquote__portrait',
|
|
33
|
+
variant: { width: 144, height: 144, fit: 'cover' },
|
|
30
34
|
}),
|
|
31
35
|
h(
|
|
32
36
|
'span',
|
|
33
|
-
{ class: 'cg-
|
|
34
|
-
h('span', { class: 'cg-
|
|
37
|
+
{ class: 'cg-pullquote__who' },
|
|
38
|
+
h('span', { class: 'cg-pullquote__name' }, attribution.name),
|
|
35
39
|
attribution.role === undefined
|
|
36
40
|
? null
|
|
37
|
-
: h('span', { class: 'cg-
|
|
41
|
+
: h('span', { class: 'cg-pullquote__role' }, attribution.role),
|
|
38
42
|
),
|
|
39
43
|
),
|
|
40
44
|
)
|