@contentrain/astro-kit 0.5.0 → 0.6.1

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 (66) hide show
  1. package/catalog.json +1512 -167
  2. package/components/_shared/KitImage.astro +10 -4
  3. package/components/_shared/image.ts +14 -0
  4. package/components/_shared/types.ts +2 -1
  5. package/components/announcement/Announcement.astro +53 -0
  6. package/components/announcement/fixtures.json +6 -0
  7. package/components/announcement/meta.json +53 -0
  8. package/components/card-grid/CardGrid.astro +5 -3
  9. package/components/card-grid/meta.json +25 -11
  10. package/components/consent/Consent.astro +87 -0
  11. package/components/consent/fixtures.json +4 -0
  12. package/components/consent/meta.json +67 -0
  13. package/components/contact-details/ContactDetails.astro +74 -0
  14. package/components/contact-details/fixtures.json +4 -0
  15. package/components/contact-details/meta.json +105 -0
  16. package/components/contact-form/ContactForm.astro +22 -6
  17. package/components/contact-form/fixtures.json +2 -1
  18. package/components/contact-form/meta.json +7 -6
  19. package/components/cta/meta.json +4 -4
  20. package/components/faq/meta.json +6 -6
  21. package/components/feature-list/FeatureList.astro +85 -0
  22. package/components/feature-list/fixtures.json +129 -0
  23. package/components/feature-list/meta.json +118 -0
  24. package/components/footer/Footer.astro +14 -6
  25. package/components/footer/fixtures.json +3 -1
  26. package/components/footer/meta.json +12 -0
  27. package/components/gallery/Gallery.astro +7 -3
  28. package/components/gallery/meta.json +6 -6
  29. package/components/header/Header.astro +19 -8
  30. package/components/header/fixtures.json +2 -1
  31. package/components/hero/meta.json +30 -8
  32. package/components/logo-cloud/LogoCloud.astro +69 -0
  33. package/components/logo-cloud/fixtures.json +139 -0
  34. package/components/logo-cloud/meta.json +107 -0
  35. package/components/newsletter/meta.json +12 -0
  36. package/components/pricing/Pricing.astro +94 -0
  37. package/components/pricing/fixtures.json +6 -0
  38. package/components/pricing/meta.json +133 -0
  39. package/components/prose/Prose.astro +7 -3
  40. package/components/prose/fixtures.json +2 -1
  41. package/components/prose/meta.json +5 -2
  42. package/components/slider/Slider.astro +15 -3
  43. package/components/slider/meta.json +8 -8
  44. package/components/split/Split.astro +64 -0
  45. package/components/split/fixtures.json +6 -0
  46. package/components/split/meta.json +154 -0
  47. package/components/stats/Stats.astro +66 -0
  48. package/components/stats/fixtures.json +98 -0
  49. package/components/stats/meta.json +113 -0
  50. package/components/steps/Steps.astro +77 -0
  51. package/components/steps/fixtures.json +5 -0
  52. package/components/steps/meta.json +105 -0
  53. package/components/team/Team.astro +82 -0
  54. package/components/team/fixtures.json +5 -0
  55. package/components/team/meta.json +116 -0
  56. package/components/testimonial/Testimonial.astro +5 -3
  57. package/components/testimonial/meta.json +21 -20
  58. package/dist/index.d.mts +115 -2
  59. package/dist/index.d.mts.map +1 -1
  60. package/dist/index.mjs +175 -3
  61. package/dist/index.mjs.map +1 -1
  62. package/mapping/classic.json +51 -6
  63. package/mapping/divi.json +359 -9
  64. package/mapping/elementor.json +581 -8
  65. package/mapping/gutenberg.json +420 -4
  66. package/package.json +2 -2
