---
/**
* FeatureAlternating: media and copy in rows that swap sides, for features
* that each deserve a picture. Rows stack on narrow widths.
*
* @example
* <FeatureAlternating items={[
* { kicker: "01", title: "Mapped from the mill", body: "...",
* image: { src: "/map.webp", alt: "Contour map" }, link: { label: "See the mills", href: "/mills" } },
* ]} />
*/
interface Picture {
src: string;
alt: string;
width?: number;
height?: number;
}
interface Link {
label: string;
href: string;
}
interface Item {
title: string;
body: string;
image: Picture;
/** Short label above the row title, e.g. a step number. */
kicker?: string;
link?: Link;
}
interface Props {
title?: string;
accent?: string;
items: Item[];
as?: "h2" | "h3";
class?: string;
}
const { title, accent, items, as: Heading = "h2", class: className } = Astro.props;
const RowHeading = title ? "h3" : Heading;
---
<section class:list={["vs-feature-alt", className]}>
<div class="vs-feature-alt__inner">
{title && <Heading class="vs-display vs-feature-alt__title">{title}{accent && <> <em>{accent}</em></>}</Heading>}
<div class="vs-feature-alt__rows">
{
items.map((item) => (
<article class="vs-feature-alt__row">
<div class="vs-feature-alt__media">
<img src={item.image.src} alt={item.image.alt} width={item.image.width} height={item.image.height} loading="lazy" />
</div>
<div class="vs-feature-alt__copy">
{item.kicker && <p class="vs-feature-alt__kicker">{item.kicker}</p>}
<RowHeading class="vs-display vs-feature-alt__name">{item.title}</RowHeading>
<p class="vs-feature-alt__body">{item.body}</p>
{item.link && (
<a class="vs-feature-alt__link" href={item.link.href}>
{item.link.label}
</a>
)}
</div>
</article>
))
}
</div>
</div>
</section>
<style>
.vs-feature-alt {
container-type: inline-size;
background: var(--vs-page);
color: var(--vs-ink);
font-family: var(--vs-font-sans);
font-stretch: var(--vs-font-stretch);
}
.vs-feature-alt__inner {
max-width: var(--vs-container);
margin-inline: auto;
padding: var(--vs-section-y) var(--vs-gutter);
}
.vs-feature-alt__title {
margin-bottom: clamp(32px, 6cqi, 72px);
font-size: clamp(2rem, 6.5cqi, 4.25rem);
}
.vs-feature-alt__rows {
display: grid;
gap: clamp(48px, 9cqi, 112px);
}
.vs-feature-alt__row {
display: grid;
gap: clamp(24px, 5cqi, 64px);
align-items: center;
}
.vs-feature-alt__media {
aspect-ratio: 5 / 4;
overflow: hidden;
border: var(--vs-border-w) solid var(--vs-line);
border-radius: var(--vs-radius-card);
background: var(--vs-canvas);
box-shadow: var(--vs-pop-shadow, none);
}
.vs-feature-alt__media img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
.vs-feature-alt__kicker {
margin: 0 0 14px;
font-size: 0.8125rem;
font-weight: 600;
color: var(--vs-accent-ink);
}
.vs-feature-alt__name {
font-size: clamp(1.75rem, 4.5cqi, 3rem);
}
.vs-feature-alt__body {
max-width: 28rem;
margin: 1rem 0 0;
font-size: 1rem;
line-height: 1.6;
color: var(--vs-ink-2);
}
.vs-feature-alt__link {
display: inline-block;
margin-top: 1.5rem;
padding-bottom: 2px;
border-bottom: calc(var(--vs-border-w) + 1px) solid var(--vs-accent);
font-size: 0.9375rem;
font-weight: 600;
color: var(--vs-ink);
text-decoration: none;
}
.vs-feature-alt__link:focus-visible {
outline: none;
box-shadow: var(--vs-focus-ring);
}
@container (min-width: 760px) {
.vs-feature-alt__row {
grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
}
.vs-feature-alt__row:nth-child(even) .vs-feature-alt__media {
order: 2;
}
.vs-feature-alt__row:nth-child(even) {
grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
}
}
</style>