FeatureCardspopular

apps/www/src/sections/FeatureCards.astro · 81 lines · group Features & lists

FeatureCards — a grid of cards, one <Feature> per card: services, benefits, reasons to choose, audiences. Use for: three to six parallel items of equal weight, each with an icon or a picture and a sentence or two. The most used section of the library. Not for: a dense list of many short points (<FeatureList>, no cards); one topic explained in several points beside a heading (<FeatureAside>); a numbered process (<Steps>); a choice between two or three things (<Compare>). Children: <Feature> tags only, three or more. Content written directly between the tags is not rendered. Layout: `columns` 1–4 (default 3); the cards wrap to one column on phones. `overlap` pulls the grid up over the previous section — pair it with a <HeroCover> right above (its default overlapNext keeps the room). `heading="left"` or a `kicker` moves the heading out of the centre. Look: `icons="soft"` paints the icon squares in a pale tint instead of the brand gradient; `decor="dots"` adds the decorative dot grid; on theme="brand" the cards turn translucent white on the gradient. Link: `linkText` + `href` add one centered button under the grid.

In the fleet

Used on 4 sites built with this engine.

Samples 3

sample

WHY NORDVIND

What the station actually changes

  • Frost alerts

    Threshold, rate of fall and dew point, checked every minute inside the crop.

  • Leaf wetness

    Tells you whether the spray will hold before you fill the tank.

  • Offline buffer

    The gateway keeps a week of readings when the uplink drops.

MDX of this sample
import BoltIcon from 'astro-heroicons/outline/Bolt.astro';
import SignalIcon from 'astro-heroicons/outline/Signal.astro';
import CloudIcon from 'astro-heroicons/outline/Cloud.astro';

<FeatureCards kicker="WHY NORDVIND" title="What the station actually changes" columns={3}>
  <Feature title="Frost alerts" description="Threshold, rate of fall and dew point, checked every minute inside the crop." Icon={BoltIcon} />
  <Feature title="Leaf wetness" description="Tells you whether the spray will hold before you fill the tank." Icon={CloudIcon} />
  <Feature title="Offline buffer" description="The gateway keeps a week of readings when the uplink drops." Icon={SignalIcon} />
</FeatureCards>
sample

Built to be left alone

  • 1

    One battery

    A full season on a single charge.

  • 2

    One bolt

    Mounts on any standard post.

  • 3

    No tools

    Sensors swap by hand, in the field.

  • 4

    No visits

    Firmware arrives over the air.

MDX of this sample
<FeatureCards theme="dark" icons="soft" columns={4} heading="left" title="Built to be left alone">
  <Feature number="1" title="One battery" description="A full season on a single charge." />
  <Feature number="2" title="One bolt" description="Mounts on any standard post." />
  <Feature number="3" title="No tools" description="Sensors swap by hand, in the field." />
  <Feature number="4" title="No visits" description="Firmware arrives over the air." />
</FeatureCards>
sample

Three things growers ask us first

  • Does it work without coverage?

    Yes — the gateway buffers and syncs when the network returns.

    • A week of storage
    • No lost readings
  • Can I export the data?

    CSV, PDF and an open API. The readings belong to the farm.

  • What if a sensor fails?

    You hear it from us before you notice it, and the spare fits in a pocket.

    Warranty

MDX of this sample
import SignalIcon from 'astro-heroicons/outline/Signal.astro';
import ShieldIcon from 'astro-heroicons/outline/ShieldCheck.astro';
import BatteryIcon from 'astro-heroicons/outline/Battery50.astro';

<FeatureCards overlap columns={3} title="Three things growers ask us first" decor="dots">
  <Feature title="Does it work without coverage?" description="Yes — the gateway buffers and syncs when the network returns." Icon={SignalIcon} bullets={['A week of storage', 'No lost readings']} />
  <Feature title="Can I export the data?" description="CSV, PDF and an open API. The readings belong to the farm." Icon={ShieldIcon} />
  <Feature title="What if a sensor fails?" description="You hear it from us before you notice it, and the spare fits in a pocket." Icon={BatteryIcon} linkText="Warranty" href="/warranty/" />
</FeatureCards>

Props

NameTypeRequiredDefaultDescription
idstringanchor of the section, e.g. "services" → /#services
kickerstringshort uppercase line above the title, painted with the brand gradient
titlestringthe section heading
descriptionstringone or two sentences under the heading
columns1 | 2 | 3 | 43cards per row on desktop
theme'light' | 'muted' | 'dark' | 'brand' | 'brand-dark''light'background of the section
decor'none' | 'dots' | 'dots-left''none'decorative dot grid behind the content
overlapbooleanfalsehang the cards over the bottom edge of the section above (a <HeroCover>)
heading'center' | 'left''center'where the heading sits
icons'brand' | 'soft''brand'"brand" gradient squares behind the icons, "soft" a pale tint
linkTextstringtext of one centered button under the grid; shown only with href
hrefstringlink of that button
classstringescape hatch for compositions: extra classes for the section shell

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