@@ -0,0 +1,113 @@
1
+ {
2
+ "id": "stats",
3
+ "name": "Stats",
4
+ "description": "A row of figures with a short label each (years in business, clients, uptime), with an optional heading and intro. The counter/number section of a landing page; numbers are static text, not animated.",
5
+ "category": "section",
6
+ "uses": [],
7
+ "shared": [
8
+ "Section.astro"
9
+ ],
10
+ "variants": {
11
+ "columns": {
12
+ "options": [
13
+ "2",
14
+ "3",
15
+ "4"
16
+ ],
17
+ "default": "3",
18
+ "description": "Figures per row on wide screens; two on phones."
19
+ },
20
+ "style": {
21
+ "options": [
22
+ "plain",
23
+ "card"
24
+ ],
25
+ "default": "plain",
26
+ "description": "plain: figures on the section background. card: each figure in its own box."
27
+ },
28
+ "tone": {
29
+ "options": [
30
+ "default",
31
+ "muted",
32
+ "inverse",
33
+ "accent"
34
+ ],
35
+ "default": "default",
36
+ "description": "Section background."
37
+ }
38
+ },
39
+ "props": {
40
+ "heading": {
41
+ "type": "string",
42
+ "label": "Heading",
43
+ "description": "Section heading."
44
+ },
45
+ "intro": {
46
+ "type": "text",
47
+ "label": "Introduction",
48
+ "description": "A sentence under the heading."
49
+ },
50
+ "items": {
51
+ "type": "array",
52
+ "label": "Figures",
53
+ "required": true,
54
+ "items": {
55
+ "type": "object",
56
+ "fields": {
57
+ "value": {
58
+ "type": "string",
59
+ "label": "Figure",
60
+ "required": true,
61
+ "description": "The number as shown, without prefix or suffix (`120`, `4.9`, `24/7`)."
62
+ },
63
+ "label": {
64
+ "type": "string",
65
+ "label": "Label",
66
+ "required": true,
67
+ "description": "What the figure counts (Clients, Countries)."
68
+ },
69
+ "prefix": {
70
+ "type": "string",
71
+ "label": "Prefix",
72
+ "description": "Printed before the figure (`$`, `~`)."
73
+ },
74
+ "suffix": {
75
+ "type": "string",
76
+ "label": "Suffix",
77
+ "description": "Printed after the figure (`+`, `%`, `k`)."
78
+ }
79
+ }
80
+ },
81
+ "description": "The figures, in order (Elementor counter, Divi number counter, a Gutenberg columns row of big numbers)."
82
+ },
83
+ "id": {
84
+ "type": "string",
85
+ "content": false,
86
+ "description": "Anchor id of the section; the heading gets `<id>-title`."
87
+ }
88
+ },
89
+ "slots": {},
90
+ "js": "none",
91
+ "dependencies": {},
92
+ "sources": {
93
+ "gutenberg": [
94
+ "core/columns"
95
+ ],
96
+ "elementor": [
97
+ "elementor/counter"
98
+ ],
99
+ "divi": [
100
+ "divi/et_pb_number_counter",
101
+ "divi/et_pb_circle_counter"
102
+ ]
103
+ },
104
+ "a11y": "A description list: each label is the term, each figure its value, so a screen reader reads them as pairs. The section is labelled by its heading.",
105
+ "pattern": {
106
+ "summary": "row of big numbers with short labels",
107
+ "signals": [
108
+ "2–4 columns",
109
+ "repeated number + label",
110
+ "no images"
111
+ ]
112
+ }
113
+ }
@@ -0,0 +1,77 @@
1
+ ---
2
+ // How it works: an ordered sequence of steps, each a title and a line of
3
+ // text, optionally with an image — as a vertical list or a row across the
4
+ // page, numbered or plain.
5
+ import { tv } from 'tailwind-variants'
6
+ import KitImage from '../_shared/KitImage.astro'
7
+ import Section from '../_shared/Section.astro'
8
+ import { imageOf, type ImageSource } from '../_shared/image'
9
+
10
+ const steps = tv({
11
+ slots: {
12
+ header: 'mb-10 flex max-w-2xl flex-col gap-3',
13
+ heading: 'text-3xl [font-weight:var(--font-weight-heading,700)] tracking-tight text-balance md:text-4xl',
14
+ intro: 'text-lg opacity-80',
15
+ list: 'grid gap-8 [counter-reset:kit-step]',
16
+ step: 'flex gap-4 [counter-increment:kit-step]',
17
+ body: 'flex flex-col gap-2',
18
+ media: 'mb-2 overflow-hidden rounded-[var(--radius-image,var(--radius-card))]',
19
+ title: 'text-lg leading-snug [font-weight:var(--font-weight-heading,600)] text-balance',
20
+ text: 'text-pretty opacity-80',
21
+ },
22
+ variants: {
23
+ layout: {
24
+ list: { list: 'max-w-3xl' },
25
+ row: { list: 'sm:grid-cols-2 lg:auto-cols-fr lg:grid-flow-col lg:grid-cols-none', step: 'flex-col' },
26
+ },
27
+ // The number is drawn from a CSS counter, so the list stays one <ol> a screen reader counts itself.
28
+ style: {
29
+ numbered: { step: "before:flex before:size-10 before:shrink-0 before:items-center before:justify-center before:rounded-full before:bg-accent before:font-semibold before:text-accent-ink before:content-[counter(kit-step)]" },
30
+ plain: { step: 'border-l-2 border-accent pl-4' },
31
+ },
32
+ },
33
+ defaultVariants: { layout: 'list', style: 'numbered' },
34
+ })
35
+
36
+ interface Step {
37
+ title: string
38
+ text: string
39
+ /** The image's address. */
40
+ image?: ImageSource | undefined
41
+ imageAlt?: string | undefined
42
+ }
43
+
44
+ interface Props {
45
+ heading?: string | undefined
46
+ intro?: string | undefined
47
+ items: Step[]
48
+ layout?: 'list' | 'row' | undefined
49
+ style?: 'numbered' | 'plain' | undefined
50
+ id?: string | undefined
51
+ }
52
+
53
+ const { heading, intro, items, layout, style, id } = Astro.props
54
+ const s = steps({ layout, style })
55
+ const headingId = heading && id ? `${id}-title` : undefined
56
+ ---
57
+ {items.length > 0 && (
58
+ <Section id={id} aria-labelledby={headingId}>
59
+ {(heading || intro) && (
60
+ <header class={s.header()}>
61
+ {heading && <h2 id={headingId} class={s.heading()}>{heading}</h2>}
62
+ {intro && <p class={s.intro()} data-kit-text>{intro}</p>}
63
+ </header>
64
+ )}
65
+ <ol class={s.list()}>
66
+ {items.map(step => (
67
+ <li class={s.step()}>
68
+ <div class={s.body()}>
69
+ {imageOf(step.image, step.imageAlt) && <div class={s.media()}><KitImage image={imageOf(step.image, step.imageAlt)!} sizes="(min-width: 1024px) 25vw, (min-width: 640px) 50vw, 100vw" class="aspect-[3/2] w-full object-cover" /></div>}
70
+ <h3 class={s.title()}>{step.title}</h3>
71
+ <p class={s.text()} data-kit-text>{step.text}</p>
72
+ </div>
73
+ </li>
74
+ ))}
75
+ </ol>
76
+ </Section>
77
+ )}
@@ -0,0 +1,5 @@
1
+ [
2
+ {"name": "list-numbered", "props": {"id": "how", "heading": "How it works", "intro": "Three steps from WordPress to a static site.", "items": [{"title": "Connect", "text": "Point us at your site; we read everything it publishes."}, {"title": "Review", "text": "Check the plan: every page, model and redirect."}, {"title": "Launch", "text": "Deploy the new site; every old address still works."}]}},
3
+ {"name": "row-plain-images", "props": {"layout": "row", "style": "plain", "heading": "Our process", "items": [{"title": "Discover", "text": "We learn the site and its readers.", "image": "/kit-fixtures/photo-1.svg"}, {"title": "Design", "text": "We draw the pages and the system.", "image": "/kit-fixtures/photo-2.svg"}, {"title": "Build", "text": "We ship it, fast and accessible.", "image": "/kit-fixtures/photo-3.svg", "imageAlt": "A laptop showing the finished site"}]}},
4
+ {"name": "row-numbered", "props": {"layout": "row", "items": [{"title": "Sign up", "text": "One minute, no card."}, {"title": "Import", "text": "Bring your content."}, {"title": "Edit", "text": "Change it in Studio."}, {"title": "Publish", "text": "Push and it is live."}]}}
5
+ ]
@@ -0,0 +1,105 @@
1
+ {
2
+ "id": "steps",
3
+ "name": "Steps",
4
+ "description": "How it works: an ordered sequence of steps, each a title and a line of text with an optional image, as a vertical list or a row, numbered or plain.",
5
+ "category": "section",
6
+ "uses": [],
7
+ "shared": [
8
+ "KitImage.astro",
9
+ "Section.astro",
10
+ "image.ts",
11
+ "types.ts"
12
+ ],
13
+ "variants": {
14
+ "layout": {
15
+ "options": [
16
+ "list",
17
+ "row"
18
+ ],
19
+ "default": "list",
20
+ "description": "A vertical list, or one row across the page on wide screens."
21
+ },
22
+ "style": {
23
+ "options": [
24
+ "numbered",
25
+ "plain"
26
+ ],
27
+ "default": "numbered",
28
+ "description": "A number badge before each step, or an accent rule beside it."
29
+ }
30
+ },
31
+ "props": {
32
+ "heading": {
33
+ "type": "string",
34
+ "label": "Heading",
35
+ "description": "Section heading above the steps."
36
+ },
37
+ "intro": {
38
+ "type": "text",
39
+ "label": "Intro",
40
+ "description": "A line or two under the heading."
41
+ },
42
+ "items": {
43
+ "type": "array",
44
+ "label": "Steps",
45
+ "required": true,
46
+ "items": {
47
+ "type": "object",
48
+ "fields": {
49
+ "title": {
50
+ "type": "string",
51
+ "label": "Title",
52
+ "required": true,
53
+ "description": "The step's name."
54
+ },
55
+ "text": {
56
+ "type": "text",
57
+ "label": "Text",
58
+ "required": true,
59
+ "description": "What happens in this step."
60
+ },
61
+ "image": {
62
+ "type": "image",
63
+ "label": "Image",
64
+ "description": "An image for the step."
65
+ },
66
+ "imageAlt": {
67
+ "type": "string",
68
+ "label": "Image description",
69
+ "description": "What the image shows; empty if decorative."
70
+ }
71
+ }
72
+ },
73
+ "description": "The steps, in order (Elementor icon boxes, Divi blurbs, Gutenberg columns of heading + text)."
74
+ },
75
+ "id": {
76
+ "type": "string",
77
+ "content": false,
78
+ "description": "Anchor id of the section; the heading gets `<id>-title`."
79
+ }
80
+ },
81
+ "slots": {},
82
+ "js": "none",
83
+ "dependencies": {},
84
+ "sources": {
85
+ "gutenberg": [
86
+ "core/columns"
87
+ ],
88
+ "elementor": [
89
+ "elementor/icon-box"
90
+ ],
91
+ "divi": [
92
+ "divi/et_pb_blurb"
93
+ ]
94
+ },
95
+ "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>. The section is labelled by its heading when it has one.",
96
+ "pattern": {
97
+ "summary": "ordered sequence: numbered title + short text units",
98
+ "signals": [
99
+ "3+ repeated units",
100
+ "number or step label",
101
+ "title",
102
+ "short text"
103
+ ]
104
+ }
105
+ }
@@ -0,0 +1,82 @@
1
+ ---
2
+ // The people behind the site: a portrait, a name, a role and a short bio for
3
+ // each, as a grid of cards or plain entries. A member with a page links to it.
4
+ import { tv } from 'tailwind-variants'
5
+ import KitImage from '../_shared/KitImage.astro'
6
+ import Section from '../_shared/Section.astro'
7
+ import { imageOf, type ImageSource } from '../_shared/image'
8
+
9
+ const team = tv({
10
+ slots: {
11
+ header: 'mb-10 flex max-w-2xl flex-col gap-3',
12
+ heading: 'text-3xl [font-weight:var(--font-weight-heading,700)] tracking-tight text-balance md:text-4xl',
13
+ intro: 'text-lg opacity-80',
14
+ list: 'grid gap-6 sm:grid-cols-2 md:gap-8',
15
+ member: 'group relative flex flex-col gap-3',
16
+ portrait: 'overflow-hidden rounded-[var(--radius-image,var(--radius-card))]',
17
+ name: 'text-lg leading-snug [font-weight:var(--font-weight-heading,600)]',
18
+ role: 'text-sm font-medium text-accent',
19
+ bio: 'text-pretty opacity-80',
20
+ },
21
+ variants: {
22
+ columns: {
23
+ '2': {},
24
+ '3': { list: 'md:grid-cols-3' },
25
+ '4': { list: 'lg:grid-cols-4' },
26
+ },
27
+ style: {
28
+ card: { member: 'rounded-card border border-line p-6', portrait: '-mx-6 -mt-6 mb-2 rounded-b-none' },
29
+ plain: {},
30
+ },
31
+ },
32
+ defaultVariants: { columns: '3', style: 'card' },
33
+ })
34
+
35
+ interface Member {
36
+ name: string
37
+ role?: string | undefined
38
+ bio?: string | undefined
39
+ /** The portrait's address. */
40
+ image?: ImageSource | undefined
41
+ imageAlt?: string | undefined
42
+ href?: string | undefined
43
+ }
44
+
45
+ interface Props {
46
+ heading?: string | undefined
47
+ intro?: string | undefined
48
+ items: Member[]
49
+ columns?: '2' | '3' | '4' | undefined
50
+ style?: 'card' | 'plain' | undefined
51
+ id?: string | undefined
52
+ }
53
+
54
+ const { heading, intro, items, columns, style, id } = Astro.props
55
+ const s = team({ columns, style })
56
+ const headingId = heading && id ? `${id}-title` : undefined
57
+ ---
58
+ {items.length > 0 && (
59
+ <Section id={id} aria-labelledby={headingId}>
60
+ {(heading || intro) && (
61
+ <header class={s.header()}>
62
+ {heading && <h2 id={headingId} class={s.heading()}>{heading}</h2>}
63
+ {intro && <p class={s.intro()} data-kit-text>{intro}</p>}
64
+ </header>
65
+ )}
66
+ <ul class={s.list()}>
67
+ {items.map(member => (
68
+ <li class={s.member()}>
69
+ {/* The portrait's text defaults to the name: a photo of a named person is not decoration. */}
70
+ {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>}
71
+ <h3 class={s.name()}>
72
+ {member.href
73
+ ? <a href={member.href} class="text-current no-underline after:absolute after:inset-0 after:content-[''] hover:text-accent">{member.name}</a>
74
+ : member.name}
75
+ </h3>
76
+ {member.role && <p class={s.role()}>{member.role}</p>}
77
+ {member.bio && <p class={s.bio()} data-kit-text>{member.bio}</p>}
78
+ </li>
79
+ ))}
80
+ </ul>
81
+ </Section>
82
+ )}
@@ -0,0 +1,5 @@
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
+ ]
@@ -0,0 +1,116 @@
1
+ {
2
+ "id": "team",
3
+ "name": "Team",
4
+ "description": "The people behind the site: portrait, name, role and a short bio each, as cards or plain entries. A member with a page is clickable as a whole.",
5
+ "category": "section",
6
+ "uses": [],
7
+ "shared": [
8
+ "KitImage.astro",
9
+ "Section.astro",
10
+ "image.ts",
11
+ "types.ts"
12
+ ],
13
+ "variants": {
14
+ "columns": {
15
+ "options": [
16
+ "2",
17
+ "3",
18
+ "4"
19
+ ],
20
+ "default": "3",
21
+ "description": "Columns on wide screens; one on phones, two on tablets."
22
+ },
23
+ "style": {
24
+ "options": [
25
+ "card",
26
+ "plain"
27
+ ],
28
+ "default": "card",
29
+ "description": "Member chrome: a bordered card with the portrait on top, or none."
30
+ }
31
+ },
32
+ "props": {
33
+ "heading": {
34
+ "type": "string",
35
+ "label": "Heading",
36
+ "description": "Section heading above the team."
37
+ },
38
+ "intro": {
39
+ "type": "text",
40
+ "label": "Intro",
41
+ "description": "A line or two under the heading."
42
+ },
43
+ "items": {
44
+ "type": "array",
45
+ "label": "Members",
46
+ "required": true,
47
+ "items": {
48
+ "type": "object",
49
+ "fields": {
50
+ "name": {
51
+ "type": "string",
52
+ "label": "Name",
53
+ "required": true,
54
+ "description": "The person's name."
55
+ },
56
+ "role": {
57
+ "type": "string",
58
+ "label": "Role",
59
+ "description": "Their title or role."
60
+ },
61
+ "bio": {
62
+ "type": "text",
63
+ "label": "Bio",
64
+ "description": "A short biography."
65
+ },
66
+ "image": {
67
+ "type": "image",
68
+ "label": "Photo",
69
+ "description": "Their portrait; shown square."
70
+ },
71
+ "imageAlt": {
72
+ "type": "string",
73
+ "label": "Photo description",
74
+ "description": "Who the photo shows; defaults to their name."
75
+ },
76
+ "href": {
77
+ "type": "url",
78
+ "label": "Link",
79
+ "description": "Their profile page; makes the whole entry a link."
80
+ }
81
+ }
82
+ },
83
+ "description": "The people, in display order (Divi team member name/position/content/image_url, Elementor image box)."
84
+ },
85
+ "id": {
86
+ "type": "string",
87
+ "content": false,
88
+ "description": "Anchor id of the section; the heading gets `<id>-title`."
89
+ }
90
+ },
91
+ "slots": {},
92
+ "js": "none",
93
+ "dependencies": {},
94
+ "sources": {
95
+ "gutenberg": [
96
+ "core/columns"
97
+ ],
98
+ "elementor": [
99
+ "elementor/image-box"
100
+ ],
101
+ "divi": [
102
+ "divi/et_pb_team_member"
103
+ ]
104
+ },
105
+ "a11y": "Members are a <ul>; each name is an <h3>, and a linked member's link text is the name. A portrait's text defaults to the name. The section is labelled by its heading when it has one.",
106
+ "pattern": {
107
+ "summary": "repeated person units: portrait + name + role",
108
+ "signals": [
109
+ "2+ columns",
110
+ "1 image per column",
111
+ "name",
112
+ "role",
113
+ "short text"
114
+ ]
115
+ }
116
+ }
@@ -5,13 +5,15 @@
5
5
  import { tv } from 'tailwind-variants'
