@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.
Files changed (67) hide show
  1. package/README.md +25 -2
  2. package/catalog.json +3305 -83
  3. package/components/_shared/Section.astro +18 -3
  4. package/components/_shared/measured.ts +152 -0
  5. package/components/_shared/types.ts +40 -0
  6. package/components/band/Band.astro +132 -0
  7. package/components/band/fixtures.json +8 -0
  8. package/components/band/meta.json +323 -0
  9. package/components/card-grid/CardGrid.astro +7 -3
  10. package/components/card-grid/fixtures.json +2 -1
  11. package/components/card-grid/meta.json +111 -0
  12. package/components/contact-details/ContactDetails.astro +7 -2
  13. package/components/contact-details/meta.json +113 -1
  14. package/components/contact-form/ContactForm.astro +48 -12
  15. package/components/contact-form/fixtures.json +169 -9
  16. package/components/contact-form/meta.json +283 -15
  17. package/components/cta/Cta.astro +52 -12
  18. package/components/cta/fixtures.json +5 -1
  19. package/components/cta/meta.json +11 -2
  20. package/components/faq/Faq.astro +60 -17
  21. package/components/faq/fixtures.json +75 -0
  22. package/components/faq/meta.json +8 -2
  23. package/components/feature-list/FeatureList.astro +34 -6
  24. package/components/feature-list/fixtures.json +2 -1
  25. package/components/feature-list/meta.json +125 -1
  26. package/components/gallery/Gallery.astro +47 -13
  27. package/components/gallery/fixtures.json +275 -0
  28. package/components/gallery/meta.json +266 -14
  29. package/components/hero/Hero.astro +14 -8
  30. package/components/hero/fixtures.json +2 -1
  31. package/components/hero/meta.json +111 -0
  32. package/components/logo-cloud/LogoCloud.astro +85 -21
  33. package/components/logo-cloud/fixtures.json +239 -0
  34. package/components/logo-cloud/meta.json +145 -5
  35. package/components/newsletter/Newsletter.astro +5 -1
  36. package/components/newsletter/meta.json +113 -1
  37. package/components/post-card/PostCard.astro +4 -2
  38. package/components/post-card/meta.json +2 -1
  39. package/components/post-list/PostList.astro +114 -0
  40. package/components/post-list/fixtures.json +387 -0
  41. package/components/post-list/meta.json +308 -0
  42. package/components/pricing/Pricing.astro +128 -24
  43. package/components/pricing/fixtures.json +288 -4
  44. package/components/pricing/meta.json +188 -1
  45. package/components/split/Split.astro +76 -14
  46. package/components/split/fixtures.json +8 -1
  47. package/components/split/meta.json +226 -4
  48. package/components/stats/Stats.astro +42 -10
  49. package/components/stats/fixtures.json +173 -0
  50. package/components/stats/meta.json +153 -5
  51. package/components/steps/Steps.astro +36 -8
  52. package/components/steps/fixtures.json +217 -5
  53. package/components/steps/meta.json +157 -3
  54. package/components/tabs/Tabs.astro +5 -2
  55. package/components/tabs/meta.json +2 -1
  56. package/components/team/Team.astro +52 -8
  57. package/components/team/fixtures.json +217 -4
  58. package/components/team/meta.json +168 -3
  59. package/components/team/team.ts +9 -0
  60. package/components/testimonial/Testimonial.astro +71 -21
  61. package/components/testimonial/fixtures.json +73 -0
  62. package/components/testimonial/meta.json +167 -3
  63. package/mapping/classic.json +7 -1
  64. package/mapping/divi.json +21 -4
  65. package/mapping/elementor.json +33 -8
  66. package/mapping/gutenberg.json +20 -4
  67. package/package.json +2 -2
@@ -8,16 +8,19 @@
8
8
  "KitImage.astro",
9
9
  "Section.astro",
10
10
  "image.ts",
