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
<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
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| title | string | yes | the item heading | |
| kicker | string | a short mono label beside the badge, e.g. a product code or a version | ||
| description | string | one or two sentences | ||
| Icon | any | a Heroicon component imported in the article, e.g. Icon={BoltIcon} | ||
| image | string | file name in images/, shown above the title | ||
| number | string | number | an ordinal shown in the badge instead of an icon | ||
| bullets | string[] | [] | short points listed under the description | |
| linkText | string | text of the arrow link at the bottom; shown only with href | ||
| href | string | link 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.