6
6
  import KitImage from '../_shared/KitImage.astro'
7
7
  import Section from '../_shared/Section.astro'
8
- import type { ImageInput } from '../_shared/types'
8
+ import { imageOf, type ImageSource } from '../_shared/image'
9
9
 
10
10
  export interface TestimonialItem {
11
11
  quote: string
12
12
  name: string
13
13
  role?: string | undefined
14
- avatar?: ImageInput | undefined
14
+ /** The portrait's address (or, until 0.7, an `ImageInput`). */
15
+ avatar?: ImageSource | undefined
16
+ avatarAlt?: string | undefined
15
17
  }
16
18
 
17
19
  const testimonial = tv({
@@ -57,7 +59,7 @@ const headingId = heading && id ? `${id}-title` : undefined
57
59
  <figure class={s.card()}>
58
60
  <blockquote class={s.quote()}><p>“{item.quote}”</p></blockquote>
59
61
  <figcaption class={s.person()}>
60
- {item.avatar && <KitImage image={item.avatar} sizes="48px" class="size-12 shrink-0 rounded-full object-cover" />}
62
+ {imageOf(item.avatar, item.avatarAlt) && <KitImage image={imageOf(item.avatar, item.avatarAlt)!} sizes="48px" class="size-12 shrink-0 rounded-full object-cover" />}
61
63
  <span class="flex flex-col text-left">
62
64
  <span class={s.name()}>{item.name}</span>
63
65
  {item.role && <span class={s.role()}>{item.role}</span>}
@@ -7,6 +7,7 @@
7
7
  "shared": [
8
8
  "KitImage.astro",
9
9
  "Section.astro",
10
+ "image.ts",
10
11
  "types.ts"
11
12
  ],
12
13
  "variants": {
@@ -32,39 +33,37 @@
32
33
  "props": {
33
34
  "items": {
34
35
  "type": "array",
36
+ "label": "Testimonials",
35
37
  "required": true,
36
38
  "items": {
37
39
  "type": "object",
38
40
  "fields": {
39
41
  "quote": {
40
42
  "type": "text",
41
- "required": true
43
+ "label": "Quote",
44
+ "required": true,
45
+ "description": "What the person said, in their words."
42
46
  },
43
47
  "name": {
44
48
  "type": "string",
45
- "required": true
49
+ "label": "Name",
50
+ "required": true,
51
+ "description": "Who said it."
46
52
  },
47
53
  "role": {
48
- "type": "string"
54
+ "type": "string",
55
+ "label": "Role",
56
+ "description": "Their role or company."
49
57
  },
50
58
  "avatar": {
51
- "type": "object",
52
- "fields": {
53
- "src": {
54
- "type": "image",
55
- "required": true
56
- },
57
- "alt": {
58
- "type": "string",
59
- "required": true
60
- },
61
- "width": {
62
- "type": "integer"
63
- },
64
- "height": {
65
- "type": "integer"
66
- }
67
- }
59
+ "type": "image",
60
+ "label": "Photo",
61
+ "description": "Their photo."
62
+ },
63
+ "avatarAlt": {
64
+ "type": "string",
65
+ "label": "Photo description",
66
+ "description": "Who the photo shows; usually their name."
68
67
  }
69
68
  }
70
69
  },
@@ -72,10 +71,12 @@
72
71
  },
73
72
  "heading": {
74
73
  "type": "string",
74
+ "label": "Heading",
75
75
  "description": "Section heading above the quotes."
76
76
  },
77
77
  "id": {
78
78
  "type": "string",
79
+ "content": false,
79
80
  "description": "Anchor id of the section; the heading gets `<id>-title`."
80
81
  }
81
82
  },