11
+ "measured.ts",
11
12
  "types.ts"
12
13
  ],
13
14
  "variants": {
14
15
  "layout": {
15
16
  "options": [
16
17
  "list",
17
- "row"
18
+ "row",
19
+ "rail",
20
+ "alternate"
18
21
  ],
19
22
  "default": "list",
20
- "description": "A vertical list, or one row across the page on wide screens."
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."
21
24
  },
22
25
  "style": {
23
26
  "options": [
@@ -26,6 +29,31 @@
26
29
  ],
27
30
  "default": "numbered",
28
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."
29
57
  }
30
58
  },
31
59
  "props": {
@@ -72,6 +100,16 @@
72
100
  "type": "string",
73
101
  "label": "Image description",
74
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\")."
75
113
  }
76
114
  }
77
115
  },
@@ -81,6 +119,122 @@
81
119
  "type": "string",
82
120
  "content": false,
83
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
+ }
84
238
  }
85
239
  },
86
240
  "slots": {},
@@ -97,7 +251,7 @@
97
251
  "divi/et_pb_blurb"
98
252
  ]
99
253
  },
100
- "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.",
101
255
  "pattern": {
102
256
  "summary": "ordered sequence: numbered title + short text units",
103
257
  "signals": [
@@ -5,6 +5,7 @@
5
5
  // the same markup into tabs.
6
6
  import { tv } from 'tailwind-variants'
7
7
  import Section from '../_shared/Section.astro'
8
+ import type { MeasuredStyle } from '../_shared/types'
8
9
 
9
10
  export interface TabItem {
10
11
  label: string
@@ -47,9 +48,11 @@ interface Props {
47
48
  orientation?: 'horizontal' | 'vertical' | undefined
48
49
  tone?: 'default' | 'muted' | 'inverse' | 'accent' | undefined
49
50
  id?: string | undefined
51
+ /** 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. */
52
+ measured?: MeasuredStyle | undefined
50
53
  }
51
54
 
52
- const { items, heading, label, style, orientation = 'horizontal', tone, id = 'tabs' } = Astro.props
55
+ const { items, heading, label, style, orientation = 'horizontal', tone, id = 'tabs', measured } = Astro.props
53
56
  const s = tabs({ style, orientation, tone })
54
57
  const headingId = heading ? `${id}-title` : undefined
55
58
  // One level under the section heading, or the section's heading when there is none.
@@ -57,7 +60,7 @@ const PanelHeading = heading ? 'h3' : 'h2'
57
60
  const value = (i: number) => `tab-${i + 1}`
58
61
  ---
59
62
  {items.length > 0 && (
60
- <Section tone={tone} spacing="md" id={id} aria-labelledby={headingId}>
63
+ <Section measured={measured} tone={tone} spacing="md" id={id} aria-labelledby={headingId}>
61
64
  {heading && <h2 id={headingId} class={s.heading()}>{heading}</h2>}
62
65
  <div class={s.root()} data-kit-tabs={id} data-orientation={orientation} data-label={label ?? heading ?? undefined}>
63
66
  <div class={s.list()} data-part="list">
@@ -4,13 +4,14 @@
4
4
  "description": "Tabbed panels with the WAI-ARIA tabs pattern from Zag.js (arrow keys, Home/End, roving focus). Without script every panel shows in turn under its own title, so no content is ever hidden from a reader or a crawler.",
5
5
  "category": "content",
6
6
  "uses": [],
7
- "shared": ["Section.astro"],
7
+ "shared": ["Section.astro", "measured.ts", "types.ts"],
8
8
  "variants": {
9
9
  "style": { "options": ["underline", "pills"], "default": "underline", "description": "underline: a rule under the list, the selected tab underlined in accent. pills: the selected tab filled with accent." },
10
10
  "orientation": { "options": ["horizontal", "vertical"], "default": "horizontal", "description": "vertical: the tab list beside the panel on wide screens (Elementor vertical tabs)." },
11
11
  "tone": { "options": ["default", "muted", "inverse", "accent"], "default": "default", "description": "Section background." }
12
12
  },
13
13
  "props": {
14
+ "measured": {"type": "object", "content": false, "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.", "fields": {"containerPx": {"type": "integer", "min": 0, "max": 2560, "description": "The frame's content width."}, "padY": {"type": "integer", "min": 0, "max": 400, "description": "Top and bottom padding."}, "padX": {"type": "integer", "min": 0, "max": 200, "description": "The frame's side padding (gutter)."}, "bg": {"type": "object", "description": "The section's background.", "fields": {"color": {"type": "string", "description": "Fill colour: hex, rgb(a) or hsl(a)."}, "image": {"type": "string", "description": "Background image address (http(s) or root-relative), drawn behind the content."}, "overlay": {"type": "string", "description": "Colour or linear/radial gradient laid over the image or the fill."}, "size": {"type": "string", "description": "cover, contain or auto; anything else draws as cover."}, "position": {"type": "string", "description": "One or two of center/top/bottom/left/right or percentages."}}}, "columns": {"type": "object", "description": "Item columns at 390 (base), 768 (md) and 1280 (lg) px; a missing width follows the narrower one.", "fields": {"390": {"type": "integer", "min": 1, "max": 6, "description": "Phone."}, "768": {"type": "integer", "min": 1, "max": 6, "description": "Tablet."}, "1280": {"type": "integer", "min": 1, "max": 6, "description": "Desktop."}}}, "gap": {"type": "integer", "min": 0, "max": 200, "description": "Gap between items."}, "tone": {"type": "select", "options": ["light", "dark"], "description": "What the text is set for: dark = light text on a dark band. Without it, a fill's own lightness decides."}, "align": {"type": "select", "options": ["start", "center", "end"], "description": "Where the heading and lead sit; read by a component whose own align is not set."}, "radius": {"type": "integer", "min": 0, "max": 200, "description": "Card and item corner radius."}, "minHeight": {"type": "integer", "min": 0, "max": 2000, "description": "The section's minimum height."}}},
14
15
  "items": { "type": "array", "required": true, "items": { "type": "object", "fields": { "label": { "type": "string", "required": true }, "content": { "type": "richtext", "required": true } } }, "description": "Tabs in order (Elementor tabs tab_title/tab_content, Divi tab title + content)." },
15
16
  "heading": { "type": "string", "description": "Section heading; also names the tab list." },
16
17
  "label": { "type": "string", "description": "Accessible name of the tab list when there is no heading." },
@@ -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 && <p class={s.bio()} data-kit-text>{member.bio}</p>}
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>
@@ -1,6 +1,219 @@
1
1
  [
2
- {"name": "three-card", "props": {"id": "team", "heading": "The team", "intro": "Small, senior, and in the same room.", "items": [{"name": "Mira Kovač", "role": "Editor", "bio": "Keeps every page honest and every sentence short.", "image": "/kit-fixtures/avatar-1.svg", "href": "/team/mira/"}, {"name": "Jon Ade", "role": "Engineer", "bio": "Builds the sites and keeps them fast.", "image": "/kit-fixtures/avatar-2.svg", "href": "/team/jon/"}, {"name": "Lea Park", "role": "Designer", "bio": "Draws the systems the sites are made of.", "image": "/kit-fixtures/avatar-3.svg"}]}},
3
- {"name": "two-plain", "props": {"columns": "2", "style": "plain", "heading": "Founders", "items": [{"name": "Mira Kovač", "role": "Co-founder", "image": "/kit-fixtures/avatar-1.svg", "imageAlt": "Mira Kovač, smiling"}, {"name": "Jon Ade", "role": "Co-founder", "image": "/kit-fixtures/avatar-2.svg"}]}},
4
- {"name": "four-card-no-photos", "props": {"columns": "4", "items": [{"name": "Mira Kovač", "role": "Editor"}, {"name": "Jon Ade", "role": "Engineer"}, {"name": "Lea Park", "role": "Designer"}, {"name": "Sam Ortiz", "role": "Producer", "bio": "Keeps the schedule."}]}},
5
- {"name": "three-plain-role", "props": {"heading": "Our team", "style": "plain", "roleStyle": "plain", "items": [{"name": "Mira Kovač", "role": "Editor"}, {"name": "Jon Ade", "role": "Photographer"}, {"name": "Lea Park", "role": "Route planner"}]}}
2
+ {
3
+ "name": "three-card",
4
+ "props": {
5
+ "id": "team",
6
+ "heading": "The team",
7
+ "intro": "Small, senior, and in the same room.",
8
+ "items": [
9
+ {
10
+ "name": "Mira Kovač",
11
+ "role": "Editor",
12
+ "bio": "Keeps every page honest and every sentence short.",
13
+ "image": "/kit-fixtures/avatar-1.svg",
14
+ "href": "/team/mira/"
15
+ },
16
+ {
17
+ "name": "Jon Ade",
18
+ "role": "Engineer",
19
+ "bio": "Builds the sites and keeps them fast.",
20
+ "image": "/kit-fixtures/avatar-2.svg",
21
+ "href": "/team/jon/"
22
+ },
23
+ {
24
+ "name": "Lea Park",
25
+ "role": "Designer",
26
+ "bio": "Draws the systems the sites are made of.",
27
+ "image": "/kit-fixtures/avatar-3.svg"
28
+ }
29
+ ]
30
+ }
31
+ },
32
+ {
33
+ "name": "two-plain",
34
+ "props": {
35
+ "columns": "2",
36
+ "style": "plain",
37
+ "heading": "Founders",
38
+ "items": [
39
+ {
40
+ "name": "Mira Kovač",
41
+ "role": "Co-founder",
42
+ "image": "/kit-fixtures/avatar-1.svg",
43
+ "imageAlt": "Mira Kovač, smiling"
44
+ },
45
+ {
46
+ "name": "Jon Ade",
47
+ "role": "Co-founder",
48
+ "image": "/kit-fixtures/avatar-2.svg"
49
+ }
50
+ ]
51
+ }
52
+ },
53
+ {
54
+ "name": "four-card-no-photos",
55
+ "props": {
56
+ "columns": "4",
57
+ "items": [
58
+ {
59
+ "name": "Mira Kovač",
60
+ "role": "Editor"
61
+ },
62
+ {
63
+ "name": "Jon Ade",
64
+ "role": "Engineer"
65
+ },
66
+ {
67
+ "name": "Lea Park",
68
+ "role": "Designer"
69
+ },
70
+ {
71
+ "name": "Sam Ortiz",
72
+ "role": "Producer",
73
+ "bio": "Keeps the schedule."
74
+ }
75
+ ]
76
+ }
77
+ },
78
+ {
79
+ "name": "three-plain-role",
80
+ "props": {
81
+ "heading": "Our team",
82
+ "style": "plain",
83
+ "roleStyle": "plain",
84
+ "items": [
85
+ {
86
+ "name": "Mira Kovač",
87
+ "role": "Editor"
88
+ },
89
+ {
90
+ "name": "Jon Ade",
91
+ "role": "Photographer"
92
+ },
93
+ {
94
+ "name": "Lea Park",
95
+ "role": "Route planner"
96
+ }
97
+ ]
98
+ }
99
+ },
100
+ {
101
+ "name": "five-circle",
102
+ "props": {
103
+ "columns": "5",
104
+ "imageShape": "circle",
105
+ "align": "center",
106
+ "style": "plain",
107
+ "items": [
108
+ {
109
+ "name": "Mira Kovač",
110
+ "role": "Editor",
111
+ "bio": "Keeps every page honest and every sentence short.",
112
+ "image": "/kit-fixtures/avatar-1.svg",
113
+ "href": "/team/mira/"
114
+ },
115
+ {
116
+ "name": "Jon Ade",
117
+ "role": "Engineer",
118
+ "bio": "Builds the sites and keeps them fast.",
119
+ "image": "/kit-fixtures/avatar-2.svg",
120
+ "href": "/team/jon/"
121
+ },
122
+ {
123
+ "name": "Lea Park",
124
+ "role": "Designer",
125
+ "bio": "Draws the systems the sites are made of.",
126
+ "image": "/kit-fixtures/avatar-3.svg"
127
+ },
128
+ {
129
+ "name": "Ana Ruiz",
130
+ "role": "Editor",
131
+ "bio": "Keeps every page honest and every sentence short.",
132
+ "image": "/kit-fixtures/avatar-1.svg"
133
+ },
134
+ {
135
+ "name": "Kai Berg",
136
+ "role": "Engineer",
137
+ "bio": "Builds the sites and keeps them fast.",
138
+ "image": "/kit-fixtures/avatar-2.svg"
139
+ }
140
+ ]
141
+ }
142
+ },
143
+ {
144
+ "name": "list-rounded",
145
+ "props": {
146
+ "layout": "list",
147
+ "imageShape": "rounded",
148
+ "items": [
149
+ {
150
+ "name": "Mira Kovač",
151
+ "role": "Editor",
152
+ "bio": "Keeps every page honest and every sentence short.",
153
+ "image": "/kit-fixtures/avatar-1.svg",
154
+ "href": "/team/mira/"
155
+ },
156
+ {
157
+ "name": "Jon Ade",
158
+ "role": "Engineer",
159
+ "bio": "Builds the sites and keeps them fast.",
160
+ "image": "/kit-fixtures/avatar-2.svg",
161
+ "href": "/team/jon/"
162
+ }
163
+ ]
164
+ }
165
+ },
166
+ {
167
+ "name": "featured-socials",
168
+ "props": {
169
+ "layout": "featured",
170
+ "items": [
171
+ {
172
+ "name": "Mira Kovač",
173
+ "role": "Editor",
174
+ "bio": "Keeps every page honest and every sentence short.",
175
+ "image": "/kit-fixtures/avatar-1.svg",
176
+ "href": "/team/mira/",
177
+ "links": [
178
+ "https://www.linkedin.com/in/mira",
179
+ "https://github.com/mira",
180
+ "https://mira.example"
181
+ ]
182
+ },
183
+ {
184
+ "name": "Jon Ade",
185
+ "role": "Engineer",
186
+ "bio": "Builds the sites and keeps them fast.",
187
+ "image": "/kit-fixtures/avatar-2.svg",
188
+ "href": "/team/jon/"
189
+ },
190
+ {
191
+ "name": "Lea Park",
192
+ "role": "Designer",
193
+ "bio": "Draws the systems the sites are made of.",
194
+ "image": "/kit-fixtures/avatar-3.svg"
195
+ }
196
+ ]
197
+ }
198
+ },
199
+ {
200
+ "name": "bio-clamp",
201
+ "props": {
202
+ "bioClamp": 2,
203
+ "items": [
204
+ {
205
+ "name": "Mira Kovač",
206
+ "role": "Editor",
207
+ "bio": "Spent twelve years editing a daily paper before moving to the web, where she keeps every page honest, every sentence short and every headline checked twice against the story under it.",
208
+ "image": "/kit-fixtures/avatar-1.svg"
209
+ },
210
+ {
211
+ "name": "Jon Ade",
212
+ "role": "Engineer",
213
+ "bio": "Builds the sites and keeps them fast.",
214
+ "image": "/kit-fixtures/avatar-2.svg"
215
+ }
216
+ ]
217
+ }
218
+ }
6
219
  ]