Featurepopular

apps/www/src/sections/Feature.astro · 68 lines · group Features & lists

Feature — one item of <FeatureCards>, <FeatureAside> or <FeatureList>: a badge (icon or number), a title, a description, optional bullets, optional markdown and an optional link. Parent: never on its own; always inside one of the three Feature… sections, which decide whether it is a card, a plain block or an aside row. Badge: `Icon` is a Heroicon imported at the top of the file — `import BoltIcon from 'astro-heroicons/outline/Bolt.astro';` then `Icon={BoltIcon}`; `number` puts an ordinal in the badge instead. Without either there is no badge. Children: markdown between the tags is rendered under the description (paragraphs, lists, emphasis, links). Picture: `image` (file name in images/) is shown full width above the title — for a screenshot or an illustration instead of an icon. Link: `linkText` + `href` render an arrow link at the bottom of the item.

In the fleet

Used on 4 sites built with this engine.

Samples 1

sampleshown inside <FeatureCards>

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>

Props

NameTypeRequiredDefaultDescription
titlestringyesthe item heading
kickerstringa short mono label beside the badge, e.g. a product code or a version
descriptionstringone or two sentences
Iconanya Heroicon component imported in the article, e.g. Icon={BoltIcon}
imagestringfile name in images/, shown above the title
numberstring | numberan ordinal shown in the badge instead of an icon
bulletsstring[][]short points listed under the description
linkTextstringtext of the arrow link at the bottom; shown only with href
hrefstringlink target

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

---
/**
 * Feature — one item of <FeatureCards>, <FeatureAside> or <FeatureList>: a
 *   badge (icon or number), a title, a description, optional bullets, optional
 *   markdown and an optional link.
 *
 * Parent: never on its own; always inside one of the three Feature… sections,
 *   which decide whether it is a card, a plain block or an aside row.
 * Badge: `Icon` is a Heroicon imported at the top of the file — `import
 *   BoltIcon from 'astro-heroicons/outline/Bolt.astro';` then
 *   `Icon={BoltIcon}`; `number` puts an ordinal in the badge instead. Without
 *   either there is no badge.
 * Children: markdown between the tags is rendered under the description
 *   (paragraphs, lists, emphasis, links).
 * Picture: `image` (file name in images/) is shown full width above the title
 *   — for a screenshot or an illustration instead of an icon.
 * Link: `linkText` + `href` render an arrow link at the bottom of the item.
 */
import { Image } from 'astro:assets';
import { resolveImage } from '../lib/images';
import Button from './_Button.astro';

interface Props {
  /** the item heading */
  title: string;
  /** a short mono label beside the badge, e.g. a product code or a version */
  kicker?: string;
  /** one or two sentences */
  description?: string;
  /** a Heroicon component imported in the article, e.g. Icon={BoltIcon} */
  Icon?: any;
  /** file name in images/, shown above the title */
  image?: string;
  /** an ordinal shown in the badge instead of an icon */
  number?: string | number;
  /** short points listed under the description */
  bullets?: string[];
  /** text of the arrow link at the bottom; shown only with href */
  linkText?: string;
  /** link target */
  href?: string;
}

const { title, kicker, description, Icon, image, number, bullets = [], linkText, href } = Astro.props;
const img = image ? await resolveImage(image) : undefined;
---

<li class="flow-root">
  <div class="flex h-full flex-col rounded-lg px-6 pb-8 pt-8 [ul[data-variant=cards]_&]:bg-gray-50 [ul[data-variant=cards]_&]:text-gray-900 [ul[data-variant=cards]_&]:shadow-sm [ul[data-on-dark]_&]:!bg-white/10 [ul[data-on-dark]_&]:!text-white [ul[data-variant=plain]_&]:px-0 [ul[data-variant=plain]_&]:pt-0 [ul[data-variant=plain]_&]:pb-0 [.bg-stone-800_ul[data-variant=cards]:not([data-on-dark])_&]:bg-white [ul[data-overlap]_&]:relative [ul[data-overlap]_&]:rounded-2xl [ul[data-overlap]_&]:!bg-white [ul[data-overlap]_&]:!text-gray-900 [ul[data-overlap]_&]:!shadow-xl [ul[data-overlap]_&]:!pt-16 [ul[data-overlap]_&]:!pb-8 [ul[data-aside]_&]:grid [ul[data-aside]_&]:grid-cols-[auto_1fr] [ul[data-aside]_&]:gap-x-4">
    {(Icon || number !== undefined) && (
      <span class="inline-flex h-12 w-12 items-center justify-center rounded-md bg-gradient-to-r from-primary-600 to-primary-alt-700 text-xl font-bold text-white shadow-lg [ul[data-icons=soft]_&]:bg-none [ul[data-icons=soft]_&]:bg-primary-50 [ul[data-icons=soft]_&]:text-primary-700 [ul[data-icons=soft]_&]:shadow-none [ul[data-on-dark]_&]:bg-none [ul[data-on-dark]_&]:bg-white/10 [ul[data-on-dark]_&]:text-white [ul[data-overlap]_&]:absolute [ul[data-overlap]_&]:top-0 [ul[data-overlap]_&]:h-16 [ul[data-overlap]_&]:w-16 [ul[data-overlap]_&]:-translate-y-1/2 [ul[data-overlap]_&]:rounded-xl [ul[data-aside]_&]:row-span-2">
        {Icon ? <Icon class="h-6 w-6" aria-hidden="true" /> : number}
      </span>
    )}
    {kicker && <code class="ml-3 rounded bg-white px-2 py-1 font-mono text-sm text-primary-700 ring-1 ring-inset ring-primary-100">{kicker}</code>}
    {img && <Image src={img} alt="" widths={[400, 840]} class="w-full rounded-md" />}
    <h3 class="mt-6 text-lg font-medium tracking-tight [ul[data-variant=cards]_&]:text-primary-800 [ul[data-on-dark]_&]:!text-white [ul[data-aside]_&]:col-start-2 [ul[data-aside]_&]:!mt-0 [ul[data-aside]_&]:leading-6">{title}</h3>
    {description && <p class="mt-3 text-base [ul[data-variant=cards]_&]:text-gray-500 [ul[data-on-dark]_&]:!text-primary-100 [.bg-stone-800_ul[data-variant=plain]_&]:text-gray-300 [ul[data-aside]_&]:col-start-2 [ul[data-aside]_&]:mt-2">{description}</p>}
    {bullets.length > 0 && (
      <ul class="mt-4 list-none space-y-2 text-base text-gray-500 [ul[data-aside]_&]:col-start-2">
        {bullets.map((b) => <li class="flex"><span aria-hidden="true" class="mr-3 mt-2 h-1.5 w-1.5 flex-none rounded-full bg-gradient-to-r from-primary-500 to-primary-alt-600" /><span>{b}</span></li>)}
      </ul>
    )}
    <div class="text-base [ul[data-variant=cards]_&]:text-gray-500 [ul[data-aside]_&]:col-start-2 [&>p]:mt-2"><slot /></div>
    {linkText && href && <p class="mt-auto pt-6 [ul[data-aside]_&]:col-start-2 [ul[data-aside]_&]:!pt-2"><Button href={href} kind="link">{linkText}</Button></p>}
  </div>
</li>

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.