@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
|
@@ -29,6 +29,31 @@
|
|
|
29
29
|
],
|
|
30
30
|
"default": "default",
|
|
31
31
|
"description": "Section background."
|
|
32
|
+
},
|
|
33
|
+
"layout": {
|
|
34
|
+
"options": [
|
|
35
|
+
"cards",
|
|
36
|
+
"table"
|
|
37
|
+
],
|
|
38
|
+
"default": "cards",
|
|
39
|
+
"description": "cards: plans side by side. table: one comparison table, plans as columns and features as rows (for more than four plans)."
|
|
40
|
+
},
|
|
41
|
+
"featureStyle": {
|
|
42
|
+
"options": [
|
|
43
|
+
"check",
|
|
44
|
+
"dot",
|
|
45
|
+
"plain"
|
|
46
|
+
],
|
|
47
|
+
"default": "plain",
|
|
48
|
+
"description": "The mark before each feature in the cards: a check, a dot, or the list's own bullets."
|
|
49
|
+
},
|
|
50
|
+
"billingToggle": {
|
|
51
|
+
"options": [
|
|
52
|
+
"none",
|
|
53
|
+
"radio"
|
|
54
|
+
],
|
|
55
|
+
"default": "none",
|
|
56
|
+
"description": "radio: two labelled radios swap each plan's price for its priceAlt (monthly / yearly), with no script. The radio group is named from the section id, or a hash of the plans without one, so two toggles on a page stay independent."
|
|
32
57
|
}
|
|
33
58
|
},
|
|
34
59
|
"props": {
|
|
@@ -90,6 +115,21 @@
|
|
|
90
115
|
"type": "boolean",
|
|
91
116
|
"label": "Featured",
|
|
92
117
|
"description": "Raises the plan and gives it the badge."
|
|
118
|
+
},
|
|
119
|
+
"badge": {
|
|
120
|
+
"type": "string",
|
|
121
|
+
"label": "Badge",
|
|
122
|
+
"description": "A badge text on the plan (\"Best value\"); a featured plan without one reads the section's featuredLabel."
|
|
123
|
+
},
|
|
124
|
+
"priceAlt": {
|
|
125
|
+
"type": "string",
|
|
126
|
+
"label": "Price, other period",
|
|
127
|
+
"description": "The price for the other billing period (\"€150\"), shown when the toggle is on its second option."
|
|
128
|
+
},
|
|
129
|
+
"periodAlt": {
|
|
130
|
+
"type": "string",
|
|
131
|
+
"label": "Period, other period",
|
|
132
|
+
"description": "What the other price is per (\"/year\")."
|
|
93
133
|
}
|
|
94
134
|
}
|
|
95
135
|
},
|
|
@@ -105,6 +145,152 @@
|
|
|
105
145
|
"type": "string",
|
|
106
146
|
"content": false,
|
|
107
147
|
"description": "Anchor id of the section; the heading gets `<id>-title`."
|
|
148
|
+
},
|
|
149
|
+
"compact": {
|
|
150
|
+
"type": "boolean",
|
|
151
|
+
"default": false,
|
|
152
|
+
"content": false,
|
|
153
|
+
"description": "A denser card: smaller padding, gap and price."
|
|
154
|
+
},
|
|
155
|
+
"includedLabel": {
|
|
156
|
+
"type": "string",
|
|
157
|
+
"default": "Included",
|
|
158
|
+
"content": false,
|
|
159
|
+
"description": "Table cell text for readers where a plan has the feature."
|
|
160
|
+
},
|
|
161
|
+
"notIncludedLabel": {
|
|
162
|
+
"type": "string",
|
|
163
|
+
"default": "Not included",
|
|
164
|
+
"content": false,
|
|
165
|
+
"description": "Table cell text for readers where a plan lacks the feature."
|
|
166
|
+
},
|
|
167
|
+
"measured": {
|
|
168
|
+
"type": "object",
|
|
169
|
+
"content": false,
|
|
170
|
+
"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.",
|
|
171
|
+
"fields": {
|
|
172
|
+
"containerPx": {
|
|
173
|
+
"type": "integer",
|
|
174
|
+
"min": 0,
|
|
175
|
+
"max": 2560,
|
|
176
|
+
"description": "The frame's content width."
|
|
177
|
+
},
|
|
178
|
+
"padY": {
|
|
179
|
+
"type": "integer",
|
|
180
|
+
"min": 0,
|
|
181
|
+
"max": 400,
|
|
182
|
+
"description": "Top and bottom padding."
|
|
183
|
+
},
|
|
184
|
+
"padX": {
|
|
185
|
+
"type": "integer",
|
|
186
|
+
"min": 0,
|
|
187
|
+
"max": 200,
|
|
188
|
+
"description": "The frame's side padding (gutter)."
|
|
189
|
+
},
|
|
190
|
+
"bg": {
|
|
191
|
+
"type": "object",
|
|
192
|
+
"description": "The section's background.",
|
|
193
|
+
"fields": {
|
|
194
|
+
"color": {
|
|
195
|
+
"type": "string",
|
|
196
|
+
"description": "Fill colour: hex, rgb(a) or hsl(a)."
|
|
197
|
+
},
|
|
198
|
+
"image": {
|
|
199
|
+
"type": "string",
|
|
200
|
+
"description": "Background image address (http(s) or root-relative), drawn behind the content."
|
|
201
|
+
},
|
|
202
|
+
"overlay": {
|
|
203
|
+
"type": "string",
|
|
204
|
+
"description": "Colour or linear/radial gradient laid over the image or the fill."
|
|
205
|
+
},
|
|
206
|
+
"size": {
|
|
207
|
+
"type": "string",
|
|
208
|
+
"description": "cover, contain or auto; anything else draws as cover."
|
|
209
|
+
},
|
|
210
|
+
"position": {
|
|
211
|
+
"type": "string",
|
|
212
|
+
"description": "One or two of center/top/bottom/left/right or percentages."
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
"columns": {
|
|
217
|
+
"type": "object",
|
|
218
|
+
"description": "Item columns at 390 (base), 768 (md) and 1280 (lg) px; a missing width follows the narrower one.",
|
|
219
|
+
"fields": {
|
|
220
|
+
"390": {
|
|
221
|
+
"type": "integer",
|
|
222
|
+
"min": 1,
|
|
223
|
+
"max": 6,
|
|
224
|
+
"description": "Phone."
|
|
225
|
+
},
|
|
226
|
+
"768": {
|
|
227
|
+
"type": "integer",
|
|
228
|
+
"min": 1,
|
|
229
|
+
"max": 6,
|
|
230
|
+
"description": "Tablet."
|
|
231
|
+
},
|
|
232
|
+
"1280": {
|
|
233
|
+
"type": "integer",
|
|
234
|
+
"min": 1,
|
|
235
|
+
"max": 6,
|
|
236
|
+
"description": "Desktop."
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
"gap": {
|
|
241
|
+
"type": "integer",
|
|
242
|
+
"min": 0,
|
|
243
|
+
"max": 200,
|
|
244
|
+
"description": "Gap between items."
|
|
245
|
+
},
|
|
246
|
+
"tone": {
|
|
247
|
+
"type": "select",
|
|
248
|
+
"options": [
|
|
249
|
+
"light",
|
|
250
|
+
"dark"
|
|
251
|
+
],
|
|
252
|
+
"description": "What the text is set for: dark = light text on a dark band. Without it, a fill's own lightness decides."
|
|
253
|
+
},
|
|
254
|
+
"align": {
|
|
255
|
+
"type": "select",
|
|
256
|
+
"options": [
|
|
257
|
+
"start",
|
|
258
|
+
"center",
|
|
259
|
+
"end"
|
|
260
|
+
],
|
|
261
|
+
"description": "Where the heading and lead sit; read by a component whose own align is not set."
|
|
262
|
+
},
|
|
263
|
+
"radius": {
|
|
264
|
+
"type": "integer",
|
|
265
|
+
"min": 0,
|
|
266
|
+
"max": 200,
|
|
267
|
+
"description": "Card and item corner radius."
|
|
268
|
+
},
|
|
269
|
+
"minHeight": {
|
|
270
|
+
"type": "integer",
|
|
271
|
+
"min": 0,
|
|
272
|
+
"max": 2000,
|
|
273
|
+
"description": "The section's minimum height."
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
},
|
|
277
|
+
"billingMainLabel": {
|
|
278
|
+
"type": "string",
|
|
279
|
+
"default": "Monthly",
|
|
280
|
+
"content": false,
|
|
281
|
+
"description": "First option of the billing toggle."
|
|
282
|
+
},
|
|
283
|
+
"billingAltLabel": {
|
|
284
|
+
"type": "string",
|
|
285
|
+
"default": "Yearly",
|
|
286
|
+
"content": false,
|
|
287
|
+
"description": "Second option of the billing toggle (the plans' priceAlt)."
|
|
288
|
+
},
|
|
289
|
+
"billingLegend": {
|
|
290
|
+
"type": "string",
|
|
291
|
+
"default": "Billing period",
|
|
292
|
+
"content": false,
|
|
293
|
+
"description": "Legend of the toggle, read to screen readers."
|
|
108
294
|
}
|
|
109
295
|
},
|
|
110
296
|
"slots": {},
|
|
@@ -121,7 +307,7 @@
|
|
|
121
307
|
"divi/et_pb_pricing_tables"
|
|
122
308
|
]
|
|
123
309
|
},
|
|
124
|
-
"a11y": "Plans are a <ul>; each name is an <h3> under the section's <h2>, or an <h2> when the section has no heading. A featured plan is marked by a text badge and a ring, not colour alone. Buttons are links with their own label.",
|
|
310
|
+
"a11y": "Plans are a <ul>; each name is an <h3> under the section's <h2>, or an <h2> when the section has no heading. A featured plan is marked by a text badge and a ring, not colour alone. Buttons are links with their own label. The table layout is a real <table> with a caption, column and row headers, and Included / Not included text in every cell; the billing toggle is a fieldset with a legend and labelled radios.",
|
|
125
311
|
"pattern": {
|
|
126
312
|
"summary": "repeated plan units: name + price + feature list + button",
|
|
127
313
|
"signals": [
|
|
@@ -5,59 +5,121 @@
|
|
|
5
5
|
import { tv } from 'tailwind-variants'
|
|
6
6
|
import { renderMarkdown } from '../../../lib/markdown'
|
|
7
7
|
import Button from '../_shared/Button.astro'
|
|
8
|
+
import Embed from '../embed/Embed.astro'
|
|
9
|
+
import FeatureList from '../feature-list/FeatureList.astro'
|
|
8
10
|
import KitImage from '../_shared/KitImage.astro'
|
|
9
11
|
import Section from '../_shared/Section.astro'
|
|
10
|
-
import type { ActionInput, ImageInput } from '../_shared/types'
|
|
12
|
+
import type { ActionInput, ImageInput, MeasuredStyle } from '../_shared/types'
|
|
11
13
|
|
|
12
14
|
const split = tv({
|
|
13
15
|
slots: {
|
|
14
|
-
grid: 'grid items-center gap-10 md:
|
|
16
|
+
grid: 'grid items-center gap-10 md:gap-16',
|
|
15
17
|
copy: 'flex flex-col gap-5',
|
|
16
18
|
eyebrow: 'text-sm font-semibold tracking-wide uppercase opacity-80',
|
|
17
19
|
heading: 'text-3xl [font-weight:var(--font-weight-heading,700)] tracking-tight text-balance md:text-4xl',
|
|
18
20
|
body: 'prose prose-site max-w-none text-pretty',
|
|
19
21
|
actions: 'flex flex-wrap gap-3 pt-2',
|
|
20
|
-
media: 'overflow-hidden
|
|
22
|
+
media: 'overflow-hidden',
|
|
23
|
+
image: 'h-auto w-full object-cover',
|
|
21
24
|
},
|
|
22
25
|
variants: {
|
|
23
26
|
// Source order is text first; `start` moves the image before it on wide screens only.
|
|
24
27
|
imageSide: { start: { media: 'md:order-first' }, end: {} },
|
|
28
|
+
// The text column's share against the image's (the columns follow the sides, see `columns` below).
|
|
29
|
+
ratio: { '1:1': {}, '5:7': {}, '7:5': {} },
|
|
30
|
+
// contain: the image at its own proportions. cover: it fills the column's height.
|
|
31
|
+
imageFit: { contain: {}, cover: { grid: 'items-stretch', media: 'min-h-72 md:h-full', image: 'h-full min-h-72' } },
|
|
32
|
+
imageShape: {
|
|
33
|
+
none: { media: 'rounded-none' },
|
|
34
|
+
rounded: { media: 'rounded-[var(--radius-image,var(--radius-card))]' },
|
|
35
|
+
circle: { media: 'mx-auto aspect-square w-full max-w-lg rounded-full', image: 'h-full' },
|
|
36
|
+
},
|
|
37
|
+
// edge: the image runs on to the viewport's edge on its side (wide screens).
|
|
38
|
+
imageBleed: { none: {}, edge: { media: 'rounded-none' } },
|
|
39
|
+
// Phones show the text first, as the source does, or the picture first when it is the content itself.
|
|
40
|
+
stackOrder: { 'text-first': {}, 'media-first': { media: 'max-md:order-first' } },
|
|
41
|
+
// The media column follows the reader down a long text (wide screens).
|
|
42
|
+
sticky: { true: { media: 'md:sticky md:top-8 md:self-start' }, false: {} },
|
|
25
43
|
align: { start: {}, center: { copy: 'items-center text-center', actions: 'justify-center' } },
|
|
26
44
|
// accent/accent-ink is only ~5:1 — dimming text on it drops body copy under 4.5:1 (WCAG 1.4.3), so accent keeps it at full strength.
|
|
27
|
-
tone: { default: {}, muted: {}, inverse: {}, accent: { eyebrow: 'opacity-100' } },
|
|
45
|
+
tone: { default: {}, muted: {}, inverse: {}, accent: { eyebrow: 'opacity-100' }, brand: { eyebrow: 'opacity-100' }, none: {} },
|
|
28
46
|
},
|
|
29
|
-
defaultVariants: { imageSide: 'end', align: 'start', tone: 'default' },
|
|
47
|
+
defaultVariants: { imageSide: 'end', ratio: '1:1', imageFit: 'contain', imageShape: 'rounded', imageBleed: 'none', stackOrder: 'text-first', sticky: false, align: 'start', tone: 'default' },
|
|
30
48
|
})
|
|
31
49
|
|
|
50
|
+
// Literal class names, so the stylesheet builder sees them: the text column's share first, the image's second; a
|
|
51
|
+
// picture before the text on wide screens swaps the two columns.
|
|
52
|
+
const COLUMNS = {
|
|
53
|
+
text: { '1:1': 'md:grid-cols-2', '5:7': 'md:grid-cols-[5fr_7fr]', '7:5': 'md:grid-cols-[7fr_5fr]' },
|
|
54
|
+
image: { '1:1': 'md:grid-cols-2', '5:7': 'md:grid-cols-[7fr_5fr]', '7:5': 'md:grid-cols-[5fr_7fr]' },
|
|
55
|
+
} as const
|
|
56
|
+
// The viewport's edge, from the content's edge: what is left of the page's width, halved.
|
|
57
|
+
const BLEED = {
|
|
58
|
+
end: 'md:-me-[calc((100vw-min(var(--container-page),100vw-2*var(--spacing-gutter,2rem)))/2)]',
|
|
59
|
+
start: 'md:-ms-[calc((100vw-min(var(--container-page),100vw-2*var(--spacing-gutter,2rem)))/2)]',
|
|
60
|
+
} as const
|
|
61
|
+
|
|
62
|
+
interface Feature {
|
|
63
|
+
title: string
|
|
64
|
+
text?: string | undefined
|
|
65
|
+
/** A kit icon name (check, star, bolt, shield, heart, globe, clock, chat). */
|
|
66
|
+
icon?: string | undefined
|
|
67
|
+
}
|
|
68
|
+
|
|
32
69
|
interface Props {
|
|
33
70
|
heading: string
|
|
71
|
+
/** The heading's level: 2 under the page title; 3 or 4 when the section sits inside another. */
|
|
72
|
+
headingLevel?: 2 | 3 | 4 | undefined
|
|
34
73
|
eyebrow?: string | undefined
|
|
35
74
|
/** Markdown, rendered at build time. */
|
|
36
75
|
body?: string | undefined
|
|
37
76
|
actions?: ActionInput[] | undefined
|
|
38
77
|
image?: ImageInput | undefined
|
|
78
|
+
/** Short points under the text, each with a title and a line. */
|
|
79
|
+
features?: Feature[] | undefined
|
|
80
|
+
/** A video page (YouTube, Vimeo); the image is its poster and nothing loads from the provider until it is played. */
|
|
81
|
+
videoUrl?: string | undefined
|
|
82
|
+
/** The video's text for the play button. */
|
|
83
|
+
playLabel?: string | undefined
|
|
39
84
|
imageSide?: 'start' | 'end' | undefined
|
|
85
|
+
ratio?: '1:1' | '5:7' | '7:5' | undefined
|
|
86
|
+
imageFit?: 'contain' | 'cover' | undefined
|
|
87
|
+
imageShape?: 'none' | 'rounded' | 'circle' | undefined
|
|
88
|
+
imageBleed?: 'none' | 'edge' | undefined
|
|
89
|
+
mediaKind?: 'image' | 'video' | undefined
|
|
90
|
+
featureStyle?: 'check' | 'icon' | undefined
|
|
91
|
+
stackOrder?: 'text-first' | 'media-first' | undefined
|
|
92
|
+
sticky?: boolean | undefined
|
|
40
93
|
align?: 'start' | 'center' | undefined
|
|
41
|
-
tone?: 'default' | 'muted' | 'inverse' | 'accent' | undefined
|
|
94
|
+
tone?: 'default' | 'muted' | 'inverse' | 'accent' | 'brand' | 'none' | undefined
|
|
95
|
+
/** 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. */
|
|
96
|
+
measured?: MeasuredStyle | undefined
|
|
42
97
|
id?: string | undefined
|
|
43
98
|
}
|
|
44
99
|
|
|
45
|
-
const { heading, eyebrow, body, actions = [], image, imageSide, align, tone, id } = Astro.props
|
|
46
|
-
const s = split({ imageSide, align, tone })
|
|
100
|
+
const { heading, headingLevel = 2, eyebrow, body, actions = [], image, features = [], videoUrl, playLabel, imageSide, ratio = '1:1', imageFit, imageShape, imageBleed, mediaKind, featureStyle = 'check', stackOrder, sticky = false, align, tone, measured, id } = Astro.props
|
|
101
|
+
const s = split({ imageSide, ratio, imageFit, imageShape, imageBleed, stackOrder, sticky, align, tone })
|
|
102
|
+
const Heading = `h${headingLevel}` as 'h2'
|
|
103
|
+
const columns = COLUMNS[imageSide === 'start' ? 'image' : 'text'][ratio]
|
|
104
|
+
const bleed = imageBleed === 'edge' ? BLEED[imageSide === 'start' ? 'start' : 'end'] : undefined
|
|
105
|
+
const video = mediaKind === 'video' && videoUrl
|
|
47
106
|
const headingId = id ? `${id}-title` : undefined
|
|
48
107
|
const html = await renderMarkdown(body)
|
|
49
108
|
---
|
|
50
|
-
<Section tone={tone} id={id} aria-labelledby={headingId}>
|
|
51
|
-
<div class={s.grid()}>
|
|
109
|
+
<Section measured={measured} tone={tone} id={id} aria-labelledby={headingId} class={bleed ? 'overflow-x-clip' : undefined}>
|
|
110
|
+
<div class:list={[s.grid(), columns]}>
|
|
52
111
|
<div class={s.copy()}>
|
|
53
112
|
{eyebrow && <p class={s.eyebrow()}>{eyebrow}</p>}
|
|
54
|
-
<
|
|
113
|
+
<Heading id={headingId} class={s.heading()}>{heading}</Heading>
|
|
55
114
|
{html && <div class={s.body()} data-kit-text set:html={html} />}
|
|
115
|
+
{features.length > 0 && <FeatureList items={features} columns="2" style={featureStyle} tone={tone === 'brand' || tone === 'none' ? undefined : tone} bare titleLevel={Math.min(5, headingLevel + 1) as 3 | 4 | 5} />}
|
|
56
116
|
{actions.length > 0 && <div class={s.actions()}>{actions.map(action => <Button action={action} size="lg" />)}</div>}
|
|
57
117
|
</div>
|
|
58
|
-
{image && (
|
|
59
|
-
<div class={s.media()}>
|
|
60
|
-
|
|
118
|
+
{(video || image) && (
|
|
119
|
+
<div class:list={[s.media(), bleed]}>
|
|
120
|
+
{video
|
|
121
|
+
? <div class="[&>figure]:my-0 [&>figure]:max-w-none"><Embed url={videoUrl} title={heading} poster={image} playLabel={playLabel} aspect="video" width="page" /></div>
|
|
122
|
+
: image && <KitImage image={image} sizes="(min-width: 768px) 50vw, 100vw" class={s.image()} />}
|
|
61
123
|
</div>
|
|
62
124
|
)}
|
|
63
125
|
</div>
|
|
@@ -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,7 +3,10 @@
|
|
|
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",
|
|
@@ -20,6 +23,64 @@
|
|
|
20
23
|
"default": "end",
|
|
21
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."
|
|
22
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
|
+
},
|
|
23
84
|
"align": {
|
|
24
85
|
"options": [
|
|
25
86
|
"start",
|
|
@@ -33,10 +94,12 @@
|
|
|
33
94
|
"default",
|
|
34
95
|
"muted",
|
|
35
96
|
"inverse",
|
|
36
|
-
"accent"
|
|
97
|
+
"accent",
|
|
98
|
+
"brand",
|
|
99
|
+
"none"
|
|
37
100
|
],
|
|
38
101
|
"default": "default",
|
|
39
|
-
"description": "Section background."
|
|
102
|
+
"description": "Section background (brand: the site's brand colour; none: the page's own)."
|
|
40
103
|
}
|
|
41
104
|
},
|
|
42
105
|
"props": {
|
|
@@ -46,6 +109,12 @@
|
|
|
46
109
|
"required": true,
|
|
47
110
|
"description": "The section heading."
|
|
48
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
|
+
},
|
|
49
118
|
"eyebrow": {
|
|
50
119
|
"type": "string",
|
|
51
120
|
"label": "Eyebrow",
|
|
@@ -121,10 +190,162 @@
|
|
|
121
190
|
},
|
|
122
191
|
"description": "The image beside the text. Width and height, when known, let the site reserve its space."
|
|
123
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
|
+
},
|
|
124
339
|
"id": {
|
|
125
340
|
"type": "string",
|
|
126
341
|
"content": false,
|
|
127
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)."
|
|
128
349
|
}
|
|
129
350
|
},
|
|
130
351
|
"slots": {},
|
|
@@ -141,7 +362,7 @@
|
|
|
141
362
|
"divi/et_pb_image"
|
|
142
363
|
]
|
|
143
364
|
},
|
|
144
|
-
"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.",
|
|
145
366
|
"pattern": {
|
|
146
367
|
"summary": "image beside heading + text",
|
|
147
368
|
"signals": [
|