@contentrain/astro-kit 0.16.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 CHANGED
@@ -2229,6 +2229,22 @@
2229
2229
  ],
2230
2230
  "default": "vertical",
2231
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."
2232
2248
  }
2233
2249
  },
2234
2250
  "props": {
@@ -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
- {(category || date) && (
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()}>{date.toLocaleDateString(locale, { dateStyle: 'long', timeZone: 'UTC' })}</time>}
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={s.list()}>
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>
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "format": "astro-kit-mapping@1",
3
3
  "builder": "elementor",
4
- "version": "9",
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": "icon"
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.16.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.35.0"
45
+ "@contentrain/types": "1.36.0"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@types/node": "^22.10.0",