@contentrain/astro-kit 0.15.0 → 0.17.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 +24 -0
- package/components/cta/Cta.astro +11 -3
- package/components/cta/fixtures.json +2 -1
- package/components/cta/meta.json +2 -1
- package/components/post-card/PostCard.astro +34 -7
- package/components/post-card/fixtures.json +23 -0
- package/components/post-card/meta.json +3 -1
- package/components/testimonial/Testimonial.astro +3 -1
- package/mapping/elementor.json +17 -2
- package/package.json +2 -2
package/catalog.json
CHANGED
|
@@ -657,6 +657,14 @@
|
|
|
657
657
|
],
|
|
658
658
|
"default": "accent",
|
|
659
659
|
"description": "Background. On accent, primary buttons are drawn in the secondary style so they stay visible. none: no fill of its own; the page background shows through (a source colour the palette does not have)."
|
|
660
|
+
},
|
|
661
|
+
"actionsAlign": {
|
|
662
|
+
"options": [
|
|
663
|
+
"inherit",
|
|
664
|
+
"start"
|
|
665
|
+
],
|
|
666
|
+
"default": "inherit",
|
|
667
|
+
"description": "The buttons' alignment. inherit: as the layout sets it. start: at the start edge under centred heading and text (layout centered; a WordPress buttons block with no justification in a centred group). inline and stacked ignore it."
|
|
660
668
|
}
|
|
661
669
|
},
|
|
662
670
|
"props": {
|
|
@@ -2221,6 +2229,22 @@
|
|
|
2221
2229
|
],
|
|
2222
2230
|
"default": "vertical",
|
|
2223
2231
|
"description": "Image on top, image beside the text, or text only with a divider."
|
|
2232
|
+
},
|
|
2233
|
+
"surface": {
|
|
2234
|
+
"options": [
|
|
2235
|
+
"plain",
|
|
2236
|
+
"card"
|
|
2237
|
+
],
|
|
2238
|
+
"default": "plain",
|
|
2239
|
+
"description": "plain: no surface. card: its own background (--color-card), radius and shadow (--shadow-card), the text padded (--spacing-card) and the image flush at the edge, as theme cards are drawn on a tinted page."
|
|
2240
|
+
},
|
|
2241
|
+
"byline": {
|
|
2242
|
+
"options": [
|
|
2243
|
+
"inline",
|
|
2244
|
+
"below"
|
|
2245
|
+
],
|
|
2246
|
+
"default": "inline",
|
|
2247
|
+
"description": "inline: category · date above the title. below: the category above the title and author / date under it, both in the accent colour, small and semibold."
|
|
2224
2248
|
}
|
|
2225
2249
|
},
|
|
2226
2250
|
"props": {
|
package/components/cta/Cta.astro
CHANGED
|
@@ -28,6 +28,11 @@ const cta = tv({
|
|
|
28
28
|
// 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.
|
|
29
29
|
// none: no fill of its own — the page background and text colour show through.
|
|
30
30
|
tone: { default: {}, muted: {}, inverse: {}, accent: { body: 'opacity-100' }, none: {} },
|
|
31
|
+
// The buttons' own alignment: `start` keeps them at the start edge under centred text (a WordPress buttons block
|
|
32
|
+
// with no justification sits left in a group whose heading and paragraph are centred). The row is the text's
|
|
33
|
+
// column, as WordPress's content width is, not the whole section. Only centred needs it: inline and stacked
|
|
34
|
+
// already start them there.
|
|
35
|
+
actionsAlign: { inherit: {}, start: {} },
|
|
31
36
|
},
|
|
32
37
|
compoundVariants: [
|
|
33
38
|
// A card carries the tone itself, on a plain section; a plain card still needs a surface to read as one.
|
|
@@ -35,8 +40,9 @@ const cta = tv({
|
|
|
35
40
|
{ frame: 'card', tone: 'muted', class: { box: 'border-transparent bg-surface-muted text-ink' } },
|
|
36
41
|
{ frame: 'card', tone: 'inverse', class: { box: 'border-transparent bg-ink text-surface' } },
|
|
37
42
|
{ frame: 'card', tone: 'accent', class: { box: 'border-transparent bg-accent text-accent-ink' } },
|
|
43
|
+
{ layout: 'centered', actionsAlign: 'start', class: { actions: 'w-full max-w-2xl justify-start' } },
|
|
38
44
|
],
|
|
39
|
-
defaultVariants: { layout: 'inline', frame: 'full', tone: 'accent' },
|
|
45
|
+
defaultVariants: { layout: 'inline', frame: 'full', tone: 'accent', actionsAlign: 'inherit' },
|
|
40
46
|
})
|
|
41
47
|
|
|
42
48
|
interface Props {
|
|
@@ -46,11 +52,13 @@ interface Props {
|
|
|
46
52
|
layout?: 'inline' | 'centered' | 'stacked' | undefined
|
|
47
53
|
frame?: 'full' | 'card' | undefined
|
|
48
54
|
tone?: 'default' | 'muted' | 'inverse' | 'accent' | 'none' | undefined
|
|
55
|
+
/** The buttons' alignment: as the layout sets it, or at the start edge under centred text (layout centered). */
|
|
56
|
+
actionsAlign?: 'inherit' | 'start' | undefined
|
|
49
57
|
id?: string | undefined
|
|
50
58
|
}
|
|
51
59
|
|
|
52
|
-
const { heading, body, actions, layout, frame, tone = 'accent', id } = Astro.props
|
|
53
|
-
const s = cta({ layout, frame, tone })
|
|
60
|
+
const { heading, body, actions, layout, frame, tone = 'accent', actionsAlign, id } = Astro.props
|
|
61
|
+
const s = cta({ layout, frame, tone, actionsAlign })
|
|
54
62
|
const headingId = id ? `${id}-title` : undefined
|
|
55
63
|
// On the accent background the primary button (accent) would vanish: it is drawn as the secondary style there.
|
|
56
64
|
const onAccent = (action: ActionInput): ActionInput => ({ label: action.label, href: action.href, newTab: action.newTab, style: 'secondary' })
|
|
@@ -3,5 +3,6 @@
|
|
|
3
3
|
{ "name": "centered-inverse", "props": { "heading": "Planning a trip?", "body": "Start with the guides readers use most.", "layout": "centered", "tone": "inverse", "actions": [{ "label": "Popular guides", "href": "/guides/" }, { "label": "Ask a question", "href": "/contact/", "style": "ghost" }] } },
|
|
4
4
|
{ "name": "card-default", "props": { "heading": "Write for the journal", "body": "We publish reader essays every season.", "frame": "card", "tone": "default", "actions": [{ "label": "Pitch an essay", "href": "/pitch/" }] } },
|
|
5
5
|
{ "name": "card-muted", "props": { "heading": "Support independent travel writing", "frame": "card", "tone": "muted", "layout": "centered", "actions": [{ "label": "Become a member", "href": "/members/" }] } },
|
|
6
|
-
{ "name": "stacked-none", "props": { "heading": "Ready when you are", "body": "Tell us where you want to go and we will send a first plan within a week.", "layout": "stacked", "tone": "none", "actions": [{ "label": "Start planning", "href": "/contact/" }] } }
|
|
6
|
+
{ "name": "stacked-none", "props": { "heading": "Ready when you are", "body": "Tell us where you want to go and we will send a first plan within a week.", "layout": "stacked", "tone": "none", "actions": [{ "label": "Start planning", "href": "/contact/" }] } },
|
|
7
|
+
{ "name": "centered-actions-start", "props": { "heading": "See what is on this week", "body": "New films every Thursday, and a late show on Saturdays.", "layout": "centered", "tone": "inverse", "actionsAlign": "start", "actions": [{ "label": "Programme", "href": "/programme/" }] } }
|
|
7
8
|
]
|
package/components/cta/meta.json
CHANGED
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
"variants": {
|
|
9
9
|
"layout": { "options": ["inline", "centered", "stacked"], "default": "inline", "description": "inline: text left, buttons right on wide screens. centered: everything centred. stacked: heading, text and buttons in one column at the start edge on every width." },
|
|
10
10
|
"frame": { "options": ["full", "card"], "default": "full", "description": "full: the tone fills the section. card: a rounded box in a plain section carries the tone." },
|
|
11
|
-
"tone": { "options": ["default", "muted", "inverse", "accent", "none"], "default": "accent", "description": "Background. On accent, primary buttons are drawn in the secondary style so they stay visible. none: no fill of its own; the page background shows through (a source colour the palette does not have)." }
|
|
11
|
+
"tone": { "options": ["default", "muted", "inverse", "accent", "none"], "default": "accent", "description": "Background. On accent, primary buttons are drawn in the secondary style so they stay visible. none: no fill of its own; the page background shows through (a source colour the palette does not have)." },
|
|
12
|
+
"actionsAlign": { "options": ["inherit", "start"], "default": "inherit", "description": "The buttons' alignment. inherit: as the layout sets it. start: at the start edge under centred heading and text (layout centered; a WordPress buttons block with no justification in a centred group). inline and stacked ignore it." }
|
|
12
13
|
},
|
|
13
14
|
"props": {
|
|
14
15
|
"heading": { "type": "string", "label": "Heading", "required": true, "description": "The call's heading (Divi CTA title, Elementor call-to-action title)." },
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
// One post in a list: image, category, date, title, excerpt and author. The
|
|
3
|
-
// whole card is clickable through its title link.
|
|
3
|
+
// whole card is clickable through its title link. `surface: card` makes it a
|
|
4
|
+
// card of its own (background, radius, shadow, padded text, image flush);
|
|
5
|
+
// `byline: below` prints the category above the title and `author / date`
|
|
6
|
+
// under it, as card themes do.
|
|
4
7
|
import { tv } from 'tailwind-variants'
|
|
5
8
|
import KitImage from '../_shared/KitImage.astro'
|
|
6
9
|
import type { ImageInput, LinkInput } from '../_shared/types'
|
|
@@ -20,8 +23,16 @@ const card = tv({
|
|
|
20
23
|
horizontal: { root: 'flex-col sm:flex-row sm:items-start', media: 'w-full sm:w-64', title: 'text-xl', excerpt: 'line-clamp-2' },
|
|
21
24
|
minimal: { root: 'flex-col border-b border-line pb-5', media: 'hidden', title: 'text-lg', excerpt: 'line-clamp-2' },
|
|
22
25
|
},
|
|
26
|
+
surface: {
|
|
27
|
+
plain: {},
|
|
28
|
+
card: { root: 'gap-0 overflow-hidden rounded-[var(--radius-card)] bg-card shadow-[var(--shadow-card,none)]', media: 'rounded-none', body: 'gap-3 p-[var(--spacing-card,1.5rem)]' },
|
|
29
|
+
},
|
|
30
|
+
byline: {
|
|
31
|
+
inline: {},
|
|
32
|
+
below: { meta: 'text-xs font-semibold text-accent' },
|
|
33
|
+
},
|
|
23
34
|
},
|
|
24
|
-
defaultVariants: { layout: 'vertical' },
|
|
35
|
+
defaultVariants: { layout: 'vertical', surface: 'plain', byline: 'inline' },
|
|
25
36
|
})
|
|
26
37
|
|
|
27
38
|
interface Props {
|
|
@@ -37,13 +48,17 @@ interface Props {
|
|
|
37
48
|
author?: string | undefined
|
|
38
49
|
headingLevel?: 2 | 3 | undefined
|
|
39
50
|
layout?: 'vertical' | 'horizontal' | 'minimal' | undefined
|
|
51
|
+
surface?: 'plain' | 'card' | undefined
|
|
52
|
+
byline?: 'inline' | 'below' | undefined
|
|
40
53
|
/** The title in the site's link colour, as themes that colour every link do (Hello Elementor). */
|
|
41
54
|
linkTitle?: boolean | undefined
|
|
42
55
|
}
|
|
43
56
|
|
|
44
|
-
const { title, href, excerpt, date: rawDate, locale = 'en', image, category, author, headingLevel = 2, layout, linkTitle } = Astro.props
|
|
57
|
+
const { title, href, excerpt, date: rawDate, locale = 'en', image, category, author, headingLevel = 2, layout, surface, byline, linkTitle } = Astro.props
|
|
45
58
|
const date = rawDate === undefined ? undefined : new Date(rawDate)
|
|
46
|
-
const slots = card({ layout })
|
|
59
|
+
const slots = card({ layout, surface, byline })
|
|
60
|
+
const below = byline === 'below'
|
|
61
|
+
const shown = date?.toLocaleDateString(locale, { dateStyle: 'long', timeZone: 'UTC' })
|
|
47
62
|
const Heading = `h${headingLevel}` as 'h2' | 'h3'
|
|
48
63
|
---
|
|
49
64
|
<article class={slots.root()}>
|
|
@@ -53,17 +68,29 @@ const Heading = `h${headingLevel}` as 'h2' | 'h3'
|
|
|
53
68
|
</div>
|
|
54
69
|
)}
|
|
55
70
|
<div class={slots.body()}>
|
|
56
|
-
{
|
|
71
|
+
{below && category && (
|
|
72
|
+
<p class={slots.meta()}>
|
|
73
|
+
<a href={category.href} class="relative z-10 no-underline hover:underline">{category.label}</a>
|
|
74
|
+
</p>
|
|
75
|
+
)}
|
|
76
|
+
{!below && (category || date) && (
|
|
57
77
|
<p class={slots.meta()}>
|
|
58
78
|
{category && <a href={category.href} class="relative z-10 font-medium text-accent no-underline hover:underline">{category.label}</a>}
|
|
59
79
|
{category && date && <span aria-hidden="true">·</span>}
|
|
60
|
-
{date && <time datetime={date.toISOString()}>{
|
|
80
|
+
{date && <time datetime={date.toISOString()}>{shown}</time>}
|
|
61
81
|
</p>
|
|
62
82
|
)}
|
|
63
83
|
<Heading class={slots.title({ class: linkTitle ? 'text-[color:var(--color-link,var(--color-accent))]' : undefined })}>
|
|
64
84
|
<a href={href} class="text-current no-underline after:absolute after:inset-0 after:content-[''] hover:text-accent">{title}</a>
|
|
65
85
|
</Heading>
|
|
86
|
+
{below && (author || date) && (
|
|
87
|
+
<p class={slots.meta()}>
|
|
88
|
+
{author && <span>{author}</span>}
|
|
89
|
+
{author && date && <span aria-hidden="true">/</span>}
|
|
90
|
+
{date && <time datetime={date.toISOString()}>{shown}</time>}
|
|
91
|
+
</p>
|
|
92
|
+
)}
|
|
66
93
|
{excerpt && <p class={slots.excerpt()}>{excerpt}</p>}
|
|
67
|
-
{author && <p class="text-sm text-ink-muted">{author}</p>}
|
|
94
|
+
{!below && author && <p class="text-sm text-ink-muted">{author}</p>}
|
|
68
95
|
</div>
|
|
69
96
|
</article>
|
|
@@ -50,5 +50,28 @@
|
|
|
50
50
|
"href": "/category/news/"
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"name": "card-byline",
|
|
56
|
+
"props": {
|
|
57
|
+
"surface": "card",
|
|
58
|
+
"byline": "below",
|
|
59
|
+
"title": "Laminating dough without a sheeter",
|
|
60
|
+
"href": "/recipes/laminating/",
|
|
61
|
+
"excerpt": "Laminating dough without a sheeter — the short version first.",
|
|
62
|
+
"date": "2026-02-10T09:00:00Z",
|
|
63
|
+
"category": {
|
|
64
|
+
"label": "Recipes",
|
|
65
|
+
"href": "/category/recipes/"
|
|
66
|
+
},
|
|
67
|
+
"author": "admin",
|
|
68
|
+
"image": {
|
|
69
|
+
"src": "/kit-fixtures/photo-1.svg",
|
|
70
|
+
"alt": "",
|
|
71
|
+
"width": 1200,
|
|
72
|
+
"height": 800
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
"frame": "narrow"
|
|
53
76
|
}
|
|
54
77
|
]
|
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
"uses": [],
|
|
7
7
|
"shared": ["KitImage.astro", "types.ts"],
|
|
8
8
|
"variants": {
|
|
9
|
-
"layout": { "options": ["vertical", "horizontal", "minimal"], "default": "vertical", "description": "Image on top, image beside the text, or text only with a divider." }
|
|
9
|
+
"layout": { "options": ["vertical", "horizontal", "minimal"], "default": "vertical", "description": "Image on top, image beside the text, or text only with a divider." },
|
|
10
|
+
"surface": { "options": ["plain", "card"], "default": "plain", "description": "plain: no surface. card: its own background (--color-card), radius and shadow (--shadow-card), the text padded (--spacing-card) and the image flush at the edge, as theme cards are drawn on a tinted page." },
|
|
11
|
+
"byline": { "options": ["inline", "below"], "default": "inline", "description": "inline: category · date above the title. below: the category above the title and author / date under it, both in the accent colour, small and semibold." }
|
|
10
12
|
},
|
|
11
13
|
"props": {
|
|
12
14
|
"title": { "type": "string", "required": true, "description": "Post title (posts.title)." },
|
|
@@ -58,6 +58,8 @@ interface Props {
|
|
|
58
58
|
const { items, heading, layout, quoteSize, tone, id } = Astro.props
|
|
59
59
|
const s = testimonial({ layout, quoteSize, tone })
|
|
60
60
|
const headingId = heading && id ? `${id}-title` : undefined
|
|
61
|
+
// Two quotes share the row: a third column would stand empty beside them and narrow both (IP-4 elementor launch G9).
|
|
62
|
+
const list = s.list(items.length === 2 && (layout ?? 'grid') === 'grid' ? { class: 'lg:grid-cols-2' } : {})
|
|
61
63
|
// The quotation marks are drawn, not written: the quote's text is the source's words, read once. A quote the source
|
|
62
64
|
// already wrote inside marks keeps its own.
|
|
63
65
|
const quoted = (text: string) => /^\s*["'“‘„«‹「『]/.test(text)
|
|
@@ -65,7 +67,7 @@ const quoted = (text: string) => /^\s*["'“‘„«‹「『]/.test(text)
|
|
|
65
67
|
{items.length > 0 && (
|
|
66
68
|
<Section tone={tone} spacing="md" id={id} aria-labelledby={headingId}>
|
|
67
69
|
{heading && <h2 id={headingId} class={s.heading()}>{heading}</h2>}
|
|
68
|
-
<div class={
|
|
70
|
+
<div class={list}>
|
|
69
71
|
{items.map(item => (
|
|
70
72
|
<figure class={s.card()}>
|
|
71
73
|
<blockquote class={s.quote()}><p class:list={[!quoted(item.quote) && "before:content-[open-quote] after:content-[close-quote]"]}>{item.quote}</p></blockquote>
|
package/mapping/elementor.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"format": "astro-kit-mapping@1",
|
|
3
3
|
"builder": "elementor",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "10",
|
|
5
5
|
"fallback": "prose",
|
|
6
6
|
"defaults": {
|
|
7
7
|
"elementor/video": {
|
|
@@ -605,6 +605,9 @@
|
|
|
605
605
|
{
|
|
606
606
|
"id": "team.columns",
|
|
607
607
|
"component": "team",
|
|
608
|
+
"variant": {
|
|
609
|
+
"roleStyle": "plain"
|
|
610
|
+
},
|
|
608
611
|
"when": {
|
|
609
612
|
"columns": [
|
|
610
613
|
2,
|
|
@@ -761,6 +764,9 @@
|
|
|
761
764
|
{
|
|
762
765
|
"id": "stats.counter",
|
|
763
766
|
"component": "stats",
|
|
767
|
+
"variant": {
|
|
768
|
+
"labelSize": "lg"
|
|
769
|
+
},
|
|
764
770
|
"slots": {
|
|
765
771
|
"title": {
|
|
766
772
|
"match": "elementor/heading",
|
|
@@ -848,6 +854,9 @@
|
|
|
848
854
|
{
|
|
849
855
|
"id": "testimonial.quotes",
|
|
850
856
|
"component": "testimonial",
|
|
857
|
+
"variant": {
|
|
858
|
+
"quoteSize": "xl"
|
|
859
|
+
},
|
|
851
860
|
"slots": {
|
|
852
861
|
"title": {
|
|
853
862
|
"match": "elementor/heading",
|
|
@@ -874,6 +883,9 @@
|
|
|
874
883
|
{
|
|
875
884
|
"id": "faq.accordion",
|
|
876
885
|
"component": "faq",
|
|
886
|
+
"variant": {
|
|
887
|
+
"questionStyle": "bold"
|
|
888
|
+
},
|
|
877
889
|
"slots": {
|
|
878
890
|
"title": {
|
|
879
891
|
"match": "elementor/heading",
|
|
@@ -898,6 +910,9 @@
|
|
|
898
910
|
{
|
|
899
911
|
"id": "faq.toggle",
|
|
900
912
|
"component": "faq",
|
|
913
|
+
"variant": {
|
|
914
|
+
"questionStyle": "bold"
|
|
915
|
+
},
|
|
901
916
|
"slots": {
|
|
902
917
|
"title": {
|
|
903
918
|
"match": "elementor/heading",
|
|
@@ -1103,7 +1118,7 @@
|
|
|
1103
1118
|
"id": "feature-list.icon-list",
|
|
1104
1119
|
"component": "feature-list",
|
|
1105
1120
|
"variant": {
|
|
1106
|
-
"style": "
|
|
1121
|
+
"style": "list"
|
|
1107
1122
|
},
|
|
1108
1123
|
"slots": {
|
|
1109
1124
|
"title": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contentrain/astro-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Copy-in Astro components for Contentrain sites (shadcn model): typed props, tailwind-variants, zero JavaScript by default — with a machine-readable catalog",
|
|
6
6
|
"type": "module",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"mapping"
|
|
43
43
|
],
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@contentrain/types": "1.
|
|
45
|
+
"@contentrain/types": "1.36.0"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@types/node": "^22.10.0",
|