@contentrain/astro-kit 0.31.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/catalog.json +2531 -345
- package/components/_shared/measured.ts +10 -0
- 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 +7 -1
- package/components/feature-list/FeatureList.astro +28 -4
- package/components/feature-list/meta.json +15 -3
- package/components/gallery/Gallery.astro +47 -13
- package/components/gallery/fixtures.json +275 -0
- package/components/gallery/meta.json +266 -14
- package/components/logo-cloud/LogoCloud.astro +85 -21
- package/components/logo-cloud/fixtures.json +239 -0
- package/components/logo-cloud/meta.json +144 -5
- 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 +187 -1
- package/components/split/Split.astro +76 -14
- package/components/split/fixtures.json +8 -1
- package/components/split/meta.json +225 -4
- package/components/stats/Stats.astro +42 -10
- package/components/stats/fixtures.json +173 -0
- package/components/stats/meta.json +150 -4
- package/components/steps/Steps.astro +36 -8
- package/components/steps/fixtures.json +217 -5
- package/components/steps/meta.json +156 -3
- package/components/team/Team.astro +52 -8
- package/components/team/fixtures.json +217 -4
- package/components/team/meta.json +167 -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 +166 -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 +1 -1
|
@@ -1,7 +1,219 @@
|
|
|
1
1
|
[
|
|
2
|
-
{
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
{
|
|
3
|
+
"name": "list-numbered",
|
|
4
|
+
"props": {
|
|
5
|
+
"id": "how",
|
|
6
|
+
"heading": "How it works",
|
|
7
|
+
"intro": "Three steps from WordPress to a static site.",
|
|
8
|
+
"items": [
|
|
9
|
+
{
|
|
10
|
+
"title": "Connect",
|
|
11
|
+
"text": "Point us at your site; we read everything it publishes."
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"title": "Review",
|
|
15
|
+
"text": "Check the plan: every page, model and redirect."
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"title": "Launch",
|
|
19
|
+
"text": "Deploy the new site; every old address still works."
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"name": "row-plain-images",
|
|
26
|
+
"props": {
|
|
27
|
+
"layout": "row",
|
|
28
|
+
"style": "plain",
|
|
29
|
+
"heading": "Our process",
|
|
30
|
+
"items": [
|
|
31
|
+
{
|
|
32
|
+
"title": "Discover",
|
|
33
|
+
"text": "We learn the site and its readers.",
|
|
34
|
+
"image": "/kit-fixtures/photo-1.svg"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"title": "Design",
|
|
38
|
+
"text": "We draw the pages and the system.",
|
|
39
|
+
"image": "/kit-fixtures/photo-2.svg"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"title": "Build",
|
|
43
|
+
"text": "We ship it, fast and accessible.",
|
|
44
|
+
"image": "/kit-fixtures/photo-3.svg",
|
|
45
|
+
"imageAlt": "A laptop showing the finished site"
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"name": "row-numbered",
|
|
52
|
+
"props": {
|
|
53
|
+
"layout": "row",
|
|
54
|
+
"items": [
|
|
55
|
+
{
|
|
56
|
+
"title": "Sign up",
|
|
57
|
+
"text": "One minute, no card."
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"title": "Import",
|
|
61
|
+
"text": "Bring your content."
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"title": "Edit",
|
|
65
|
+
"text": "Change it in Studio."
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"title": "Publish",
|
|
69
|
+
"text": "Push and it is live."
|
|
70
|
+
}
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"name": "source-numbers",
|
|
76
|
+
"props": {
|
|
77
|
+
"heading": "Getting started",
|
|
78
|
+
"items": [
|
|
79
|
+
{
|
|
80
|
+
"number": "01",
|
|
81
|
+
"title": "Connect",
|
|
82
|
+
"text": "Point Calm Status at the services you already monitor."
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"number": "02",
|
|
86
|
+
"title": "Write",
|
|
87
|
+
"text": "Pick the words customers see for each kind of problem."
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"number": "03",
|
|
91
|
+
"title": "Publish",
|
|
92
|
+
"text": "Put the page live on your own domain."
|
|
93
|
+
}
|
|
94
|
+
]
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"name": "row-plain-source-numbers",
|
|
99
|
+
"props": {
|
|
100
|
+
"layout": "row",
|
|
101
|
+
"style": "plain",
|
|
102
|
+
"items": [
|
|
103
|
+
{
|
|
104
|
+
"number": "Step 1",
|
|
105
|
+
"title": "Call",
|
|
106
|
+
"text": "We learn what you make and who buys it."
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"number": "Step 2",
|
|
110
|
+
"title": "Design and build",
|
|
111
|
+
"text": "Four weeks of weekly previews."
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
"number": "Step 3",
|
|
115
|
+
"title": "Launch",
|
|
116
|
+
"text": "We switch it on together."
|
|
117
|
+
}
|
|
118
|
+
]
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"name": "timeline-rail",
|
|
123
|
+
"props": {
|
|
124
|
+
"id": "history",
|
|
125
|
+
"heading": "Our story",
|
|
126
|
+
"kind": "timeline",
|
|
127
|
+
"marker": "dot",
|
|
128
|
+
"items": [
|
|
129
|
+
{
|
|
130
|
+
"date": "2019",
|
|
131
|
+
"title": "Founded",
|
|
132
|
+
"text": "Two people and a shared desk."
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"date": "Mar 2021",
|
|
136
|
+
"title": "First customers",
|
|
137
|
+
"text": "The first twenty sites moved over."
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
"date": "2024",
|
|
141
|
+
"title": "Open source",
|
|
142
|
+
"text": "The toolkit went public.",
|
|
143
|
+
"current": true
|
|
144
|
+
}
|
|
145
|
+
]
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"name": "timeline-alternate-dashed",
|
|
150
|
+
"props": {
|
|
151
|
+
"kind": "timeline",
|
|
152
|
+
"layout": "alternate",
|
|
153
|
+
"connector": "dashed",
|
|
154
|
+
"items": [
|
|
155
|
+
{
|
|
156
|
+
"date": "2019",
|
|
157
|
+
"title": "Founded",
|
|
158
|
+
"text": "Two people and a shared desk."
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
"date": "Mar 2021",
|
|
162
|
+
"title": "First customers",
|
|
163
|
+
"text": "The first twenty sites moved over."
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
"date": "2024",
|
|
167
|
+
"title": "Open source",
|
|
168
|
+
"text": "The toolkit went public.",
|
|
169
|
+
"current": true
|
|
170
|
+
}
|
|
171
|
+
]
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
"name": "timeline-no-connector",
|
|
176
|
+
"props": {
|
|
177
|
+
"kind": "timeline",
|
|
178
|
+
"layout": "rail",
|
|
179
|
+
"connector": "none",
|
|
180
|
+
"items": [
|
|
181
|
+
{
|
|
182
|
+
"date": "2019",
|
|
183
|
+
"title": "Founded",
|
|
184
|
+
"text": "Two people and a shared desk."
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
"date": "Mar 2021",
|
|
188
|
+
"title": "First customers",
|
|
189
|
+
"text": "The first twenty sites moved over."
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
"title": "No date here",
|
|
193
|
+
"text": "An item with no date reads as a step."
|
|
194
|
+
}
|
|
195
|
+
]
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
"name": "rail-numbered",
|
|
200
|
+
"props": {
|
|
201
|
+
"layout": "rail",
|
|
202
|
+
"marker": "number",
|
|
203
|
+
"items": [
|
|
204
|
+
{
|
|
205
|
+
"title": "Connect",
|
|
206
|
+
"text": "Point us at your site; we read everything it publishes."
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
"title": "Review",
|
|
210
|
+
"text": "Check the plan: every page, model and redirect."
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
"title": "Launch",
|
|
214
|
+
"text": "Deploy the new site; every old address still works."
|
|
215
|
+
}
|
|
216
|
+
]
|
|
217
|
+
}
|
|
218
|
+
}
|
|
7
219
|
]
|
|
@@ -15,10 +15,12 @@
|
|
|
15
15
|
"layout": {
|
|
16
16
|
"options": [
|
|
17
17
|
"list",
|
|
18
|
-
"row"
|
|
18
|
+
"row",
|
|
19
|
+
"rail",
|
|
20
|
+
"alternate"
|
|
19
21
|
],
|
|
20
22
|
"default": "list",
|
|
21
|
-
"description": "
|
|
23
|
+
"description": "list: a vertical list. row: across the page. rail: one rail on the start side. alternate: items alternate either side of a centre rail from md (one rail on phones); DOM order stays chronological."
|
|
22
24
|
},
|
|
23
25
|
"style": {
|
|
24
26
|
"options": [
|
|
@@ -27,6 +29,31 @@
|
|
|
27
29
|
],
|
|
28
30
|
"default": "numbered",
|
|
29
31
|
"description": "A number badge before each step, or an accent rule beside it."
|
|
32
|
+
},
|
|
33
|
+
"kind": {
|
|
34
|
+
"options": [
|
|
35
|
+
"steps",
|
|
36
|
+
"timeline"
|
|
37
|
+
],
|
|
38
|
+
"default": "steps",
|
|
39
|
+
"description": "steps: a numbered sequence. timeline: a date per item on a vertical rail (the rail layouts below; without a layout it uses rail)."
|
|
40
|
+
},
|
|
41
|
+
"marker": {
|
|
42
|
+
"options": [
|
|
43
|
+
"number",
|
|
44
|
+
"dot"
|
|
45
|
+
],
|
|
46
|
+
"default": "number",
|
|
47
|
+
"description": "What sits on the rail: the style's number badge, or a plain dot (the timeline default). Not yet: icon and date markers, and a visual emphasis for the current item (it has aria-current and a hidden label only)."
|
|
48
|
+
},
|
|
49
|
+
"connector": {
|
|
50
|
+
"options": [
|
|
51
|
+
"none",
|
|
52
|
+
"line",
|
|
53
|
+
"dashed"
|
|
54
|
+
],
|
|
55
|
+
"default": "line",
|
|
56
|
+
"description": "The rail between items on the rail layouts."
|
|
30
57
|
}
|
|
31
58
|
},
|
|
32
59
|
"props": {
|
|
@@ -73,6 +100,16 @@
|
|
|
73
100
|
"type": "string",
|
|
74
101
|
"label": "Image description",
|
|
75
102
|
"description": "What the image shows; empty if decorative."
|
|
103
|
+
},
|
|
104
|
+
"date": {
|
|
105
|
+
"type": "string",
|
|
106
|
+
"label": "Date",
|
|
107
|
+
"description": "A timeline's date as written (\"2019\", \"Mar 2021\"); marked up as a <time> only when it is an ISO date or a year."
|
|
108
|
+
},
|
|
109
|
+
"current": {
|
|
110
|
+
"type": "boolean",
|
|
111
|
+
"label": "Current",
|
|
112
|
+
"description": "Marks the latest item (aria-current=\"step\" and a hidden \"Current\")."
|
|
76
113
|
}
|
|
77
114
|
}
|
|
78
115
|
},
|
|
@@ -82,6 +119,122 @@
|
|
|
82
119
|
"type": "string",
|
|
83
120
|
"content": false,
|
|
84
121
|
"description": "Anchor id of the section; the heading gets `<id>-title`."
|
|
122
|
+
},
|
|
123
|
+
"currentLabel": {
|
|
124
|
+
"type": "string",
|
|
125
|
+
"default": "Current",
|
|
126
|
+
"content": false,
|
|
127
|
+
"description": "Visually hidden text on the current item, so it is not shown by colour alone."
|
|
128
|
+
},
|
|
129
|
+
"measured": {
|
|
130
|
+
"type": "object",
|
|
131
|
+
"content": false,
|
|
132
|
+
"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.",
|
|
133
|
+
"fields": {
|
|
134
|
+
"containerPx": {
|
|
135
|
+
"type": "integer",
|
|
136
|
+
"min": 0,
|
|
137
|
+
"max": 2560,
|
|
138
|
+
"description": "The frame's content width."
|
|
139
|
+
},
|
|
140
|
+
"padY": {
|
|
141
|
+
"type": "integer",
|
|
142
|
+
"min": 0,
|
|
143
|
+
"max": 400,
|
|
144
|
+
"description": "Top and bottom padding."
|
|
145
|
+
},
|
|
146
|
+
"padX": {
|
|
147
|
+
"type": "integer",
|
|
148
|
+
"min": 0,
|
|
149
|
+
"max": 200,
|
|
150
|
+
"description": "The frame's side padding (gutter)."
|
|
151
|
+
},
|
|
152
|
+
"bg": {
|
|
153
|
+
"type": "object",
|
|
154
|
+
"description": "The section's background.",
|
|
155
|
+
"fields": {
|
|
156
|
+
"color": {
|
|
157
|
+
"type": "string",
|
|
158
|
+
"description": "Fill colour: hex, rgb(a) or hsl(a)."
|
|
159
|
+
},
|
|
160
|
+
"image": {
|
|
161
|
+
"type": "string",
|
|
162
|
+
"description": "Background image address (http(s) or root-relative), drawn behind the content."
|
|
163
|
+
},
|
|
164
|
+
"overlay": {
|
|
165
|
+
"type": "string",
|
|
166
|
+
"description": "Colour or linear/radial gradient laid over the image or the fill."
|
|
167
|
+
},
|
|
168
|
+
"size": {
|
|
169
|
+
"type": "string",
|
|
170
|
+
"description": "cover, contain or auto; anything else draws as cover."
|
|
171
|
+
},
|
|
172
|
+
"position": {
|
|
173
|
+
"type": "string",
|
|
174
|
+
"description": "One or two of center/top/bottom/left/right or percentages."
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
"columns": {
|
|
179
|
+
"type": "object",
|
|
180
|
+
"description": "Item columns at 390 (base), 768 (md) and 1280 (lg) px; a missing width follows the narrower one.",
|
|
181
|
+
"fields": {
|
|
182
|
+
"390": {
|
|
183
|
+
"type": "integer",
|
|
184
|
+
"min": 1,
|
|
185
|
+
"max": 6,
|
|
186
|
+
"description": "Phone."
|
|
187
|
+
},
|
|
188
|
+
"768": {
|
|
189
|
+
"type": "integer",
|
|
190
|
+
"min": 1,
|
|
191
|
+
"max": 6,
|
|
192
|
+
"description": "Tablet."
|
|
193
|
+
},
|
|
194
|
+
"1280": {
|
|
195
|
+
"type": "integer",
|
|
196
|
+
"min": 1,
|
|
197
|
+
"max": 6,
|
|
198
|
+
"description": "Desktop."
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
"gap": {
|
|
203
|
+
"type": "integer",
|
|
204
|
+
"min": 0,
|
|
205
|
+
"max": 200,
|
|
206
|
+
"description": "Gap between items."
|
|
207
|
+
},
|
|
208
|
+
"tone": {
|
|
209
|
+
"type": "select",
|
|
210
|
+
"options": [
|
|
211
|
+
"light",
|
|
212
|
+
"dark"
|
|
213
|
+
],
|
|
214
|
+
"description": "What the text is set for: dark = light text on a dark band. Without it, a fill's own lightness decides."
|
|
215
|
+
},
|
|
216
|
+
"align": {
|
|
217
|
+
"type": "select",
|
|
218
|
+
"options": [
|
|
219
|
+
"start",
|
|
220
|
+
"center",
|
|
221
|
+
"end"
|
|
222
|
+
],
|
|
223
|
+
"description": "Where the heading and lead sit; read by a component whose own align is not set."
|
|
224
|
+
},
|
|
225
|
+
"radius": {
|
|
226
|
+
"type": "integer",
|
|
227
|
+
"min": 0,
|
|
228
|
+
"max": 200,
|
|
229
|
+
"description": "Card and item corner radius."
|
|
230
|
+
},
|
|
231
|
+
"minHeight": {
|
|
232
|
+
"type": "integer",
|
|
233
|
+
"min": 0,
|
|
234
|
+
"max": 2000,
|
|
235
|
+
"description": "The section's minimum height."
|
|
236
|
+
}
|
|
237
|
+
}
|
|
85
238
|
}
|
|
86
239
|
},
|
|
87
240
|
"slots": {},
|
|
@@ -98,7 +251,7 @@
|
|
|
98
251
|
"divi/et_pb_blurb"
|
|
99
252
|
]
|
|
100
253
|
},
|
|
101
|
-
"a11y": "Steps are an <ol>, so their order and count are announced; the visible numbers are CSS counters, not text. Each title is an <h3> under the section's <h2>, or an <h2> when the section has no heading. The section is labelled by its heading when it has one.",
|
|
254
|
+
"a11y": "Steps are an <ol>, so their order and count are announced; the visible numbers are CSS counters, not text. Each title is an <h3> under the section's <h2>, or an <h2> when the section has no heading. The section is labelled by its heading when it has one. A timeline is the same <ol>; a date is a <time datetime> only when machine-readable; the rail and dots are CSS and not in the accessibility tree.",
|
|
102
255
|
"pattern": {
|
|
103
256
|
"summary": "ordered sequence: numbered title + short text units",
|
|
104
257
|
"signals": [
|
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
import { tv } from 'tailwind-variants'
|
|
5
5
|
import KitImage from '../_shared/KitImage.astro'
|
|
6
6
|
import Section from '../_shared/Section.astro'
|
|
7
|
+
import { measuredGrid } from '../_shared/measured'
|
|
8
|
+
import type { MeasuredStyle } from '../_shared/types'
|
|
7
9
|
import { imageOf, type ImageSource } from '../_shared/image'
|
|
10
|
+
import { profile } from './team'
|
|
8
11
|
|
|
9
12
|
const team = tv({
|
|
10
13
|
slots: {
|
|
@@ -12,6 +15,9 @@ const team = tv({
|
|
|
12
15
|
heading: 'text-3xl [font-weight:var(--font-weight-heading,700)] tracking-tight text-balance md:text-4xl',
|
|
13
16
|
intro: 'text-lg opacity-80',
|
|
14
17
|
list: 'grid gap-6 sm:grid-cols-2 md:gap-8',
|
|
18
|
+
socials: 'relative z-10 mt-1 flex flex-wrap gap-3 text-sm',
|
|
19
|
+
social: 'rounded text-accent-text underline-offset-2 hover:underline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent',
|
|
20
|
+
more: 'text-pretty opacity-80 [&>summary]:text-sm [&>summary]:font-medium [&>summary]:text-accent-text',
|
|
15
21
|
member: 'group relative flex flex-col gap-3',
|
|
16
22
|
portrait: 'overflow-hidden rounded-[var(--radius-image,var(--radius-card))]',
|
|
17
23
|
name: 'text-lg leading-snug [font-weight:var(--font-weight-heading,600)]',
|
|
@@ -23,7 +29,16 @@ const team = tv({
|
|
|
23
29
|
'2': {},
|
|
24
30
|
'3': { list: 'md:grid-cols-3' },
|
|
25
31
|
'4': { list: 'lg:grid-cols-4' },
|
|
32
|
+
'5': { list: 'md:grid-cols-3 lg:grid-cols-5' },
|
|
26
33
|
},
|
|
34
|
+
// How the members are laid out: a grid, one stacked row each (photo beside text), or the first member large over a grid.
|
|
35
|
+
layout: {
|
|
36
|
+
grid: {},
|
|
37
|
+
list: { list: 'grid-cols-1 sm:grid-cols-1', member: 'sm:flex-row sm:items-start sm:gap-6', portrait: 'sm:w-48 sm:shrink-0' },
|
|
38
|
+
featured: { list: '[&>li:first-child]:col-span-full [&>li:first-child]:sm:flex-row [&>li:first-child]:sm:gap-8 [&>li:first-child_.kit-portrait]:sm:w-1/2' },
|
|
39
|
+
},
|
|
40
|
+
imageShape: { square: {}, rounded: { portrait: 'rounded-[var(--radius-image,1rem)]' }, circle: { portrait: 'rounded-full' } },
|
|
41
|
+
align: { start: {}, center: { member: 'items-center text-center', socials: 'justify-center' } },
|
|
27
42
|
style: {
|
|
28
43
|
card: { member: 'rounded-card border border-line p-6', portrait: '-mx-6 -mt-6 mb-2 rounded-b-none' },
|
|
29
44
|
plain: {},
|
|
@@ -31,7 +46,7 @@ const team = tv({
|
|
|
31
46
|
// accent: a small accent line. plain: body-size text in the text colour, as a byline.
|
|
32
47
|
roleStyle: { accent: {}, plain: { role: 'text-[length:var(--text-body,1rem)] font-normal text-current' } },
|
|
33
48
|
},
|
|
34
|
-
defaultVariants: { columns: '3', style: 'card', roleStyle: 'accent' },
|
|
49
|
+
defaultVariants: { columns: '3', layout: 'grid', imageShape: 'square', align: 'start', style: 'card', roleStyle: 'accent' },
|
|
35
50
|
})
|
|
36
51
|
|
|
37
52
|
interface Member {
|
|
@@ -42,44 +57,73 @@ interface Member {
|
|
|
42
57
|
image?: ImageSource | undefined
|
|
43
58
|
imageAlt?: string | undefined
|
|
44
59
|
href?: string | undefined
|
|
60
|
+
/** Profile addresses (the contract's `links[]`); each is labelled by the network its host names, never an invented one. */
|
|
61
|
+
links?: string[] | undefined
|
|
45
62
|
}
|
|
46
63
|
|
|
47
64
|
interface Props {
|
|
48
65
|
heading?: string | undefined
|
|
49
66
|
intro?: string | undefined
|
|
50
67
|
items: Member[]
|
|
51
|
-
columns?: '2' | '3' | '4' | undefined
|
|
68
|
+
columns?: '2' | '3' | '4' | '5' | undefined
|
|
69
|
+
layout?: 'grid' | 'list' | 'featured' | undefined
|
|
70
|
+
imageShape?: 'square' | 'rounded' | 'circle' | undefined
|
|
71
|
+
align?: 'start' | 'center' | undefined
|
|
72
|
+
/** Clamp a long bio to this many lines with the rest in a native `<details>` ("Read more"). */
|
|
73
|
+
bioClamp?: 2 | 3 | 4 | undefined
|
|
74
|
+
readMoreLabel?: string | undefined
|
|
75
|
+
/** The accessible name of a profile link; `{name}` and `{network}` are replaced ("{name} on {network}"). */
|
|
76
|
+
socialLabel?: string | undefined
|
|
52
77
|
style?: 'card' | 'plain' | undefined
|
|
53
78
|
roleStyle?: 'accent' | 'plain' | 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
|
|
54
81
|
id?: string | undefined
|
|
55
82
|
}
|
|
56
83
|
|
|
57
|
-
const { heading, intro, items, columns, style, roleStyle, id } = Astro.props
|
|
58
|
-
const s = team({ columns, style, roleStyle })
|
|
84
|
+
const { heading, intro, items, columns, layout, imageShape, align, bioClamp, readMoreLabel = 'Read more', socialLabel = '{name} on {network}', style, roleStyle, measured, id } = Astro.props
|
|
85
|
+
const s = team({ columns, layout, imageShape, align, style, roleStyle })
|
|
86
|
+
const clamp = { 2: 'line-clamp-2', 3: 'line-clamp-3', 4: 'line-clamp-4' } as const
|
|
87
|
+
// A bio that fits the clamp needs no disclosure: short means about 160 characters per two lines, a rough, build-time guess.
|
|
88
|
+
const longBio = (bio: string) => bioClamp !== undefined && bio.length > bioClamp * 80
|
|
59
89
|
const headingId = heading && id ? `${id}-title` : undefined
|
|
60
90
|
// One level under the section heading, or the section's heading when there is none.
|
|
61
91
|
const NameHeading = heading ? 'h3' : 'h2'
|
|
62
92
|
---
|
|
63
93
|
{items.length > 0 && (
|
|
64
|
-
<Section id={id} aria-labelledby={headingId}>
|
|
94
|
+
<Section measured={measured} id={id} aria-labelledby={headingId}>
|
|
65
95
|
{(heading || intro) && (
|
|
66
96
|
<header class={s.header()}>
|
|
67
97
|
{heading && <h2 id={headingId} class={s.heading()}>{heading}</h2>}
|
|
68
98
|
{intro && <p class={s.intro()} data-kit-text>{intro}</p>}
|
|
69
99
|
</header>
|
|
70
100
|
)}
|
|
71
|
-
<ul class={s.list()}>
|
|
101
|
+
<ul class={s.list({ class: measuredGrid(measured) })}>
|
|
72
102
|
{items.map(member => (
|
|
73
103
|
<li class={s.member()}>
|
|
74
104
|
{/* The portrait's text defaults to the name: a photo of a named person is not decoration. */}
|
|
75
|
-
{imageOf(member.image, member.imageAlt ?? member.name) && <div class={s.portrait()}><KitImage image={imageOf(member.image, member.imageAlt ?? member.name)!} sizes="(min-width: 1024px) 25vw, (min-width: 640px) 50vw, 100vw" class="aspect-square w-full object-cover" /></div>}
|
|
105
|
+
{imageOf(member.image, member.imageAlt ?? member.name) && <div class={`${s.portrait()} kit-portrait`}><KitImage image={imageOf(member.image, member.imageAlt ?? member.name)!} sizes="(min-width: 1024px) 25vw, (min-width: 640px) 50vw, 100vw" class="aspect-square w-full object-cover" /></div>}
|
|
76
106
|
<NameHeading class={s.name()}>
|
|
77
107
|
{member.href
|
|
78
108
|
? <a href={member.href} class="text-current no-underline after:absolute after:inset-0 after:content-[''] hover:text-accent-text">{member.name}</a>
|
|
79
109
|
: member.name}
|
|
80
110
|
</NameHeading>
|
|
81
111
|
{member.role && <p class={s.role()}>{member.role}</p>}
|
|
82
|
-
{member.bio &&
|
|
112
|
+
{member.bio && (longBio(member.bio)
|
|
113
|
+
? (
|
|
114
|
+
<div class="[&:has(details[open])>p]:hidden">
|
|
115
|
+
<p class={`${s.bio()} ${clamp[bioClamp!]}`} data-kit-text>{member.bio}</p>
|
|
116
|
+
<details class={s.more()}><summary>{readMoreLabel}</summary><p class={s.bio()} data-kit-text>{member.bio}</p></details>
|
|
117
|
+
</div>
|
|
118
|
+
)
|
|
119
|
+
: <p class={s.bio()} data-kit-text>{member.bio}</p>)}
|
|
120
|
+
{member.links && member.links.length > 0 && (
|
|
121
|
+
<ul class={s.socials()}>
|
|
122
|
+
{member.links.map(profile).map(link => (
|
|
123
|
+
<li><a href={link.href} class={s.social()} aria-label={socialLabel.replace('{name}', member.name).replace('{network}', link.label)}>{link.label}</a></li>
|
|
124
|
+
))}
|
|
125
|
+
</ul>
|
|
126
|
+
)}
|
|
83
127
|
</li>
|
|
84
128
|
))}
|
|
85
129
|
</ul>
|