---
/**
 * FeatureCards — a grid of cards, one <Feature> per card: services, benefits,
 *   reasons to choose, audiences.
 *
 * Use for: three to six parallel items of equal weight, each with an icon or a
 *   picture and a sentence or two. The most used section of the library.
 * Not for: a dense list of many short points (<FeatureList>, no cards); one
 *   topic explained in several points beside a heading (<FeatureAside>); a
 *   numbered process (<Steps>); a choice between two or three things
 *   (<Compare>).
 * Children: <Feature> tags only, three or more. Content written directly
 *   between the tags is not rendered.
 * Layout: `columns` 1–4 (default 3); the cards wrap to one column on phones.
 *   `overlap` pulls the grid up over the previous section — pair it with a
 *   <HeroCover> right above (its default overlapNext keeps the room).
 *   `heading="left"` or a `kicker` moves the heading out of the centre.
 * Look: `icons="soft"` paints the icon squares in a pale tint instead of the
 *   brand gradient; `decor="dots"` adds the decorative dot grid; on
 *   theme="brand" the cards turn translucent white on the gradient.
 * Link: `linkText` + `href` add one centered button under the grid.
 */
import Section from './_Section.astro';
import type { Theme } from './_Section.astro';
import Button from './_Button.astro';

interface Props {
  /** anchor of the section, e.g. "services" → /#services */
  id?: string;
  /** short uppercase line above the title, painted with the brand gradient */
  kicker?: string;
  /** the section heading */
  title?: string;
  /** one or two sentences under the heading */
  description?: string;
  /** cards per row on desktop */
  columns?: 1 | 2 | 3 | 4;
  /** background of the section */
  theme?: Theme;
  /** decorative dot grid behind the content */
  decor?: 'none' | 'dots' | 'dots-left';
  /** hang the cards over the bottom edge of the section above (a <HeroCover>) */
  overlap?: boolean;
  /** where the heading sits */
  heading?: 'center' | 'left';
  /** "brand" gradient squares behind the icons, "soft" a pale tint */
  icons?: 'brand' | 'soft';
  /** text of one centered button under the grid; shown only with href */
  linkText?: string;
  /** link of that button */
  href?: string;
  /** escape hatch for compositions: extra classes for the section shell */
  class?: string;
}

const { id, kicker, title, description, columns = 3, theme = 'light', decor = 'none', overlap = false, heading = 'center', icons = 'brand', linkText, href, class: extra = '' } = Astro.props;
const cols = { 1: '', 2: 'sm:grid-cols-2', 3: 'sm:grid-cols-2 lg:grid-cols-3', 4: 'sm:grid-cols-2 lg:grid-cols-4' }[columns];
// the shell centers a plain title; a kicker or a left heading is our own block
const ownHeader = Boolean(title) && (Boolean(kicker) || heading === 'left');
---

<Section id={id} title={ownHeader ? undefined : title} description={ownHeader ? undefined : description} theme={theme} overlap={overlap} heading={title && !ownHeader ? heading : 'sr'} class={extra} decor={decor}>
  {ownHeader && (
    <div class:list={['mb-12 max-w-3xl', heading === 'center' && 'mx-auto text-center']}>
      {kicker && <p class="text-sm font-semibold uppercase tracking-wide"><span class="bg-gradient-to-r from-primary-500 to-primary-alt-600 bg-clip-text text-transparent [.bg-gradient-to-r_&]:from-primary-100 [.bg-gradient-to-r_&]:to-primary-alt-400">{kicker}</span></p>}
      <h2 class="mt-3 text-3xl font-extrabold tracking-tight sm:text-4xl">{title}</h2>
      {description && <p class="mt-4 text-lg text-gray-500 [.bg-stone-800_&]:text-primary-100 [.bg-gradient-to-r_&]:text-primary-100">{description}</p>}
    </div>
  )}
  <ul
    class:list={['grid list-none grid-cols-1 gap-8', cols, 'gap-y-16 sm:gap-y-8', overlap && 'gap-y-20 sm:gap-y-12']}
    data-variant="cards"
    data-overlap={overlap ? '' : undefined}
    data-on-dark={theme === 'brand' ? '' : undefined}
    data-icons={icons}
  >
    <slot />
  </ul>
  {linkText && href && <p class="mt-10 text-center"><Button href={href}>{linkText}</Button></p>}
</Section>

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.