---
/**
* PricingRows: one plan per ruled row (name, inclusions, price and action).
* Good for many plans or long feature lists. The featured row is tinted.
*
* @example
* <PricingRows title="Pick your" accent="plan." tiers={[...]} />
*/
import Button from "../button/Button.astro";
import Icon from "../icon/Icon.astro";
interface Cta {
label: string;
href: string;
}
interface Tier {
name: string;
price: string;
period?: string;
description?: string;
features: string[];
cta: Cta;
featured?: boolean;
}
interface Props {
title: string;
accent?: string;
intro?: string;
tiers: Tier[];
as?: "h2" | "h3";
class?: string;
}
const { title, accent, intro, tiers, as: Heading = "h2", class: className } = Astro.props;
---
<section class:list={["vs-pricing-rows", className]}>
<div class="vs-pricing-rows__inner">
<Heading class="vs-display vs-pricing-rows__title">{title}{accent && <> <em>{accent}</em></>}</Heading>
{intro && <p class="vs-pricing-rows__intro">{intro}</p>}
<ul class="vs-pricing-rows__list">
{
tiers.map((t) => (
<li class:list={["vs-pricing-rows__row", { "is-featured": t.featured }]}>
<div>
<h3 class="vs-pricing-rows__name">{t.name}</h3>
{t.description && <p class="vs-pricing-rows__desc">{t.description}</p>}
</div>
<ul class="vs-pricing-rows__features">
{t.features.map((f) => (
<li>
<Icon name="check" size={16} strokeWidth={2.25} />
<span>{f}</span>
</li>
))}
</ul>
<div class="vs-pricing-rows__buy">
<p class="vs-display vs-pricing-rows__price">{t.price}</p>
{t.period && <p class="vs-pricing-rows__period">{t.period}</p>}
<div class="vs-pricing-rows__action">
<Button href={t.cta.href} variant={t.featured ? "primary" : "secondary"} size="sm">{t.cta.label}</Button>
</div>
</div>
</li>
))
}
</ul>
</div>
</section>
<style>
.vs-pricing-rows {
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-pricing-rows__inner {
max-width: var(--vs-container);
margin-inline: auto;
padding: var(--vs-section-y) var(--vs-gutter);
}
.vs-pricing-rows__title {
font-size: clamp(2rem, 6.5cqi, 4.25rem);
}
.vs-pricing-rows__intro {
max-width: 34rem;
margin: 1.25rem 0 0;
font-size: 1.0625rem;
line-height: 1.55;
color: var(--vs-ink-2);
}
.vs-pricing-rows__list {
margin: clamp(28px, 5cqi, 56px) 0 0;
padding: 0;
list-style: none;
border-top: calc(var(--vs-border-w) + 1px) solid var(--vs-ink);
}
.vs-pricing-rows__row {
display: grid;
gap: 20px;
padding: 28px 4px;
border-bottom: var(--vs-border-w) solid var(--vs-line-strong);
}
.vs-pricing-rows__row.is-featured {
padding-inline: 20px;
background: var(--vs-accent-tint);
}
.vs-pricing-rows__name {
margin: 0;
font-size: 1.25rem;
font-weight: 600;
}
.vs-pricing-rows__desc {
margin: 8px 0 0;
font-size: 0.9375rem;
line-height: 1.5;
color: var(--vs-ink-2);
}
.vs-pricing-rows__features {
display: grid;
gap: 10px;
margin: 0;
padding: 0;
list-style: none;
font-size: 0.9375rem;
}
.vs-pricing-rows__features li {
display: flex;
gap: 10px;
line-height: 1.4;
}
.vs-pricing-rows__features :global(.vs-icon) {
margin-top: 2px;
color: var(--vs-accent-ink);
}
.vs-pricing-rows__price {
font-size: clamp(2rem, 4.5cqi, 2.75rem);
line-height: 1;
}
.vs-pricing-rows__period {
margin: 6px 0 0;
font-size: 0.8125rem;
color: var(--vs-ink-3);
}
.vs-pricing-rows__action {
margin-top: 14px;
}
@container (min-width: 860px) {
.vs-pricing-rows__row {
grid-template-columns: minmax(0, 4fr) minmax(0, 5fr) minmax(0, 3fr);
gap: 32px;
align-items: start;
}
.vs-pricing-rows__features {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.vs-pricing-rows__buy {
text-align: right;
}
}
</style>