@contentrain/astro-kit 0.29.3 → 0.30.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
|
@@ -2427,6 +2427,18 @@
|
|
|
2427
2427
|
"type": "boolean",
|
|
2428
2428
|
"default": false,
|
|
2429
2429
|
"description": "The title in the site's link colour (--color-link), as themes that colour every link do."
|
|
2430
|
+
},
|
|
2431
|
+
"sizes": {
|
|
2432
|
+
"type": "string",
|
|
2433
|
+
"description": "The image's `sizes` hint; absent, the card's own guess. A list passes the slot its columns give."
|
|
2434
|
+
},
|
|
2435
|
+
"imageHeight": {
|
|
2436
|
+
"type": "string",
|
|
2437
|
+
"description": "The image block's height as the source sets it (a CSS length or clamp()), cropped to fit; wins over imageAspect."
|
|
2438
|
+
},
|
|
2439
|
+
"imageAspect": {
|
|
2440
|
+
"type": "string",
|
|
2441
|
+
"description": "The image block's aspect ratio (16 / 9) where the source sets an aspect ratio; absent, 16 / 9."
|
|
2430
2442
|
}
|
|
2431
2443
|
},
|
|
2432
2444
|
"slots": {},
|
|
@@ -23,6 +23,8 @@ interface Props {
|
|
|
23
23
|
/** Rendered width in the layout, for srcset selection. */
|
|
24
24
|
sizes?: string | undefined
|
|
25
25
|
class?: string | undefined
|
|
26
|
+
/** Inline style, for a size the source sets (a card image's height). */
|
|
27
|
+
style?: string | undefined
|
|
26
28
|
/** The page's largest image: load it first. */
|
|
27
29
|
priority?: boolean | undefined
|
|
28
30
|
}
|
|
@@ -30,7 +32,7 @@ interface Props {
|
|
|
30
32
|
// Raster files under public/, loaded only when a page shows one.
|
|
31
33
|
const LOCAL = import.meta.glob<ImageMetadata>('/public/**/*.{avif,gif,jpeg,jpg,png,tif,tiff,webp,AVIF,GIF,JPEG,JPG,PNG,TIF,TIFF,WEBP}', { import: 'default' })
|
|
32
34
|
|
|
33
|
-
const { image: given, sizes = '100vw', class: className, priority = false } = Astro.props
|
|
35
|
+
const { image: given, sizes = '100vw', class: className, style, priority = false } = Astro.props
|
|
34
36
|
// Content stores an address; the size the migration measured comes from the site's media table.
|
|
35
37
|
const measured = given.width && given.height ? undefined : mediaSize(given.src)
|
|
36
38
|
const image: ImageInput = measured ? { ...given, ...measured } : given
|
|
@@ -48,7 +50,7 @@ const STEPS = [320, 640, 960, 1280, 1600, 1920, 2560]
|
|
|
48
50
|
const widthsUpTo = (max: number) => [...STEPS.filter(step => step < max), max]
|
|
49
51
|
---
|
|
50
52
|
{local
|
|
51
|
-
? <Image src={local} alt={image.alt ?? ''} {...size} widths={widthsUpTo(image.width ?? local.width)} sizes={sizes} class={className} loading={loading} fetchpriority={fetchpriority} />
|
|
53
|
+
? <Image src={local} alt={image.alt ?? ''} {...size} widths={widthsUpTo(image.width ?? local.width)} sizes={sizes} class={className} style={style} loading={loading} fetchpriority={fetchpriority} />
|
|
52
54
|
: image.width && image.height && path === undefined
|
|
53
|
-
? <Image src={image.src} alt={image.alt ?? ''} width={image.width} height={image.height} widths={widthsUpTo(image.width)} sizes={sizes} class={className} loading={loading} fetchpriority={fetchpriority} />
|
|
54
|
-
: <img src={image.src} alt={image.alt ?? ''} width={image.width} height={image.height} class={className} loading={loading} fetchpriority={fetchpriority} decoding="async" />}
|
|
55
|
+
? <Image src={image.src} alt={image.alt ?? ''} width={image.width} height={image.height} widths={widthsUpTo(image.width)} sizes={sizes} class={className} style={style} loading={loading} fetchpriority={fetchpriority} />
|
|
56
|
+
: <img src={image.src} alt={image.alt ?? ''} width={image.width} height={image.height} class={className} style={style} loading={loading} fetchpriority={fetchpriority} decoding="async" />}
|
package/components/nav/Nav.astro
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
// Site navigation. Wide screens get an inline list whose submenus open on
|
|
3
|
-
// hover and keyboard focus; narrow screens get a toggle that opens the menu as
|
|
3
|
+
// hover and keyboard focus (a chevron button marks the ones that have one); narrow screens get a toggle that opens the menu as
|
|
4
4
|
// a native popover (drawer) or a <details> dropdown. No JavaScript.
|
|
5
5
|
import { tv } from 'tailwind-variants'
|
|
6
6
|
import type { NavItem } from '../_shared/types'
|
|
@@ -57,11 +57,17 @@ const drawer = `${id}-drawer`
|
|
|
57
57
|
<li class="group relative">
|
|
58
58
|
<a href={item.href} aria-current={item.href === current ? 'page' : undefined} target={item.newTab ? '_blank' : undefined} rel={item.newTab ? 'noopener' : undefined} class={link()}>{item.label}</a>
|
|
59
59
|
{item.children && item.children.length > 0 && (
|
|
60
|
+
<>
|
|
61
|
+
{/* The chevron is the submenu's handle: focusing it (a tap on a touch screen, Tab on a keyboard) opens the submenu, with no script. */}
|
|
62
|
+
<button type="button" aria-haspopup="true" aria-label={item.label} class="ms-1 inline-flex cursor-pointer items-center align-middle opacity-80 hover:opacity-100">
|
|
63
|
+
<svg aria-hidden="true" viewBox="0 0 24 24" class="size-4" fill="none" stroke="currentColor" stroke-width="2"><path d="m6 9 6 6 6-6" /></svg>
|
|
64
|
+
</button>
|
|
60
65
|
<ul class={sub()}>
|
|
61
66
|
{item.children.map(child => (
|
|
62
67
|
<li><a href={child.href} aria-current={child.href === current ? 'page' : undefined} class="block rounded-md px-3 py-2 text-sm text-ink no-underline hover:bg-surface-muted">{child.label}</a></li>
|
|
63
68
|
))}
|
|
64
69
|
</ul>
|
|
70
|
+
</>
|
|
65
71
|
)}
|
|
66
72
|
</li>
|
|
67
73
|
))}
|
|
@@ -52,19 +52,26 @@ interface Props {
|
|
|
52
52
|
byline?: 'inline' | 'below' | undefined
|
|
53
53
|
/** The title in the site's link colour, as themes that colour every link do (Hello Elementor). */
|
|
54
54
|
linkTitle?: boolean | undefined
|
|
55
|
+
/** The image's `sizes` hint; absent, the card's own guess. A list passes the slot its columns give. */
|
|
56
|
+
sizes?: string | undefined
|
|
57
|
+
/** The image block's height as the source sets it (a CSS length or `clamp()`), cropped to fit; wins over `imageAspect`. */
|
|
58
|
+
imageHeight?: string | undefined
|
|
59
|
+
/** The image block's aspect ratio (`16 / 9`) where the source sets an aspect ratio; absent, 16 / 9. */
|
|
60
|
+
imageAspect?: string | undefined
|
|
55
61
|
}
|
|
56
62
|
|
|
57
|
-
const { title, href, excerpt, date: rawDate, locale = 'en', image, category, author, headingLevel = 2, layout, surface, byline, linkTitle } = Astro.props
|
|
63
|
+
const { title, href, excerpt, date: rawDate, locale = 'en', image, category, author, headingLevel = 2, layout, surface, byline, linkTitle, sizes, imageHeight, imageAspect } = Astro.props
|
|
58
64
|
const date = rawDate === undefined ? undefined : new Date(rawDate)
|
|
59
65
|
const slots = card({ layout, surface, byline })
|
|
60
66
|
const below = byline === 'below'
|
|
61
67
|
const shown = date?.toLocaleDateString(locale, { dateStyle: 'long', timeZone: 'UTC' })
|
|
68
|
+
const imageStyle = imageHeight ? `height:${imageHeight}` : imageAspect ? `aspect-ratio:${imageAspect}` : undefined
|
|
62
69
|
const Heading = `h${headingLevel}` as 'h2' | 'h3'
|
|
63
70
|
---
|
|
64
71
|
<article class={slots.root()}>
|
|
65
72
|
{image && layout !== 'minimal' && (
|
|
66
73
|
<div class={slots.media()}>
|
|
67
|
-
<KitImage image={image} sizes={layout === 'horizontal' ? '256px' : '(min-width: 1024px) 360px, 100vw'} class=
|
|
74
|
+
<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} />
|
|
68
75
|
</div>
|
|
69
76
|
)}
|
|
70
77
|
<div class={slots.body()}>
|
|
@@ -20,7 +20,10 @@
|
|
|
20
20
|
"category": { "type": "object", "fields": { "label": { "type": "string", "required": true }, "href": { "type": "url", "required": true } }, "description": "Primary category and its archive." },
|
|
21
21
|
"author": { "type": "string", "description": "Author name (posts.author → authors.name)." },
|
|
22
22
|
"headingLevel": { "type": "integer", "min": 2, "max": 3, "default": 2, "description": "Title heading level in the page outline." },
|
|
23
|
-
"linkTitle": { "type": "boolean", "default": false, "description": "The title in the site's link colour (--color-link), as themes that colour every link do." }
|
|
23
|
+
"linkTitle": { "type": "boolean", "default": false, "description": "The title in the site's link colour (--color-link), as themes that colour every link do." },
|
|
24
|
+
"sizes": { "type": "string", "description": "The image's `sizes` hint; absent, the card's own guess. A list passes the slot its columns give." },
|
|
25
|
+
"imageHeight": { "type": "string", "description": "The image block's height as the source sets it (a CSS length or clamp()), cropped to fit; wins over imageAspect." },
|
|
26
|
+
"imageAspect": { "type": "string", "description": "The image block's aspect ratio (16 / 9) where the source sets an aspect ratio; absent, 16 / 9." }
|
|
24
27
|
},
|
|
25
28
|
"slots": {},
|
|
26
29
|
"js": "none",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contentrain/astro-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.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.58.0"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@types/node": "^22.10.0",
|