@contentrain/astro-kit 0.30.0 → 0.32.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/README.md +25 -2
- package/catalog.json +3305 -83
- package/components/_shared/Section.astro +18 -3
- package/components/_shared/measured.ts +152 -0
- package/components/_shared/types.ts +40 -0
- package/components/band/Band.astro +132 -0
- package/components/band/fixtures.json +8 -0
- package/components/band/meta.json +323 -0
- package/components/card-grid/CardGrid.astro +7 -3
- package/components/card-grid/fixtures.json +2 -1
- package/components/card-grid/meta.json +111 -0
- package/components/contact-details/ContactDetails.astro +7 -2
- package/components/contact-details/meta.json +113 -1
- package/components/contact-form/ContactForm.astro +48 -12
- package/components/contact-form/fixtures.json +169 -9
- package/components/contact-form/meta.json +283 -15
- package/components/cta/Cta.astro +52 -12
- package/components/cta/fixtures.json +5 -1
- package/components/cta/meta.json +11 -2
- package/components/faq/Faq.astro +60 -17
- package/components/faq/fixtures.json +75 -0
- package/components/faq/meta.json +8 -2
- package/components/feature-list/FeatureList.astro +34 -6
- package/components/feature-list/fixtures.json +2 -1
- package/components/feature-list/meta.json +125 -1
- package/components/gallery/Gallery.astro +47 -13
- package/components/gallery/fixtures.json +275 -0
- package/components/gallery/meta.json +266 -14
- package/components/hero/Hero.astro +14 -8
- package/components/hero/fixtures.json +2 -1
- package/components/hero/meta.json +111 -0
- package/components/logo-cloud/LogoCloud.astro +85 -21
- package/components/logo-cloud/fixtures.json +239 -0
- package/components/logo-cloud/meta.json +145 -5
- package/components/newsletter/Newsletter.astro +5 -1
- package/components/newsletter/meta.json +113 -1
- package/components/post-card/PostCard.astro +4 -2
- package/components/post-card/meta.json +2 -1
- package/components/post-list/PostList.astro +114 -0
- package/components/post-list/fixtures.json +387 -0
- package/components/post-list/meta.json +308 -0
- package/components/pricing/Pricing.astro +128 -24
- package/components/pricing/fixtures.json +288 -4
- package/components/pricing/meta.json +188 -1
- package/components/split/Split.astro +76 -14
- package/components/split/fixtures.json +8 -1
- package/components/split/meta.json +226 -4
- package/components/stats/Stats.astro +42 -10
- package/components/stats/fixtures.json +173 -0
- package/components/stats/meta.json +153 -5
- package/components/steps/Steps.astro +36 -8
- package/components/steps/fixtures.json +217 -5
- package/components/steps/meta.json +157 -3
- package/components/tabs/Tabs.astro +5 -2
- package/components/tabs/meta.json +2 -1
- package/components/team/Team.astro +52 -8
- package/components/team/fixtures.json +217 -4
- package/components/team/meta.json +168 -3
- package/components/team/team.ts +9 -0
- package/components/testimonial/Testimonial.astro +71 -21
- package/components/testimonial/fixtures.json +73 -0
- package/components/testimonial/meta.json +167 -3
- package/mapping/classic.json +7 -1
- package/mapping/divi.json +21 -4
- package/mapping/elementor.json +33 -8
- package/mapping/gutenberg.json +20 -4
- package/package.json +2 -2
|
@@ -2,5 +2,12 @@
|
|
|
2
2
|
{ "name": "end-default", "props": { "eyebrow": "Our studio", "heading": "Small team, careful work", "body": "We have built websites for independent publishers since 2012.\n\nEvery project gets **one** designer and one engineer from start to launch.", "actions": [{ "label": "Meet the team", "href": "/team/" }, { "label": "Our process", "href": "/process/", "style": "ghost" }], "image": { "src": "/kit-fixtures/photo-2.svg", "alt": "The studio's workshop", "width": 1200, "height": 800 } } },
|
|
3
3
|
{ "name": "start-muted", "props": { "imageSide": "start", "tone": "muted", "heading": "Content you can edit", "body": "Pages live as structured content in your own repository, editable in [Contentrain Studio](/studio/).", "image": { "src": "/kit-fixtures/photo-3.svg", "alt": "", "width": 1200, "height": 800 } } },
|
|
4
4
|
{ "name": "center-inverse", "props": { "align": "center", "tone": "inverse", "heading": "Fast by default", "body": "Static HTML and one stylesheet.", "actions": [{ "label": "See the numbers", "href": "/performance/" }], "image": { "src": "/kit-fixtures/photo-4.svg", "alt": "", "width": 1200, "height": 800 } } },
|
|
5
|
-
{ "name": "accent", "props": { "tone": "accent", "eyebrow": "New", "heading": "Now with search", "body": "Every page is indexed at build time.", "image": { "src": "/kit-fixtures/photo-5.svg", "alt": "", "width": 1200, "height": 800 } } }
|
|
5
|
+
{ "name": "accent", "props": { "tone": "accent", "eyebrow": "New", "heading": "Now with search", "body": "Every page is indexed at build time.", "image": { "src": "/kit-fixtures/photo-5.svg", "alt": "", "width": 1200, "height": 800 } } },
|
|
6
|
+
{ "name": "ratio-5-7-cover-square", "props": { "heading": "Small team, careful work", "body": "We have built websites for independent publishers since 2012.\n\nEvery project gets **one** designer and one engineer from start to launch.", "ratio": "5:7", "imageFit": "cover", "imageShape": "none", "image": { "src": "/kit-fixtures/photo-1.svg", "alt": "The studio's workshop", "width": 1200, "height": 800 } } },
|
|
7
|
+
{ "name": "ratio-7-5-start-features", "props": { "heading": "Why teams choose us", "body": "We have built websites for independent publishers since 2012.\n\nEvery project gets **one** designer and one engineer from start to launch.", "ratio": "7:5", "imageSide": "start", "features": [{ "title": "Fast", "text": "Static pages, one stylesheet.", "icon": "bolt" }, { "title": "Safe", "text": "Your repository stays yours.", "icon": "shield" }, { "title": "Editable", "text": "Change copy without a developer.", "icon": "chat" }], "featureStyle": "icon", "image": { "src": "/kit-fixtures/photo-2.svg", "alt": "The studio's workshop", "width": 1200, "height": 800 }, "tone": "muted" } },
|
|
8
|
+
{ "name": "circle-media-first", "props": { "heading": "Meet the founder", "body": "We have built websites for independent publishers since 2012.\n\nEvery project gets **one** designer and one engineer from start to launch.", "imageShape": "circle", "stackOrder": "media-first", "image": { "src": "/kit-fixtures/photo-3.svg", "alt": "The studio's workshop", "width": 1200, "height": 800 } } },
|
|
9
|
+
{ "name": "bleed-edge-end", "props": { "heading": "A wider picture", "body": "We have built websites for independent publishers since 2012.\n\nEvery project gets **one** designer and one engineer from start to launch.", "imageBleed": "edge", "image": { "src": "/kit-fixtures/photo-4.svg", "alt": "The studio's workshop", "width": 1200, "height": 800 }, "tone": "brand" } },
|
|
10
|
+
{ "name": "bleed-edge-start-none", "props": { "heading": "From the other side", "body": "We have built websites for independent publishers since 2012.\n\nEvery project gets **one** designer and one engineer from start to launch.", "imageBleed": "edge", "imageSide": "start", "image": { "src": "/kit-fixtures/photo-5.svg", "alt": "The studio's workshop", "width": 1200, "height": 800 }, "tone": "none" } },
|
|
11
|
+
{ "name": "video-sticky-check", "props": { "heading": "See how it works", "headingLevel": 3, "mediaKind": "video", "videoUrl": "https://www.youtube.com/watch?v=dQw4w9WgXcQ", "playLabel": "Play video", "sticky": true, "body": "We have built websites for independent publishers since 2012.\n\nEvery project gets **one** designer and one engineer from start to launch.\n\nWe have built websites for independent publishers since 2012.\n\nEvery project gets **one** designer and one engineer from start to launch.\n\nWe have built websites for independent publishers since 2012.\n\nEvery project gets **one** designer and one engineer from start to launch.", "features": [{ "title": "Fast", "text": "Static pages, one stylesheet.", "icon": "bolt" }, { "title": "Safe", "text": "Your repository stays yours.", "icon": "shield" }], "image": { "src": "/kit-fixtures/photo-6.svg", "alt": "The studio's workshop", "width": 1200, "height": 800 } } },
|
|
12
|
+
{ "name": "measured-frame", "props": { "eyebrow": "Our studio", "heading": "Small team, careful work", "body": "We have built websites for independent publishers since 2012.\n\nEvery project gets **one** designer and one engineer from start to launch.", "actions": [{ "label": "Meet the team", "href": "/team/" }, { "label": "Our process", "href": "/process/", "style": "ghost" }], "image": { "src": "/kit-fixtures/photo-2.svg", "alt": "The studio's workshop", "width": 1200, "height": 800 }, "measured": { "containerPx": 1000, "padY": 80, "bg": { "color": "#eef2ff" }, "tone": "light" } } }
|
|
6
13
|
]
|
|
@@ -3,11 +3,15 @@
|
|
|
3
3
|
"name": "Split",
|
|
4
4
|
"description": "An image beside a block of text — eyebrow, heading, a markdown body and buttons — on either side, stacked on phones. The media-and-text section of a landing page (Gutenberg media-text, a two-column Elementor container with an image, Divi's image + text row).",
|
|
5
5
|
"category": "section",
|
|
6
|
-
"uses": [
|
|
6
|
+
"uses": [
|
|
7
|
+
"embed",
|
|
8
|
+
"feature-list"
|
|
9
|
+
],
|
|
7
10
|
"shared": [
|
|
8
11
|
"Button.astro",
|
|
9
12
|
"KitImage.astro",
|
|
10
13
|
"Section.astro",
|
|
14
|
+
"measured.ts",
|
|
11
15
|
"types.ts"
|
|
12
16
|
],
|
|
13
17
|
"variants": {
|
|
@@ -19,6 +23,64 @@
|
|
|
19
23
|
"default": "end",
|
|
20
24
|
"description": "Which side the image takes on wide screens: start (left in left-to-right text) or end. Phones always show it after the text."
|
|
21
25
|
},
|
|
26
|
+
"ratio": {
|
|
27
|
+
"options": [
|
|
28
|
+
"1:1",
|
|
29
|
+
"5:7",
|
|
30
|
+
"7:5"
|
|
31
|
+
],
|
|
32
|
+
"default": "1:1",
|
|
33
|
+
"description": "The text column's share against the image's: equal, text 5 to image 7, or text 7 to image 5."
|
|
34
|
+
},
|
|
35
|
+
"imageFit": {
|
|
36
|
+
"options": [
|
|
37
|
+
"contain",
|
|
38
|
+
"cover"
|
|
39
|
+
],
|
|
40
|
+
"default": "contain",
|
|
41
|
+
"description": "contain: the image at its own proportions. cover: it fills the column's height."
|
|
42
|
+
},
|
|
43
|
+
"imageShape": {
|
|
44
|
+
"options": [
|
|
45
|
+
"none",
|
|
46
|
+
"rounded",
|
|
47
|
+
"circle"
|
|
48
|
+
],
|
|
49
|
+
"default": "rounded",
|
|
50
|
+
"description": "The image's corners: square, the theme's rounding, or a circle."
|
|
51
|
+
},
|
|
52
|
+
"imageBleed": {
|
|
53
|
+
"options": [
|
|
54
|
+
"none",
|
|
55
|
+
"edge"
|
|
56
|
+
],
|
|
57
|
+
"default": "none",
|
|
58
|
+
"description": "edge: the image runs on to the edge of the screen on its side (wide screens)."
|
|
59
|
+
},
|
|
60
|
+
"mediaKind": {
|
|
61
|
+
"options": [
|
|
62
|
+
"image",
|
|
63
|
+
"video"
|
|
64
|
+
],
|
|
65
|
+
"default": "image",
|
|
66
|
+
"description": "image: the picture. video: the picture is the poster of a video that loads only when played."
|
|
67
|
+
},
|
|
68
|
+
"featureStyle": {
|
|
69
|
+
"options": [
|
|
70
|
+
"check",
|
|
71
|
+
"icon"
|
|
72
|
+
],
|
|
73
|
+
"default": "check",
|
|
74
|
+
"description": "The marks of the points under the text: a check, or each point's own icon."
|
|
75
|
+
},
|
|
76
|
+
"stackOrder": {
|
|
77
|
+
"options": [
|
|
78
|
+
"text-first",
|
|
79
|
+
"media-first"
|
|
80
|
+
],
|
|
81
|
+
"default": "text-first",
|
|
82
|
+
"description": "On a phone: the text first, or the picture first."
|
|
83
|
+
},
|
|
22
84
|
"align": {
|
|
23
85
|
"options": [
|
|
24
86
|
"start",
|
|
@@ -32,10 +94,12 @@
|
|
|
32
94
|
"default",
|
|
33
95
|
"muted",
|
|
34
96
|
"inverse",
|
|
35
|
-
"accent"
|
|
97
|
+
"accent",
|
|
98
|
+
"brand",
|
|
99
|
+
"none"
|
|
36
100
|
],
|
|
37
101
|
"default": "default",
|
|
38
|
-
"description": "Section background."
|
|
102
|
+
"description": "Section background (brand: the site's brand colour; none: the page's own)."
|
|
39
103
|
}
|
|
40
104
|
},
|
|
41
105
|
"props": {
|
|
@@ -45,6 +109,12 @@
|
|
|
45
109
|
"required": true,
|
|
46
110
|
"description": "The section heading."
|
|
47
111
|
},
|
|
112
|
+
"headingLevel": {
|
|
113
|
+
"type": "integer",
|
|
114
|
+
"default": 2,
|
|
115
|
+
"content": false,
|
|
116
|
+
"description": "The heading's level: 2 under the page title; 3 or 4 when the section sits inside another."
|
|
117
|
+
},
|
|
48
118
|
"eyebrow": {
|
|
49
119
|
"type": "string",
|
|
50
120
|
"label": "Eyebrow",
|
|
@@ -120,10 +190,162 @@
|
|
|
120
190
|
},
|
|
121
191
|
"description": "The image beside the text. Width and height, when known, let the site reserve its space."
|
|
122
192
|
},
|
|
193
|
+
"features": {
|
|
194
|
+
"type": "array",
|
|
195
|
+
"label": "Points",
|
|
196
|
+
"items": {
|
|
197
|
+
"type": "object",
|
|
198
|
+
"fields": {
|
|
199
|
+
"title": {
|
|
200
|
+
"type": "string",
|
|
201
|
+
"label": "Title",
|
|
202
|
+
"required": true,
|
|
203
|
+
"description": "The point, in a few words."
|
|
204
|
+
},
|
|
205
|
+
"text": {
|
|
206
|
+
"type": "string",
|
|
207
|
+
"label": "Text",
|
|
208
|
+
"description": "A line about it."
|
|
209
|
+
},
|
|
210
|
+
"icon": {
|
|
211
|
+
"type": "string",
|
|
212
|
+
"label": "Icon",
|
|
213
|
+
"description": "A kit icon name (check, star, bolt, shield, heart, globe, clock, chat)."
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
"description": "Short points under the text (an Elementor icon list or icon box, a Gutenberg list), each with a title and a line."
|
|
218
|
+
},
|
|
219
|
+
"videoUrl": {
|
|
220
|
+
"type": "url",
|
|
221
|
+
"label": "Video",
|
|
222
|
+
"description": "A video page (YouTube, Vimeo); the image is its poster and nothing loads from the provider until it is played."
|
|
223
|
+
},
|
|
224
|
+
"playLabel": {
|
|
225
|
+
"type": "string",
|
|
226
|
+
"content": false,
|
|
227
|
+
"description": "The play button's text (ui-strings embed.play)."
|
|
228
|
+
},
|
|
229
|
+
"measured": {
|
|
230
|
+
"type": "object",
|
|
231
|
+
"content": false,
|
|
232
|
+
"description": "The source section's measured style (MeasuredStyle): frame width, padding, fill, image, columns per width, gap, text tone, radius, minimum height — all in pixels. Absent, the site's tokens; present, it wins over tone and columns. Values that are not a number, a colour or a known keyword are dropped.",
|
|
233
|
+
"fields": {
|
|
234
|
+
"containerPx": {
|
|
235
|
+
"type": "integer",
|
|
236
|
+
"min": 0,
|
|
237
|
+
"max": 2560,
|
|
238
|
+
"description": "The frame's content width."
|
|
239
|
+
},
|
|
240
|
+
"padY": {
|
|
241
|
+
"type": "integer",
|
|
242
|
+
"min": 0,
|
|
243
|
+
"max": 400,
|
|
244
|
+
"description": "Top and bottom padding."
|
|
245
|
+
},
|
|
246
|
+
"padX": {
|
|
247
|
+
"type": "integer",
|
|
248
|
+
"min": 0,
|
|
249
|
+
"max": 200,
|
|
250
|
+
"description": "The frame's side padding (gutter)."
|
|
251
|
+
},
|
|
252
|
+
"bg": {
|
|
253
|
+
"type": "object",
|
|
254
|
+
"description": "The section's background.",
|
|
255
|
+
"fields": {
|
|
256
|
+
"color": {
|
|
257
|
+
"type": "string",
|
|
258
|
+
"description": "Fill colour: hex, rgb(a) or hsl(a)."
|
|
259
|
+
},
|
|
260
|
+
"image": {
|
|
261
|
+
"type": "string",
|
|
262
|
+
"description": "Background image address (http(s) or root-relative), drawn behind the content."
|
|
263
|
+
},
|
|
264
|
+
"overlay": {
|
|
265
|
+
"type": "string",
|
|
266
|
+
"description": "Colour or linear/radial gradient laid over the image or the fill."
|
|
267
|
+
},
|
|
268
|
+
"size": {
|
|
269
|
+
"type": "string",
|
|
270
|
+
"description": "cover, contain or auto; anything else draws as cover."
|
|
271
|
+
},
|
|
272
|
+
"position": {
|
|
273
|
+
"type": "string",
|
|
274
|
+
"description": "One or two of center/top/bottom/left/right or percentages."
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
},
|
|
278
|
+
"columns": {
|
|
279
|
+
"type": "object",
|
|
280
|
+
"description": "Item columns at 390 (base), 768 (md) and 1280 (lg) px; a missing width follows the narrower one.",
|
|
281
|
+
"fields": {
|
|
282
|
+
"390": {
|
|
283
|
+
"type": "integer",
|
|
284
|
+
"min": 1,
|
|
285
|
+
"max": 6,
|
|
286
|
+
"description": "Phone."
|
|
287
|
+
},
|
|
288
|
+
"768": {
|
|
289
|
+
"type": "integer",
|
|
290
|
+
"min": 1,
|
|
291
|
+
"max": 6,
|
|
292
|
+
"description": "Tablet."
|
|
293
|
+
},
|
|
294
|
+
"1280": {
|
|
295
|
+
"type": "integer",
|
|
296
|
+
"min": 1,
|
|
297
|
+
"max": 6,
|
|
298
|
+
"description": "Desktop."
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
},
|
|
302
|
+
"gap": {
|
|
303
|
+
"type": "integer",
|
|
304
|
+
"min": 0,
|
|
305
|
+
"max": 200,
|
|
306
|
+
"description": "Gap between items."
|
|
307
|
+
},
|
|
308
|
+
"tone": {
|
|
309
|
+
"type": "select",
|
|
310
|
+
"options": [
|
|
311
|
+
"light",
|
|
312
|
+
"dark"
|
|
313
|
+
],
|
|
314
|
+
"description": "What the text is set for: dark = light text on a dark band. Without it, a fill's own lightness decides."
|
|
315
|
+
},
|
|
316
|
+
"align": {
|
|
317
|
+
"type": "select",
|
|
318
|
+
"options": [
|
|
319
|
+
"start",
|
|
320
|
+
"center",
|
|
321
|
+
"end"
|
|
322
|
+
],
|
|
323
|
+
"description": "Where the heading and lead sit; read by a component whose own align is not set."
|
|
324
|
+
},
|
|
325
|
+
"radius": {
|
|
326
|
+
"type": "integer",
|
|
327
|
+
"min": 0,
|
|
328
|
+
"max": 200,
|
|
329
|
+
"description": "Card and item corner radius."
|
|
330
|
+
},
|
|
331
|
+
"minHeight": {
|
|
332
|
+
"type": "integer",
|
|
333
|
+
"min": 0,
|
|
334
|
+
"max": 2000,
|
|
335
|
+
"description": "The section's minimum height."
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
},
|
|
123
339
|
"id": {
|
|
124
340
|
"type": "string",
|
|
125
341
|
"content": false,
|
|
126
342
|
"description": "Anchor id of the section; the heading gets `<id>-title`."
|
|
343
|
+
},
|
|
344
|
+
"sticky": {
|
|
345
|
+
"type": "boolean",
|
|
346
|
+
"default": false,
|
|
347
|
+
"content": false,
|
|
348
|
+
"description": "The picture stays in view while a long text scrolls past it (wide screens)."
|
|
127
349
|
}
|
|
128
350
|
},
|
|
129
351
|
"slots": {},
|
|
@@ -140,7 +362,7 @@
|
|
|
140
362
|
"divi/et_pb_image"
|
|
141
363
|
]
|
|
142
364
|
},
|
|
143
|
-
"a11y": "The section is labelled by its heading. The image is a separate figure in reading order after the text; an empty description marks a decorative one.",
|
|
365
|
+
"a11y": "The section is labelled by its heading. The image is a separate figure in reading order after the text; an empty description marks a decorative one. With stackOrder media-first the picture comes first on a phone by CSS order only: the DOM and so the reading order stay text first, which is right when the picture is decoration; give the picture an empty description then.",
|
|
144
366
|
"pattern": {
|
|
145
367
|
"summary": "image beside heading + text",
|
|
146
368
|
"signals": [
|
|
@@ -2,24 +2,47 @@
|
|
|
2
2
|
// A row of figures, each with a short label, under an optional heading and
|
|
3
3
|
// intro. Figures are static text: no counting animation.
|
|
4
4
|
import { tv } from 'tailwind-variants'
|
|
5
|
+
import KitImage from '../_shared/KitImage.astro'
|
|
5
6
|
import Section, { type Tone } from '../_shared/Section.astro'
|
|
7
|
+
import { imageOf, type ImageSource } from '../_shared/image'
|
|
8
|
+
import { measuredGrid } from '../_shared/measured'
|
|
9
|
+
import type { MeasuredStyle } from '../_shared/types'
|
|
6
10
|
|
|
7
11
|
const stats = tv({
|
|
8
12
|
slots: {
|
|
9
13
|
list: 'grid grid-cols-2 gap-6 md:gap-8',
|
|
10
14
|
item: 'flex gap-1',
|
|
11
15
|
value: 'text-4xl [font-weight:var(--font-weight-heading,700)] tracking-tight tabular-nums md:text-5xl',
|
|
16
|
+
icon: 'mb-3 block size-10 object-contain',
|
|
12
17
|
label: 'text-sm opacity-80',
|
|
13
18
|
},
|
|
14
19
|
variants: {
|
|
20
|
+
// grid: figures in columns. inline: one line that wraps, for two to four short figures.
|
|
21
|
+
layout: {
|
|
22
|
+
grid: {},
|
|
23
|
+
inline: { list: 'flex flex-wrap gap-x-12 gap-y-6 md:gap-x-16' },
|
|
24
|
+
},
|
|
15
25
|
columns: {
|
|
16
26
|
'2': { list: 'md:grid-cols-2' },
|
|
17
27
|
'3': { list: 'md:grid-cols-3' },
|
|
18
28
|
'4': { list: 'md:grid-cols-4' },
|
|
29
|
+
'5': { list: 'md:grid-cols-3 lg:grid-cols-5' },
|
|
30
|
+
'6': { list: 'md:grid-cols-3 lg:grid-cols-6' },
|
|
19
31
|
},
|
|
20
32
|
style: {
|
|
21
33
|
plain: {},
|
|
22
34
|
card: { item: 'rounded-[var(--radius-card)] bg-surface p-6 text-ink ring-1 ring-line/60' },
|
|
35
|
+
// Hairlines between the figures, none before the first, no box: across on a wide screen, above each on a phone.
|
|
36
|
+
// Meant for a single row (figures no more than the columns); a wrapped row starts with a line.
|
|
37
|
+
divided: { item: 'border-t border-line pt-6 first:border-t-0 first:pt-0 md:border-t-0 md:pt-0 md:border-s md:ps-6 md:first:border-s-0 md:first:ps-0' },
|
|
38
|
+
},
|
|
39
|
+
align: { start: {}, center: { item: 'items-center text-center', icon: 'mx-auto' } },
|
|
40
|
+
// The figure's size: md, lg (the default), xl, or a display number.
|
|
41
|
+
valueSize: {
|
|
42
|
+
md: { value: 'text-3xl md:text-4xl' },
|
|
43
|
+
lg: {},
|
|
44
|
+
xl: { value: 'text-5xl md:text-6xl' },
|
|
45
|
+
display: { value: 'text-6xl md:text-7xl' },
|
|
23
46
|
},
|
|
24
47
|
// accent/accent-ink is only ~5:1 — dimming text on it drops it under 4.5:1 (WCAG 1.4.3).
|
|
25
48
|
tone: { default: {}, muted: {}, inverse: {}, accent: { label: 'opacity-100' } },
|
|
@@ -29,7 +52,7 @@ const stats = tv({
|
|
|
29
52
|
// Elementor counter names itself first). The figure is drawn on top either way.
|
|
30
53
|
order: { 'label-first': { item: 'flex-col-reverse' }, 'value-first': { item: 'flex-col' } },
|
|
31
54
|
},
|
|
32
|
-
defaultVariants: { columns: '3', style: 'plain', tone: 'default', labelSize: 'sm', order: 'label-first' },
|
|
55
|
+
defaultVariants: { layout: 'grid', columns: '3', style: 'plain', align: 'start', valueSize: 'lg', tone: 'default', labelSize: 'sm', order: 'label-first' },
|
|
33
56
|
})
|
|
34
57
|
|
|
35
58
|
interface Item {
|
|
@@ -37,26 +60,35 @@ interface Item {
|
|
|
37
60
|
label: string
|
|
38
61
|
prefix?: string | undefined
|
|
39
62
|
suffix?: string | undefined
|
|
63
|
+
/** A small picture above the figure (its address); decorative, so it carries no text. */
|
|
64
|
+
icon?: ImageSource | undefined
|
|
40
65
|
}
|
|
41
66
|
|
|
42
67
|
interface Props {
|
|
43
68
|
heading?: string | undefined
|
|
44
69
|
intro?: string | undefined
|
|
45
70
|
items: Item[]
|
|
46
|
-
|
|
47
|
-
|
|
71
|
+
layout?: 'grid' | 'inline' | undefined
|
|
72
|
+
columns?: '2' | '3' | '4' | '5' | '6' | undefined
|
|
73
|
+
style?: 'plain' | 'card' | 'divided' | undefined
|
|
74
|
+
align?: 'start' | 'center' | undefined
|
|
75
|
+
valueSize?: 'md' | 'lg' | 'xl' | 'display' | undefined
|
|
48
76
|
labelSize?: 'sm' | 'base' | 'lg' | undefined
|
|
49
77
|
order?: 'label-first' | 'value-first' | undefined
|
|
50
78
|
tone?: Tone | undefined
|
|
79
|
+
/** The source section's measured style (`MeasuredStyle`): frame, padding, fill, background image, columns per width, gap. Absent, the site's tokens; present, it wins over tone and columns. */
|
|
80
|
+
measured?: MeasuredStyle | undefined
|
|
51
81
|
id?: string | undefined
|
|
52
82
|
}
|
|
53
83
|
|
|
54
|
-
const { heading, intro, items, columns, style, labelSize, order, tone, id } = Astro.props
|
|
55
|
-
const s = stats({ columns, style, labelSize, order, tone })
|
|
84
|
+
const { heading, intro, items, layout, columns, style, align, valueSize, labelSize, order, tone, measured, id } = Astro.props
|
|
85
|
+
const s = stats({ layout, columns, style, align, valueSize, labelSize, order, tone })
|
|
86
|
+
const grid = (layout ?? 'grid') === 'grid' ? measuredGrid(measured) : undefined
|
|
56
87
|
const headingId = heading && id ? `${id}-title` : undefined
|
|
88
|
+
const iconOf = (item: Item) => imageOf(item.icon, '')
|
|
57
89
|
// A <dl> names its term first, so figure-first is a plain list, read in the order it is drawn (WCAG 1.3.2).
|
|
58
90
|
---
|
|
59
|
-
<Section tone={tone} id={id} aria-labelledby={headingId}>
|
|
91
|
+
<Section measured={measured} tone={tone} id={id} aria-labelledby={headingId}>
|
|
60
92
|
{(heading || intro) && (
|
|
61
93
|
<header class="mb-10 flex max-w-2xl flex-col gap-3">
|
|
62
94
|
{heading && <h2 id={headingId} class="text-3xl [font-weight:var(--font-weight-heading,700)] tracking-tight text-balance md:text-4xl">{heading}</h2>}
|
|
@@ -64,20 +96,20 @@ const headingId = heading && id ? `${id}-title` : undefined
|
|
|
64
96
|
</header>
|
|
65
97
|
)}
|
|
66
98
|
{order === 'value-first' ? (
|
|
67
|
-
<ul class={s.list()} role="list">
|
|
99
|
+
<ul class={s.list({ class: grid })} role="list">
|
|
68
100
|
{items.map(item => (
|
|
69
101
|
<li class={s.item()}>
|
|
70
|
-
<p class={s.value()}>{item.prefix}{item.value}{item.suffix}</p>
|
|
102
|
+
<p class={s.value()}>{iconOf(item) && <KitImage image={iconOf(item)!} sizes="40px" class={s.icon()} />}{item.prefix}{item.value}{item.suffix}</p>
|
|
71
103
|
<p class={s.label()}>{item.label}</p>
|
|
72
104
|
</li>
|
|
73
105
|
))}
|
|
74
106
|
</ul>
|
|
75
107
|
) : (
|
|
76
|
-
<dl class={s.list()}>
|
|
108
|
+
<dl class={s.list({ class: grid })}>
|
|
77
109
|
{items.map(item => (
|
|
78
110
|
<div class={s.item()}>
|
|
79
111
|
<dt class={s.label()}>{item.label}</dt>
|
|
80
|
-
<dd class={s.value()}>{item.prefix}{item.value}{item.suffix}</dd>
|
|
112
|
+
<dd class={s.value()}>{iconOf(item) && <KitImage image={iconOf(item)!} sizes="40px" class={s.icon()} />}{item.prefix}{item.value}{item.suffix}</dd>
|
|
81
113
|
</div>
|
|
82
114
|
))}
|
|
83
115
|
</dl>
|
|
@@ -156,5 +156,178 @@
|
|
|
156
156
|
}
|
|
157
157
|
]
|
|
158
158
|
}
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
"name": "divided-center-md",
|
|
162
|
+
"props": {
|
|
163
|
+
"items": [
|
|
164
|
+
{
|
|
165
|
+
"value": "12",
|
|
166
|
+
"label": "Years in business",
|
|
167
|
+
"suffix": "+"
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
"value": "480",
|
|
171
|
+
"label": "Clients served"
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
"value": "99.9",
|
|
175
|
+
"label": "Uptime",
|
|
176
|
+
"suffix": "%"
|
|
177
|
+
}
|
|
178
|
+
],
|
|
179
|
+
"style": "divided",
|
|
180
|
+
"align": "center",
|
|
181
|
+
"valueSize": "md"
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
"name": "inline-xl",
|
|
186
|
+
"props": {
|
|
187
|
+
"items": [
|
|
188
|
+
{
|
|
189
|
+
"value": "12",
|
|
190
|
+
"label": "Years in business",
|
|
191
|
+
"suffix": "+"
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
"value": "480",
|
|
195
|
+
"label": "Clients served"
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
"value": "99.9",
|
|
199
|
+
"label": "Uptime",
|
|
200
|
+
"suffix": "%"
|
|
201
|
+
}
|
|
202
|
+
],
|
|
203
|
+
"layout": "inline",
|
|
204
|
+
"valueSize": "xl",
|
|
205
|
+
"heading": "In numbers"
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
"name": "display-icons",
|
|
210
|
+
"props": {
|
|
211
|
+
"items": [
|
|
212
|
+
{
|
|
213
|
+
"value": "12",
|
|
214
|
+
"label": "Years in business",
|
|
215
|
+
"suffix": "+",
|
|
216
|
+
"icon": "/kit-fixtures/logo.svg"
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
"value": "480",
|
|
220
|
+
"label": "Clients served",
|
|
221
|
+
"icon": "/kit-fixtures/logo.svg"
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
"value": "99.9",
|
|
225
|
+
"label": "Uptime",
|
|
226
|
+
"suffix": "%",
|
|
227
|
+
"icon": "/kit-fixtures/logo.svg"
|
|
228
|
+
}
|
|
229
|
+
],
|
|
230
|
+
"valueSize": "display"
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
"name": "five-columns",
|
|
235
|
+
"props": {
|
|
236
|
+
"items": [
|
|
237
|
+
{
|
|
238
|
+
"value": "12",
|
|
239
|
+
"label": "Years in business",
|
|
240
|
+
"suffix": "+"
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
"value": "480",
|
|
244
|
+
"label": "Clients served"
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
"value": "99.9",
|
|
248
|
+
"label": "Uptime",
|
|
249
|
+
"suffix": "%"
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
"value": "7",
|
|
253
|
+
"label": "Offices"
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
"value": "24/7",
|
|
257
|
+
"label": "Support"
|
|
258
|
+
}
|
|
259
|
+
],
|
|
260
|
+
"columns": "5"
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
"name": "six-columns",
|
|
265
|
+
"props": {
|
|
266
|
+
"items": [
|
|
267
|
+
{
|
|
268
|
+
"value": "12",
|
|
269
|
+
"label": "Years in business",
|
|
270
|
+
"suffix": "+"
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
"value": "480",
|
|
274
|
+
"label": "Clients served"
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
"value": "99.9",
|
|
278
|
+
"label": "Uptime",
|
|
279
|
+
"suffix": "%"
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
"value": "7",
|
|
283
|
+
"label": "Offices"
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
"value": "24/7",
|
|
287
|
+
"label": "Support"
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
"value": "3",
|
|
291
|
+
"label": "Awards"
|
|
292
|
+
}
|
|
293
|
+
],
|
|
294
|
+
"columns": "6"
|
|
295
|
+
}
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
"name": "measured-columns",
|
|
299
|
+
"props": {
|
|
300
|
+
"heading": "By the numbers",
|
|
301
|
+
"intro": "A decade of shipping sites for small teams.",
|
|
302
|
+
"items": [
|
|
303
|
+
{
|
|
304
|
+
"value": "12",
|
|
305
|
+
"label": "Years in business",
|
|
306
|
+
"suffix": "+"
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
"value": "480",
|
|
310
|
+
"label": "Clients served"
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
"value": "99.9",
|
|
314
|
+
"label": "Uptime",
|
|
315
|
+
"suffix": "%"
|
|
316
|
+
}
|
|
317
|
+
],
|
|
318
|
+
"measured": {
|
|
319
|
+
"columns": {
|
|
320
|
+
"390": 1,
|
|
321
|
+
"768": 2,
|
|
322
|
+
"1280": 4
|
|
323
|
+
},
|
|
324
|
+
"gap": 40,
|
|
325
|
+
"padY": 96,
|
|
326
|
+
"bg": {
|
|
327
|
+
"color": "#0f172a"
|
|
328
|
+
},
|
|
329
|
+
"tone": "dark"
|
|
330
|
+
}
|
|
331
|
+
}
|
|
159
332
|
}
|
|
160
333
|
]
|