@contentrain/astro-kit 0.46.1 → 0.46.3
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 +21 -0
- package/components/_shared/KitImage.astro +1 -3
- package/components/_shared/card-image.ts +13 -0
- package/components/_shared/image-widths.ts +13 -0
- package/components/band/Band.astro +2 -1
- package/components/band/meta.json +2 -0
- package/components/card-grid/CardGrid.astro +3 -2
- package/components/card-grid/meta.json +2 -0
- package/components/contact-form/meta.json +1 -0
- package/components/cta/meta.json +1 -1
- package/components/embed/meta.json +1 -1
- package/components/feature-list/meta.json +1 -0
- package/components/gallery/meta.json +1 -0
- package/components/header/meta.json +1 -1
- package/components/hero/meta.json +1 -0
- package/components/logo-cloud/meta.json +1 -0
- package/components/post-card/PostCard.astro +2 -1
- package/components/post-card/meta.json +1 -1
- package/components/slider/SliderTrack.astro +3 -2
- package/components/slider/fixtures.json +1 -0
- package/components/slider/meta.json +1 -1
- package/components/split/meta.json +1 -0
- package/components/stats/meta.json +1 -0
- package/components/steps/meta.json +1 -0
- package/components/team/Team.astro +2 -1
- package/components/team/meta.json +2 -0
- package/components/testimonial/meta.json +1 -0
- package/package.json +1 -1
package/catalog.json
CHANGED
|
@@ -105,6 +105,8 @@
|
|
|
105
105
|
"KitImage.astro",
|
|
106
106
|
"Lines.astro",
|
|
107
107
|
"Section.astro",
|
|
108
|
+
"card-image.ts",
|
|
109
|
+
"image-widths.ts",
|
|
108
110
|
"instance-id.ts",
|
|
109
111
|
"measured.ts",
|
|
110
112
|
"muted.ts",
|
|
@@ -789,6 +791,8 @@
|
|
|
789
791
|
"shared": [
|
|
790
792
|
"KitImage.astro",
|
|
791
793
|
"Section.astro",
|
|
794
|
+
"card-image.ts",
|
|
795
|
+
"image-widths.ts",
|
|
792
796
|
"image.ts",
|
|
793
797
|
"instance-id.ts",
|
|
794
798
|
"measured.ts",
|
|
@@ -1400,6 +1404,7 @@
|
|
|
1400
1404
|
"shared": [
|
|
1401
1405
|
"KitImage.astro",
|
|
1402
1406
|
"Section.astro",
|
|
1407
|
+
"image-widths.ts",
|
|
1403
1408
|
"instance-id.ts",
|
|
1404
1409
|
"measured.ts",
|
|
1405
1410
|
"muted.ts",
|
|
@@ -1734,6 +1739,7 @@
|
|
|
1734
1739
|
"Button.astro",
|
|
1735
1740
|
"KitImage.astro",
|
|
1736
1741
|
"Section.astro",
|
|
1742
|
+
"image-widths.ts",
|
|
1737
1743
|
"instance-id.ts",
|
|
1738
1744
|
"measured.ts",
|
|
1739
1745
|
"muted.ts",
|
|
@@ -2327,6 +2333,7 @@
|
|
|
2327
2333
|
"KitImage.astro",
|
|
2328
2334
|
"embed-frame.ts",
|
|
2329
2335
|
"embed-target.ts",
|
|
2336
|
+
"image-widths.ts",
|
|
2330
2337
|
"safe-url.ts",
|
|
2331
2338
|
"types.ts"
|
|
2332
2339
|
],
|
|
@@ -2715,6 +2722,7 @@
|
|
|
2715
2722
|
"shared": [
|
|
2716
2723
|
"KitImage.astro",
|
|
2717
2724
|
"Section.astro",
|
|
2725
|
+
"image-widths.ts",
|
|
2718
2726
|
"instance-id.ts",
|
|
2719
2727
|
"measured.ts",
|
|
2720
2728
|
"muted.ts",
|
|
@@ -3225,6 +3233,7 @@
|
|
|
3225
3233
|
"shared": [
|
|
3226
3234
|
"KitImage.astro",
|
|
3227
3235
|
"Section.astro",
|
|
3236
|
+
"image-widths.ts",
|
|
3228
3237
|
"image.ts",
|
|
3229
3238
|
"instance-id.ts",
|
|
3230
3239
|
"measured.ts",
|
|
@@ -3549,6 +3558,7 @@
|
|
|
3549
3558
|
"shared": [
|
|
3550
3559
|
"Button.astro",
|
|
3551
3560
|
"KitImage.astro",
|
|
3561
|
+
"image-widths.ts",
|
|
3552
3562
|
"instance-id.ts",
|
|
3553
3563
|
"muted.ts",
|
|
3554
3564
|
"safe-url.ts",
|
|
@@ -3783,6 +3793,7 @@
|
|
|
3783
3793
|
"Lines.astro",
|
|
3784
3794
|
"Section.astro",
|
|
3785
3795
|
"bleed.ts",
|
|
3796
|
+
"image-widths.ts",
|
|
3786
3797
|
"instance-id.ts",
|
|
3787
3798
|
"measured.ts",
|
|
3788
3799
|
"muted.ts",
|
|
@@ -4280,6 +4291,7 @@
|
|
|
4280
4291
|
"shared": [
|
|
4281
4292
|
"KitImage.astro",
|
|
4282
4293
|
"Section.astro",
|
|
4294
|
+
"image-widths.ts",
|
|
4283
4295
|
"image.ts",
|
|
4284
4296
|
"instance-id.ts",
|
|
4285
4297
|
"measured.ts",
|
|
@@ -5005,6 +5017,8 @@
|
|
|
5005
5017
|
"uses": [],
|
|
5006
5018
|
"shared": [
|
|
5007
5019
|
"KitImage.astro",
|
|
5020
|
+
"card-image.ts",
|
|
5021
|
+
"image-widths.ts",
|
|
5008
5022
|
"safe-url.ts",
|
|
5009
5023
|
"types.ts"
|
|
5010
5024
|
],
|
|
@@ -6075,6 +6089,7 @@
|
|
|
6075
6089
|
"SectionHeader.astro",
|
|
6076
6090
|
"embed-frame.ts",
|
|
6077
6091
|
"embed-target.ts",
|
|
6092
|
+
"image-widths.ts",
|
|
6078
6093
|
"image.ts",
|
|
6079
6094
|
"instance-id.ts",
|
|
6080
6095
|
"measured.ts",
|
|
@@ -6543,6 +6558,7 @@
|
|
|
6543
6558
|
"KitImage.astro",
|
|
6544
6559
|
"Section.astro",
|
|
6545
6560
|
"bleed.ts",
|
|
6561
|
+
"image-widths.ts",
|
|
6546
6562
|
"instance-id.ts",
|
|
6547
6563
|
"measured.ts",
|
|
6548
6564
|
"muted.ts",
|
|
@@ -6952,6 +6968,7 @@
|
|
|
6952
6968
|
"behaviors.ts",
|
|
6953
6969
|
"embed-frame.ts",
|
|
6954
6970
|
"embed-target.ts",
|
|
6971
|
+
"image-widths.ts",
|
|
6955
6972
|
"image.ts",
|
|
6956
6973
|
"instance-id.ts",
|
|
6957
6974
|
"measured.ts",
|
|
@@ -7259,6 +7276,7 @@
|
|
|
7259
7276
|
"shared": [
|
|
7260
7277
|
"KitImage.astro",
|
|
7261
7278
|
"Section.astro",
|
|
7279
|
+
"image-widths.ts",
|
|
7262
7280
|
"image.ts",
|
|
7263
7281
|
"instance-id.ts",
|
|
7264
7282
|
"measured.ts",
|
|
@@ -7809,6 +7827,8 @@
|
|
|
7809
7827
|
"shared": [
|
|
7810
7828
|
"KitImage.astro",
|
|
7811
7829
|
"Section.astro",
|
|
7830
|
+
"card-image.ts",
|
|
7831
|
+
"image-widths.ts",
|
|
7812
7832
|
"image.ts",
|
|
7813
7833
|
"instance-id.ts",
|
|
7814
7834
|
"measured.ts",
|
|
@@ -8124,6 +8144,7 @@
|
|
|
8124
8144
|
"KitImage.astro",
|
|
8125
8145
|
"Lines.astro",
|
|
8126
8146
|
"Section.astro",
|
|
8147
|
+
"image-widths.ts",
|
|
8127
8148
|
"image.ts",
|
|
8128
8149
|
"instance-id.ts",
|
|
8129
8150
|
"measured.ts",
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
import type { ImageMetadata } from 'astro'
|
|
17
17
|
import { Image } from 'astro:assets'
|
|
18
18
|
import { mediaSize } from '../../../lib/media'
|
|
19
|
+
import { widthsUpTo } from './image-widths'
|
|
19
20
|
import { safeSrc } from './safe-url'
|
|
20
21
|
import type { ImageInput } from './types'
|
|
21
22
|
|
|
@@ -54,9 +55,6 @@ const load = decoded === undefined ? undefined : LOCAL[`/public${decoded}`]
|
|
|
54
55
|
const local = load ? await load() : undefined
|
|
55
56
|
// The stored size (WordPress's chosen size of the file), else the file's own.
|
|
56
57
|
const size = image.width && image.height ? { width: image.width, height: image.height } : {}
|
|
57
|
-
// Candidate widths for the srcset, up to the image's own: the browser picks one by `sizes`.
|
|
58
|
-
const STEPS = [320, 640, 960, 1280, 1600, 1920, 2560]
|
|
59
|
-
const widthsUpTo = (max: number) => [...STEPS.filter(step => step < max), max]
|
|
60
58
|
---
|
|
61
59
|
{url && (
|
|
62
60
|
local
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ImageInput } from './types'
|
|
2
|
+
|
|
3
|
+
const spoken = (text: string): string => text.replace(/\s+/g, ' ').trim().replace(/[.:;!]+$/, '').toLowerCase()
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A card's image beside its title link: when the title's link covers the card (a stretched link) and the image's text
|
|
7
|
+
* only repeats the title, the image is drawn with an empty text, so a screen reader says the title once. The content
|
|
8
|
+
* keeps its text (Studio, image search); an image outside such a card, or with words of its own, is drawn as written.
|
|
9
|
+
*/
|
|
10
|
+
export function cardImage(image: ImageInput | undefined, title: string | undefined, linked: boolean): ImageInput | undefined {
|
|
11
|
+
if (!image || !linked || !image.alt || !title || spoken(image.alt) !== spoken(title)) return image
|
|
12
|
+
return { ...image, alt: '' }
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// The width ladder of every resized image (KitImage for sections, the starter's lib/body-images for rich text): the
|
|
2
|
+
// candidates of a srcset, up to the largest one, and the browser picks one by `sizes`.
|
|
3
|
+
|
|
4
|
+
const STEPS = [320, 640, 960, 1280, 1600, 1920, 2560]
|
|
5
|
+
|
|
6
|
+
/** Candidate widths up to `max`, `max` itself the last. */
|
|
7
|
+
export const widthsUpTo = (max: number): number[] => [...STEPS.filter(step => step < max), max]
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The largest width worth making of a file shown at `shown` px: twice that (a 2× screen), never more than the file.
|
|
11
|
+
* Without a shown width, the file's own.
|
|
12
|
+
*/
|
|
13
|
+
export const widthCap = (file: number, shown?: number): number => (shown && shown > 0 ? Math.min(file, 2 * shown) : file)
|
|
@@ -12,6 +12,7 @@ import type { ActionInput, ImageInput, MeasuredStyle } from '../_shared/types'
|
|
|
12
12
|
import { safeHref } from '../_shared/safe-url'
|
|
13
13
|
import { instanceId } from '../_shared/instance-id'
|
|
14
14
|
import { muted } from '../_shared/muted'
|
|
15
|
+
import { cardImage } from '../_shared/card-image'
|
|
15
16
|
|
|
16
17
|
const band = tv({
|
|
17
18
|
slots: {
|
|
@@ -134,7 +135,7 @@ const hasCopy = Boolean(heading || eyebrow || lead || actions.length)
|
|
|
134
135
|
<ul class={s.list({ class: measuredGrid(measured) })}>
|
|
135
136
|
{items.map(item => (
|
|
136
137
|
<li class={s.item()}>
|
|
137
|
-
{item.media && <div class={s.itemMedia()}><KitImage image={{ src: item.media, alt: item.mediaAlt ?? '' }} sizes="(min-width: 1024px) 33vw, (min-width: 640px) 50vw, 100vw" class="aspect-[3/2] w-full object-cover" /></div>}
|
|
138
|
+
{item.media && <div class={s.itemMedia()}><KitImage image={cardImage({ src: item.media, alt: item.mediaAlt ?? '' }, item.title, !!item.href && !item.linkLabel)!} sizes="(min-width: 1024px) 33vw, (min-width: 640px) 50vw, 100vw" class="aspect-[3/2] w-full object-cover" /></div>}
|
|
138
139
|
{item.icon && <KitImage image={{ src: item.icon, alt: '' }} class="h-10 w-10 object-contain" />}
|
|
139
140
|
{item.value && <p class={s.itemValue()}>{item.value}</p>}
|
|
140
141
|
{item.title && (
|
|
@@ -7,6 +7,7 @@ import Section, { type Tone } from '../_shared/Section.astro'
|
|
|
7
7
|
import { instanceId } from '../_shared/instance-id'
|
|
8
8
|
import { measuredGrid } from '../_shared/measured'
|
|
9
9
|
import type { MeasuredStyle } from '../_shared/types'
|
|
10
|
+
import { cardImage } from '../_shared/card-image'
|
|
10
11
|
import { imageOf, type ImageSource } from '../_shared/image'
|
|
11
12
|
import { safeHref } from '../_shared/safe-url'
|
|
12
13
|
import { muted, mutedReset } from '../_shared/muted'
|
|
@@ -130,7 +131,7 @@ const number = (i: number) => String(i + 1).padStart(2, '0')
|
|
|
130
131
|
{item.text ? <p class={r.text()} data-kit-text>{item.text}</p> : <span aria-hidden="true" />}
|
|
131
132
|
{(imageOf(item.image, item.imageAlt) || (item.href && item.linkLabel)) && (
|
|
132
133
|
<div class={r.end({ class: imageOf(item.image, item.imageAlt) ? 'md:self-start' : undefined })}>
|
|
133
|
-
{imageOf(item.image, item.imageAlt) && <div class={r.media()}><KitImage image={imageOf(item.image, item.imageAlt)!} sizes="(min-width: 768px) 12rem, 100vw" class="aspect-[3/2] w-full object-cover" /></div>}
|
|
134
|
+
{imageOf(item.image, item.imageAlt) && <div class={r.media()}><KitImage image={cardImage(imageOf(item.image, item.imageAlt), item.title, !!item.href && !item.linkLabel)!} sizes="(min-width: 768px) 12rem, 100vw" class="aspect-[3/2] w-full object-cover" /></div>}
|
|
134
135
|
{item.href && item.linkLabel && <a href={safeHref(item.href)} class={r.link()}>{item.linkLabel}</a>}
|
|
135
136
|
</div>
|
|
136
137
|
)}
|
|
@@ -141,7 +142,7 @@ const number = (i: number) => String(i + 1).padStart(2, '0')
|
|
|
141
142
|
<ul class={list({ class: measuredGrid(measured) })}>
|
|
142
143
|
{items.map(item => (
|
|
143
144
|
<li class={card()}>
|
|
144
|
-
{imageOf(item.image, item.imageAlt) && <div class={media()}><KitImage image={imageOf(item.image, item.imageAlt)!} sizes="(min-width: 1024px) 33vw, (min-width: 640px) 50vw, 100vw" class="aspect-[3/2] w-full object-cover transition duration-300 group-hover:scale-[1.02]" /></div>}
|
|
145
|
+
{imageOf(item.image, item.imageAlt) && <div class={media()}><KitImage image={cardImage(imageOf(item.image, item.imageAlt), item.title, !!item.href && !item.linkLabel)!} sizes="(min-width: 1024px) 33vw, (min-width: 640px) 50vw, 100vw" class="aspect-[3/2] w-full object-cover transition duration-300 group-hover:scale-[1.02]" /></div>}
|
|
145
146
|
{item.icon && <KitImage image={{ src: item.icon, alt: '' }} class="h-10 w-10 object-contain" />}
|
|
146
147
|
{item.eyebrow && <p class="text-sm font-medium text-accent-text">{item.eyebrow}</p>}
|
|
147
148
|
<CardHeading class={cardTitle()}>
|
package/components/cta/meta.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"description": "A call to action: a short heading, an optional line of text and one or two buttons — across the whole section, or as a card inside it.",
|
|
5
5
|
"category": "section",
|
|
6
6
|
"uses": [],
|
|
7
|
-
"shared": ["Button.astro", "KitImage.astro", "Section.astro", "instance-id.ts", "measured.ts", "muted.ts", "safe-url.ts", "types.ts"],
|
|
7
|
+
"shared": ["Button.astro", "KitImage.astro", "Section.astro", "image-widths.ts", "instance-id.ts", "measured.ts", "muted.ts", "safe-url.ts", "types.ts"],
|
|
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." },
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"description": "A video, map or player from another site (YouTube, Vimeo, Google Maps, OpenStreetMap, Spotify, SoundCloud, Calendly, Typeform, Google Forms, Loom, Wistia), loaded only on click: until then a poster link to the provider's page, with no iframe, no third-party request and no cookie (YouTube through youtube-nocookie.com). Unknown providers are never framed; they stay a plain link.",
|
|
5
5
|
"category": "content",
|
|
6
6
|
"uses": [],
|
|
7
|
-
"shared": ["KitImage.astro", "embed-frame.ts", "embed-target.ts", "safe-url.ts", "types.ts"],
|
|
7
|
+
"shared": ["KitImage.astro", "embed-frame.ts", "embed-target.ts", "image-widths.ts", "safe-url.ts", "types.ts"],
|
|
8
8
|
"variants": {
|
|
9
9
|
"aspect": { "options": ["auto", "video", "portrait", "square", "map"], "default": "auto", "description": "auto: 16:9 for videos, 9:16 for YouTube Shorts, 4:3 (16:9 wide) for maps, a fixed strip for audio players. The others force a ratio." },
|
|
10
10
|
"width": { "options": ["prose", "page"], "default": "prose", "description": "prose: as wide as the reading column. page: the full page frame." }
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"description": "Site header: brand (logo and/or name), navigation and an optional call-to-action button.",
|
|
5
5
|
"category": "chrome",
|
|
6
6
|
"uses": ["nav", "search-box"],
|
|
7
|
-
"shared": ["Button.astro", "KitImage.astro", "instance-id.ts", "muted.ts", "safe-url.ts", "types.ts"],
|
|
7
|
+
"shared": ["Button.astro", "KitImage.astro", "image-widths.ts", "instance-id.ts", "muted.ts", "safe-url.ts", "types.ts"],
|
|
8
8
|
"variants": {
|
|
9
9
|
"layout": { "options": ["split", "centered", "brand-left"], "default": "split", "description": "split: brand left, navigation right. centered: brand above centred navigation. brand-left: navigation right after the brand." },
|
|
10
10
|
"tone": { "options": ["default", "inverse", "transparent"], "default": "default", "description": "Surface with a bottom border, dark (ink) background, or transparent over the hero that follows." },
|
|
@@ -8,6 +8,7 @@ import { tv } from 'tailwind-variants'
|
|
|
8
8
|
import KitImage from '../_shared/KitImage.astro'
|
|
9
9
|
import type { ImageInput, LinkInput } from '../_shared/types'
|
|
10
10
|
import { safeHref } from '../_shared/safe-url'
|
|
11
|
+
import { cardImage } from '../_shared/card-image'
|
|
11
12
|
|
|
12
13
|
const card = tv({
|
|
13
14
|
slots: {
|
|
@@ -82,7 +83,7 @@ const Heading = `h${headingLevel}` as 'h2' | 'h3'
|
|
|
82
83
|
<article class={slots.root()}>
|
|
83
84
|
{image && layout !== 'minimal' && (
|
|
84
85
|
<div class={slots.media()}>
|
|
85
|
-
<KitImage image={image} sizes={sizes ?? (layout === 'horizontal' ? '256px' : '(min-width: 1024px) 360px, 100vw')} class:list={['w-full object-cover transition duration-300 group-hover:scale-[1.02]', !imageStyle && 'aspect-[16/9]']} style={imageStyle} />
|
|
86
|
+
<KitImage image={cardImage(image, title, !!href)!} sizes={sizes ?? (layout === 'horizontal' ? '256px' : '(min-width: 1024px) 360px, 100vw')} class:list={['w-full object-cover transition duration-300 group-hover:scale-[1.02]', !imageStyle && 'aspect-[16/9]']} style={imageStyle} />
|
|
86
87
|
</div>
|
|
87
88
|
)}
|
|
88
89
|
<div class={slots.body()}>
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"description": "One post in a list (index, archive, related posts): image, category, date, title, excerpt, author. The whole card links to the post.",
|
|
5
5
|
"category": "content",
|
|
6
6
|
"uses": [],
|
|
7
|
-
"shared": ["KitImage.astro", "safe-url.ts", "types.ts"],
|
|
7
|
+
"shared": ["KitImage.astro", "card-image.ts", "image-widths.ts", "safe-url.ts", "types.ts"],
|
|
8
8
|
"variants": {
|
|
9
9
|
"layout": { "options": ["vertical", "horizontal", "minimal"], "default": "vertical", "description": "Image on top, image beside the text, or text only with a divider." },
|
|
10
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." },
|
|
@@ -132,6 +132,7 @@ const Root = framed ? 'section' : 'div'
|
|
|
132
132
|
const arrow = 'absolute top-1/2 z-10 hidden size-11 -translate-y-1/2 items-center justify-center rounded-full bg-surface/90 text-ink shadow-md ring-1 ring-line hover:bg-surface disabled:opacity-40 sm:group-data-[ready]:flex'
|
|
133
133
|
// A text card has no picture for the arrows to sit on: over it they would cover its words. A carousel with any text
|
|
134
134
|
// card (all of them, or mixed with pictures) has its arrows under the row, either side of the dots.
|
|
135
|
+
// The dots wrap (one 24px target per slide): 14 of them between the arrows outgrew a 390 phone and pushed both off it.
|
|
135
136
|
const cards = slides.some(slide => !slide.image && !slide.video && !slide.player)
|
|
136
137
|
// A video from another site is a link to its page, so without JavaScript it opens there; its frame keeps the image's
|
|
137
138
|
// ratio, so the player that replaces the link moves nothing.
|
|
@@ -172,7 +173,7 @@ const under = 'flex size-11 shrink-0 items-center justify-center rounded-full bg
|
|
|
172
173
|
? (
|
|
173
174
|
<div class="mt-4 hidden items-center justify-center gap-3 group-data-[ready]:flex">
|
|
174
175
|
<button type="button" class={under} aria-label={prevLabel} aria-controls={trackId} data-prev><svg aria-hidden="true" viewBox="0 0 24 24" class="size-5 rtl:-scale-x-100" fill="none" stroke="currentColor" stroke-width="2"><path d="M15 18l-6-6 6-6" /></svg></button>
|
|
175
|
-
<div class="flex justify-center" data-dots></div>
|
|
176
|
+
<div class="flex min-w-0 flex-wrap justify-center" data-dots></div>
|
|
176
177
|
<button type="button" class={under} aria-label={nextLabel} aria-controls={trackId} data-next><svg aria-hidden="true" viewBox="0 0 24 24" class="size-5 rtl:-scale-x-100" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 6l6 6-6 6" /></svg></button>
|
|
177
178
|
</div>
|
|
178
179
|
)
|
|
@@ -180,7 +181,7 @@ const under = 'flex size-11 shrink-0 items-center justify-center rounded-full bg
|
|
|
180
181
|
<>
|
|
181
182
|
<button type="button" class:list={[arrow, 'start-6 md:start-10']} aria-label={prevLabel} aria-controls={trackId} data-prev><svg aria-hidden="true" viewBox="0 0 24 24" class="size-5 rtl:-scale-x-100" fill="none" stroke="currentColor" stroke-width="2"><path d="M15 18l-6-6 6-6" /></svg></button>
|
|
182
183
|
<button type="button" class:list={[arrow, 'end-6 md:end-10']} aria-label={nextLabel} aria-controls={trackId} data-next><svg aria-hidden="true" viewBox="0 0 24 24" class="size-5 rtl:-scale-x-100" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 6l6 6-6 6" /></svg></button>
|
|
183
|
-
<div class="mt-4 hidden justify-center group-data-[ready]:flex" data-dots></div>
|
|
184
|
+
<div class="mt-4 hidden flex-wrap justify-center group-data-[ready]:flex" data-dots></div>
|
|
184
185
|
</>
|
|
185
186
|
))}
|
|
186
187
|
</Root>
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
{ "name": "three-square", "props": { "label": "Team", "perView": "3", "aspect": "square", "slides": [{ "image": { "src": "/kit-fixtures/avatar-1.svg", "alt": "Mira", "width": 96, "height": 96 } }, { "image": { "src": "/kit-fixtures/avatar-2.svg", "alt": "Jon", "width": 96, "height": 96 } }, { "image": { "src": "/kit-fixtures/avatar-3.svg", "alt": "Lea", "width": 96, "height": 96 } }, { "image": { "src": "/kit-fixtures/photo-1.svg", "alt": "Studio", "width": 1200, "height": 800 } }] } },
|
|
5
5
|
{ "name": "five-measured", "props": { "label": "Five slides", "id": "five", "eyebrow": "Recent work", "heading": "Five projects", "intro": "A few of the sites we moved this year.", "actions": [{ "label": "See all work", "href": "/work/" }], "slides": [{ "image": { "src": "/kit-fixtures/photo-1.svg", "alt": "View 1", "width": 1200, "height": 800 }, "title": "Slide 1 title" }, { "image": { "src": "/kit-fixtures/photo-2.svg", "alt": "View 2", "width": 1200, "height": 800 }, "title": "Slide 2 title" }, { "image": { "src": "/kit-fixtures/photo-3.svg", "alt": "View 3", "width": 1200, "height": 800 }, "title": "Slide 3 title" }, { "image": { "src": "/kit-fixtures/photo-4.svg", "alt": "View 4", "width": 1200, "height": 800 }, "title": "Slide 4 title" }, { "image": { "src": "/kit-fixtures/photo-5.svg", "alt": "View 5", "width": 1200, "height": 800 }, "title": "Slide 5 title" }], "measured": { "padY": 24, "columns": { "390": 1, "768": 2, "1280": 3 }, "gap": 20 } } },
|
|
6
6
|
{ "name": "text-cards", "props": { "label": "What clients say", "perView": "3", "heading": "Testimonials", "slides": [{ "title": "Ada Lovelace", "text": "They moved our whole site in a week, and nothing broke." }, { "title": "Grace Hopper", "text": "Every page is ours to edit now, without a developer." }, { "title": "Alan Turing", "text": "The new site is faster, and it looks the same.", "ctaLabel": "Read the story", "ctaHref": "/stories/" }] } },
|
|
7
|
+
{ "name": "text-cards-many", "props": { "label": "What clients say", "heading": "Fourteen reviews", "slides": [{ "title": "Ada", "text": "Review 1 of fourteen." }, { "title": "Grace", "text": "Review 2 of fourteen." }, { "title": "Alan", "text": "Review 3 of fourteen." }, { "title": "Edsger", "text": "Review 4 of fourteen." }, { "title": "Barbara", "text": "Review 5 of fourteen." }, { "title": "Donald", "text": "Review 6 of fourteen." }, { "title": "Margaret", "text": "Review 7 of fourteen." }, { "title": "Ken", "text": "Review 8 of fourteen." }, { "title": "Frances", "text": "Review 9 of fourteen." }, { "title": "John", "text": "Review 10 of fourteen." }, { "title": "Radia", "text": "Review 11 of fourteen." }, { "title": "Tim", "text": "Review 12 of fourteen." }, { "title": "Hedy", "text": "Review 13 of fourteen." }, { "title": "Claude", "text": "Review 14 of fourteen." }] } },
|
|
7
8
|
{ "name": "video-slides", "props": { "label": "Feature tour", "heading": "See it work", "slides": [{ "video": "/kit-fixtures/clip-1.mp4", "image": "/kit-fixtures/photo-1.svg", "imageAlt": "", "title": "Import" }, { "video": "/kit-fixtures/clip-2.mp4", "title": "Edit" }, { "video": "/kit-fixtures/clip-3.mp4", "image": "/kit-fixtures/photo-3.svg", "imageAlt": "", "title": "Publish", "text": "One click, and the site is live." }] } },
|
|
8
9
|
{ "name": "mixed-cards", "props": { "label": "Stories", "slides": [{ "title": "Ada Lovelace", "text": "They moved our whole site in a week, and nothing broke." }, { "image": "/kit-fixtures/photo-2.svg", "imageAlt": "Green gradient", "title": "Harbor & Co" }, { "title": "Grace Hopper", "text": "Every page is ours to edit now, without a developer." }] } },
|
|
9
10
|
{"name": "video-embed", "props": {"label": "Studio videos", "id": "videos", "aspect": "standard", "perView": "2", "slides": [{"embed": "youtube:aqz-KE-bpKQ", "image": {"src": "/kit-fixtures/photo-1.svg", "alt": "Blue gradient", "width": 1200, "height": 800}, "title": "Studio tour"}, {"embed": "https://vimeo.com/76979871", "title": "Behind the scenes"}, {"embed": "youtube:aqz-KE-bpKQ", "image": "https://i.ytimg.com/vi/aqz-KE-bpKQ/hqdefault.jpg", "title": "A provider thumbnail is never loaded"}, {"image": {"src": "/kit-fixtures/photo-3.svg", "alt": "Orange gradient", "width": 1200, "height": 800}, "title": "Field Notes"}]}}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"description": "A carousel of images, videos (a file, or a YouTube/Vimeo link loaded only on click) or text cards, with optional title, text and button per slide. Without JavaScript it is a snapping horizontal scroller; Embla adds paging, arrows and dots.",
|
|
5
5
|
"category": "section",
|
|
6
6
|
"uses": [],
|
|
7
|
-
"shared": ["Button.astro", "KitImage.astro", "Section.astro", "SectionHeader.astro", "embed-frame.ts", "embed-target.ts", "image.ts", "instance-id.ts", "measured.ts", "muted.ts", "safe-url.ts", "types.ts"],
|
|
7
|
+
"shared": ["Button.astro", "KitImage.astro", "Section.astro", "SectionHeader.astro", "embed-frame.ts", "embed-target.ts", "image-widths.ts", "image.ts", "instance-id.ts", "measured.ts", "muted.ts", "safe-url.ts", "types.ts"],
|
|
8
8
|
"variants": {
|
|
9
9
|
"perView": { "options": ["1", "2", "3"], "default": "1", "description": "Slides visible at once on wide screens." },
|
|
10
10
|
"aspect": { "options": ["wide", "standard", "square"], "default": "wide", "description": "Image aspect ratio: 21:9, 3:2 or 1:1." },
|
|
@@ -7,6 +7,7 @@ import Section from '../_shared/Section.astro'
|
|
|
7
7
|
import { instanceId } from '../_shared/instance-id'
|
|
8
8
|
import { measuredGrid } from '../_shared/measured'
|
|
9
9
|
import type { MeasuredStyle } from '../_shared/types'
|
|
10
|
+
import { cardImage } from '../_shared/card-image'
|
|
10
11
|
import { imageOf, type ImageSource } from '../_shared/image'
|
|
11
12
|
import { profile } from './team'
|
|
12
13
|
import { safeHref } from '../_shared/safe-url'
|
|
@@ -109,7 +110,7 @@ const NameHeading = heading ? 'h3' : 'h2'
|
|
|
109
110
|
{items.map(member => (
|
|
110
111
|
<li class={s.member()}>
|
|
111
112
|
{/* The portrait's text defaults to the name: a photo of a named person is not decoration. */}
|
|
112
|
-
{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>}
|
|
113
|
+
{imageOf(member.image, member.imageAlt ?? member.name) && <div class={`${s.portrait()} kit-portrait`}><KitImage image={cardImage(imageOf(member.image, member.imageAlt ?? member.name), member.name, !!member.href)!} sizes="(min-width: 1024px) 25vw, (min-width: 640px) 50vw, 100vw" class="aspect-square w-full object-cover" /></div>}
|
|
113
114
|
<NameHeading class={s.name()}>
|
|
114
115
|
{member.href
|
|
115
116
|
? <a href={safeHref(member.href)} class="text-current no-underline after:absolute after:inset-0 after:content-[''] hover:text-accent-text">{member.name}</a>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contentrain/astro-kit",
|
|
3
|
-
"version": "0.46.
|
|
3
|
+
"version": "0.46.3",
|
|
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",
|