A feature built from two tall bordered cards, each stacking an image tile with a live asset over a small kicker, an editorial title, a checked list on hairlines, and its own outline action.
A feature built from two tall bordered cards, each stacking an image tile with a live asset over a small kicker, an editorial title, a checked list on hairlines and its own outline action.
Upgrade to Pro
Pro blocks install through the shadcn CLI with your license key and ship their full source. Docs and live previews stay open to everyone, so you can read every block's details first.
Swap YOUR_EMAIL and YOUR_KEY for the email and license key on your account. Find your license key on your account page.
Radix flavor
npx shadcn add "https://ui.beste.co/r/feature258?email=YOUR_EMAIL&license_key=YOUR_KEY"Base UI flavor
npx shadcn add "https://ui.beste.co/r-base/feature258?email=YOUR_EMAIL&license_key=YOUR_KEY"This installs the block to components/beste/block/feature258.tsx, the money23 and notification20 pieces it floats on the two card tiles (installed to components/beste/piece/{name}.tsx), and the badge23 and button21 components it uses for the eyebrow and the card actions.
The installed file exports feature258Demo alongside the block: the exact props behind the preview above. Spread it to get a working feature in one line.
import { Feature258, feature258Demo } from "@/components/beste/block/feature258";
export default function Page() {
return <Feature258 {...feature258Demo} />;
}Then replace the demo with your own props. Written out, a trimmed setup looks like this:
import { Feature258 } from "@/components/beste/block/feature258";
import { Money23 } from "@/components/beste/piece/money23";
import { Notification20 } from "@/components/beste/piece/notification20";
import { ShieldCheck } from "lucide-react";
export default function Page() {
return (
<Feature258
badge={{ label: "Two hard parts" }}
heading="Billing and access, handled without a second system"
description="The two things practices usually bolt on afterwards are the two things this refuses to leave out."
items={[
{
kicker: "Billing",
title: "One bill instead of four",
description: "Charges accrue as care happens and reconcile themselves.",
points: [
"Invoices raised from the appointment record",
"Insurer and self-pay on the same ledger",
],
button: { label: "See billing", href: "/billing" },
media: <Money23 label="Estimated saving" amount="$1,840" period="per month" />,
image: { src: "/backdrops/blue.jpg", alt: "Soft blue gradient backdrop" },
},
{
kicker: "Access",
title: "Records open to the right people only",
description: "Access is requested inside the product and logged either way.",
points: ["Six named roles, no checkbox grid", "Every grant and export written down"],
button: { label: "See permissions", href: "/security" },
media: (
<Notification20
icon={ShieldCheck}
title="Access request"
meta="Noah Reyes is asking for the records role"
primaryLabel="Approve"
secondaryLabel="Decline"
/>
),
image: { src: "/backdrops/green.jpg", alt: "Deep green gradient backdrop" },
},
]}
/>
);
}| Prop | Type | Default | Description |
|---|---|---|---|
badge | { label: string } | – | Eyebrow above the hairline rule, rendered through Badge23 |
heading | string | – | Section heading in the left column of the header |
description | string | – | Supporting paragraph, right-aligned from md up |
items | FeatureCard[] | [] | Cards, laid out two-up from lg |
className | string | – | Extra classes for the outer section |
type ActionLink = {
label: string;
href: string;
};
type FeatureCard = {
kicker: string;
title: string;
description: string;
points: string[];
button: ActionLink;
media: ReactNode;
image?: TileImage;
};
type TileImage = {
src: string;
alt: string;
};media and button are required on every card while image is optional, so a card can float its asset on the plain muted tile with no photograph behind it.flex flex-col with the body marked flex-1, so a pair with different amounts of copy still ends at the same height and both actions land on the same line.h-72, then md:h-80, and sits flush inside the card's overflow-hidden rounding rather than being inset, which is what makes the image read as the card's own header rather than a picture placed in it.border-t with last:border-b, so each list closes at the bottom and reads as a small table rather than an open list.aria-hidden and the text carries the meaning, so a screen reader gets a plain list rather than "check, check, check".tone="outline". The card is already a bordered surface, and a solid button inside it would compete with the section's own emphasis.lg:grid-cols-2 only, so the cards stack full width on tablet. With tiles this tall, two-up below lg would squeeze the copy.feature189
Masonry-style two-column cards with alternating heights and image accents over background media
feature183
Two-column layout with large image card on left and stacked glass feature cards on right
feature243
A feature with an eyebrow over a hairline rule, a two-column heading, and three bordered cards that each float a compact live micro-asset above a title and description.
feature187
Alternating two-column feature cards with large icons over background media, left-right zigzag layout