Plan

apps/www/src/sections/Plan.astro · 79 lines · group Pricing

Plan — one tariff of <PricingCards>, <PricingWide> or <PricingSingle>: name, description, a check-marked feature list, the price with its note, a button and an optional small link. Parent: one of the three Pricing… sections; the parent decides the shape (card, wide row, single). Highlight: `recommended` frames the card in the brand colour and shows the `badge` label ("Most popular" by default); the button of a recommended plan is solid, of the others outlined — override with `cta`. Children: optional markdown between the tags, rendered after the feature list. Button: `primaryText` shows the button; its link defaults to "/#contacts".

In the fleet

Used on 1 site built with this engine.

Samples 1

sampleshown inside <PricingCards>

What a season costs

Hardware included; cancel at the end of any season.

One field

A single station, the essentials.

  • One station
  • Air and soil sensors
  • SMS alerts

€19per month

Estate

Most popular

Up to ten stations and a gateway.

  • Ten stations
  • Leaf wetness and wind
  • Shared forecasts
  • Priority replacement

€39per month

Co-op

Several farms behind one gateway.

  • Unlimited stations
  • Per-farm access
  • API and exports
  • Named support

€79per month

MDX of this sample
<PricingCards title="What a season costs" description="Hardware included; cancel at the end of any season.">
  <Plan name="One field" price="€19" priceNote="per month" description="A single station, the essentials." features={['One station', 'Air and soil sensors', 'SMS alerts']} primaryText="Start" />
  <Plan name="Estate" price="€39" priceNote="per month" recommended description="Up to ten stations and a gateway." features={['Ten stations', 'Leaf wetness and wind', 'Shared forecasts', 'Priority replacement']} primaryText="Start" />
  <Plan name="Co-op" price="€79" priceNote="per month" description="Several farms behind one gateway." features={['Unlimited stations', 'Per-farm access', 'API and exports', 'Named support']} primaryText="Talk to us" />
</PricingCards>

Props

NameTypeRequiredDefaultDescription
namestringyesthe tariff name
pricestringyesthe figure, e.g. "€49" or "from 900 kr"
priceNotestringthe unit beside the figure, e.g. "/month"
descriptionstringa sentence under the name
featuresstring[][]what is included, one line each
featuresTitlestringa small uppercase heading above the feature list
recommendedbooleanfalseframe the card in the brand colour and show the badge
badgestring'Most popular'the badge text of a recommended plan
primaryTextstringtext of the button; omit to hide it
cta'primary' | 'secondary'the button's weight; a lone plan carries the primary action
primaryHrefstring'/#contacts'link of the button (default "/#contacts")
linkTextstringtext of a small underlined link under the price; shown only with href
hrefstringtarget of that link

Source apps/www/src/sections/Plan.astro @ gitt.one

---
/**
 * Plan — one tariff of <PricingCards>, <PricingWide> or <PricingSingle>: name,
 *   description, a check-marked feature list, the price with its note, a
 *   button and an optional small link.
 *
 * Parent: one of the three Pricing… sections; the parent decides the shape
 *   (card, wide row, single).
 * Highlight: `recommended` frames the card in the brand colour and shows the
 *   `badge` label ("Most popular" by default); the button of a recommended
 *   plan is solid, of the others outlined — override with `cta`.
 * Children: optional markdown between the tags, rendered after the feature
 *   list.
 * Button: `primaryText` shows the button; its link defaults to "/#contacts".
 */
import Check from 'astro-heroicons/outline/Check.astro';
import Button from './_Button.astro';

interface Props {
  /** the tariff name */
  name: string;
  /** the figure, e.g. "€49" or "from 900 kr" */
  price: string;
  /** the unit beside the figure, e.g. "/month" */
  priceNote?: string;
  /** a sentence under the name */
  description?: string;
  /** what is included, one line each */
  features?: string[];
  /** a small uppercase heading above the feature list */
  featuresTitle?: string;
  /** frame the card in the brand colour and show the badge */
  recommended?: boolean;
  /** the badge text of a recommended plan */
  badge?: string;
  /** text of the button; omit to hide it */
  primaryText?: string;
  /** the button's weight; a lone plan carries the primary action */
  cta?: 'primary' | 'secondary';
  /** link of the button (default "/#contacts") */
  primaryHref?: string;
  /** text of a small underlined link under the price; shown only with href */
  linkText?: string;
  /** target of that link */
  href?: string;
}
const {
  name, price, priceNote, description, features = [], featuresTitle, recommended = false,
  badge = 'Most popular', primaryText, primaryHref = '/#contacts', linkText, href, cta,
} = Astro.props;
---

<div class:list={['relative flex flex-col rounded-2xl border bg-white shadow-sm [div[data-variant=wide]_&]:lg:flex-row', recommended ? 'border-primary-500' : 'border-gray-200']}>
  <div class="flex-1 p-8 [div[data-variant=single]_&]:pb-0 lg:[div[data-variant=wide]_&]:p-12">
    <h3 class="text-xl font-semibold text-gray-900 [div[data-variant=single]_&]:text-lg [div[data-variant=single]_&]:font-medium [div[data-variant=single]_&]:text-center [div[data-variant=wide]_&]:text-3xl [div[data-variant=wide]_&]:font-extrabold">{name}</h3>
    {recommended && <p class="absolute top-0 -translate-y-1/2 rounded-full bg-primary-600 px-4 py-1.5 text-xs font-semibold uppercase tracking-wide text-white">{badge}</p>}
    {description && <p class="mt-4 text-gray-500">{description}</p>}
    {features.length > 0 && (
      <div class="mt-6">
        {featuresTitle && <h4 class="text-sm font-semibold uppercase tracking-wider text-primary-700">{featuresTitle}</h4>}
        <ul role="list" class="mt-4 list-none space-y-4 [div[data-variant=wide]_&]:lg:grid [div[data-variant=wide]_&]:lg:grid-cols-2 [div[data-variant=wide]_&]:lg:gap-x-8 [div[data-variant=wide]_&]:lg:space-y-0 [div[data-variant=wide]_&]:lg:gap-y-4">
          {features.map((f) => (
            <li class="flex"><Check class="h-6 w-6 flex-shrink-0 text-primary-500" aria-hidden="true" /><span class="ml-3 text-gray-500">{f}</span></li>
          ))}
        </ul>
      </div>
    )}
    <slot />
  </div>
  <div class="border-t border-gray-100 p-8 text-center [div[data-variant=single]_&]:border-t-0 [div[data-variant=single]_&]:pt-0 [div[data-variant=wide]_&]:bg-gray-50 [div[data-variant=wide]_&]:lg:flex [div[data-variant=wide]_&]:lg:flex-col [div[data-variant=wide]_&]:lg:justify-center [div[data-variant=wide]_&]:lg:border-t-0 [div[data-variant=wide]_&]:lg:p-12">
    <p class="flex items-baseline justify-center gap-2 text-gray-900">
      <span class="text-5xl font-extrabold tracking-tight">{price}</span>
      {priceNote && <span class="text-xl font-medium text-gray-500">{priceNote}</span>}
    </p>
    {linkText && href && <p class="mt-4 text-sm"><a href={href} class="font-medium text-gray-500 underline">{linkText}</a></p>}
    {primaryText && <div class="mt-6"><Button href={primaryHref} kind={cta ?? (recommended ? 'primary' : 'secondary')}>{primaryText}</Button></div>}
  </div>
</div>

Source links point to engine-astro@7b686388. Samples are demo content: the components are the real ones, the copy and the pictures are made up.