Card and Image
These low-level building blocks are useful when composing a custom section. Card supplies the same article used by the two collection components; Image supplies shared responsive and theme-aware image rendering.
<script lang="ts">
import { Card, Image, type CardItem } from '@page-sections/svelte';
import '@page-sections/svelte/styles.css';
const item: CardItem = {
id: 'guide',
title: 'A first garden',
href: '/guide',
description: 'Start small.'
};
</script>
<div class="ps-shell">
<Card {item} variant="editorial" mediaAspect="4/3">
{#snippet media(card)}
<Image
image={{
src: '/images/garden.jpg',
alt: `Cover for ${card.title}`,
aspect: '4/3',
width: 1200,
height: 900
}}
/>
{/snippet}
{#snippet children(card)}<small>From the garden library: {card.title}</small
>{/snippet}
</Card>
</div> Card accepts tone="standard" (default) or "muted". CardGridSection and CarouselSection forward their cardTone option to this prop. The selected surface follows the theme; custom surface
snippets retain control over their own paint.
Card props
Generic T extends CardItem; required item. Optional cardSize (m), variant
(catalog), mediaAspect (3/2; also 4/3 or 1/1), mediaFit (contain for catalog, cover for
editorial), reserveEyebrow (otherwise follows the item), textAlign (start/center),
headingLevel (inherited, standalone h2), media(item), surface(item), children(item) and
onready(parts). Children are editorial-only. Card forwards HTMLElement attributes, including
class and event callbacks; card identity/accessibility attributes remain component-owned.
Complete CardItem model
| Field | Type | Default / behavior |
|---|---|---|
id | string, required | Content identity |
title | string, required | Inherited heading; full-card link for catalog, title link for editorial |
itemKey | string | Optional occurrence key; collection keys use itemKey then id |
href | string | Optional native link, passed through safeHref |
eyebrow | string | Optional text before title |
description | string | Optional description paragraph |
subtitle | string | Fallback when description is nullish; an empty description suppresses it |
image | ImageData | Optional default media |
footer | InlineText | Editorial footer |
actions | ContentAction[] | Independent editorial actions |
Catalog height is the selected media frame plus an independent text area. S/M reserve two title lines; L reserves three. Eyebrows add a separate line (hidden for S); reserveEyebrow keeps an empty line when aligning cards with missing labels. Collections compute this flag from the full list. Text padding is 12px on each side. Catalog does not render descriptions, children, footers or actions; use editorial for those. Editorial grows with content and fills its grid slot.
CardMediaAspect is 3/2, 4/3 or 1/1. CardMediaFit is contain (8px media inset, whole image) or cover (no inset, cropped image). The frame ratio and image fit are independent. Card media props override ImageData.aspect/objectFit inside the slot; standalone Image behavior is unchanged. Missing catalog media keeps its frame; editorial cards without media omit it.
onready?: (parts: CardElements<T>) => void | (() => void) supplies
item, root, surface, media, copy and title elements after mount. Its returned cleanup runs
when the item/callback changes or the card unmounts. Keyed moves and media-format changes
preserve the same occurrence. The media element is the inner fitting rectangle; media-frame
is the outer aspect-ratio box. Observe element resizing when fitting external geometry:
onready is an attachment hook, not a resize notification. data-card-id contains itemKey
?? id; data-ps-card-part names each measured part. Custom catalog snippets must not introduce
interactive descendants inside a linked card. These hooks support external enhancements without
a rendering-engine dependency.
Image props and complete ImageData model
Required image: ImageData; optional eager: boolean defaults to false.
No children or attribute-forwarding props.
| ImageData field | Type | Default / behavior |
|---|---|---|
src | string, required | Fallback image URL |
alt | string, required | Alternative text; empty for decorative images |
srcDark | string | Optional dark source in picture |
width, height | number | Optional intrinsic dimensions forwarded to img |
aspect | ImageAspect | Unset/none: no forced ratio; 4/3, 16/9, 2/1 or square |
objectFit | 'cover' or 'contain' | cover |
srcset, sizes | string | Optional responsive attributes on the fallback img |
ImageAspect is 'none', '4/3', '16/9', '2/1', 'square'. The picture fills its available width; img height
is auto, uses the selected ratio and --ps-radius. Loading is lazy unless eager;
decoding is async. HeroSection uses eager mode, while cards and ImageSection use the
default.
An ancestor PageShell's explicit mode determines whether srcDark is selected. Without one,
the source uses prefers-color-scheme: dark. A data-ps-theme attribute
alone changes CSS but does not provide PageShell's Svelte image-mode context. Explicit light mode
suppresses the dark source.
Accessibility and SSR
Use meaningful alt text, avoid repeating a decorative card cover in speech, and supply width/height to reduce shifts. Native picture selection and loading work in server-rendered HTML. The library does not resize, upload or generate responsive assets.
Related: ImageSection, CardGridSection, Themes and Styling.