Blocks · Marketing
Lift cards
Two or three cards with icon, title and text; on hover and focus the card lifts and the arrow slides; the whole card is one link.
74% of the 2,073 sites in The UI Index have a card (1,540 sites).
Both previews render the same markup; the HTML tab below is that markup without a framework.
Install
- React
- npx shadcn@
latest add https:// www.themes.dev/ ui/ r/ lift-card.json - Astro
- npx shadcn@
latest add https:// www.themes.dev/ ui/ r/ astro/ lift-card.json
Or copy the files below into your project. They need clsx, tailwind-merge and the components they import, and the themes.dev UI theme. The HTML needs only the compiled CSS.
import type { IconName } from '@/components/ui/icon/icon.variants';
/** Lift cards: two or three cards with an icon, a title, a few lines and a words-plus-arrow at the foot. The whole
* card is one link; on hover and on keyboard focus it lifts and the arrow slides. Under reduced motion nothing
* moves (the border and shadow still change, so the state shows). */
export const liftCard = {
root: '@container bg-background px-6 py-14 text-foreground',
inner: 'mx-auto max-w-6xl',
title: 'mb-8 text-2xl font-semibold tracking-[-0.02em] text-balance',
list: 'grid list-none gap-4 p-0 @[40rem]:grid-cols-3',
item: 'flex min-w-0',
card: 'group flex w-full flex-col gap-3 rounded-xl border bg-card p-6 text-card-foreground no-underline transition-[box-shadow,border-color] hover:border-foreground/30 hover:shadow-md focus-visible:border-foreground/30 focus-visible:shadow-md active:bg-pressed/40 motion-safe:transition-[transform,box-shadow,border-color] motion-safe:hover:-translate-y-1 motion-safe:focus-visible:-translate-y-1 motion-safe:active:translate-y-0',
icon: 'mb-1 shrink-0 text-foreground',
glyph: 'size-6',
name: 'text-lg font-semibold leading-snug text-balance',
text: 'text-base text-pretty text-muted-foreground',
go: 'mt-auto inline-flex items-center gap-1.5 pt-3 text-sm font-medium text-foreground',
arrow: 'shrink-0 motion-safe:transition-transform motion-safe:group-hover:translate-x-1 motion-safe:group-focus-visible:translate-x-1',
};
export type LiftCardItem = { title: string; text: string; href: string; icon: IconName; label: string };
export type LiftCardContent = { id?: string; label: string; title?: string; items: LiftCardItem[] };import { Icon } from '@/components/ui/icon';
import { cn } from '@/lib/utils';
import { liftCard as c, type LiftCardContent } from './lift-card.classes';
export type LiftCardProps = LiftCardContent & { className?: string };
/** Lift cards: the whole card is one link; on hover and focus it lifts and the arrow slides (not under reduced motion). */
export function LiftCard({ id = 'lift-card', label, title, items, className }: LiftCardProps) {
return (
<section className={cn(c.root, className)} id={id} aria-label={title ? undefined : label} aria-labelledby={title ? `${id}-title` : undefined}>
<div className={c.inner}>
{title && <h2 className={c.title} id={`${id}-title`}>{title}</h2>}
<ul className={c.list}>
{items.map((it) => (
<li key={it.href} className={c.item}>
<a href={it.href} className={c.card}>
<Icon name={it.icon} className={cn(c.icon, c.glyph)} />
<span className={c.name}>{it.title}</span>
<span className={c.text}>{it.text}</span>
<span className={c.go}>{it.label}<Icon name="arrow-right" className={c.arrow} /></span>
</a>
</li>
))}
</ul>
</div>
</section>
);
}---
import Icon from '@/components/ui/icon.astro';
import { cn } from '@/lib/utils';
import { liftCard as c, type LiftCardContent } from './lift-card.classes';
type Props = LiftCardContent & { class?: string };
const { id = 'lift-card', label, title, items, class: className } = Astro.props;
---
<section class={cn(c.root, className)} id={id} aria-label={title ? undefined : label} aria-labelledby={title ? `${id}-title` : undefined}>
<div class={c.inner}>
{title && <h2 class={c.title} id={`${id}-title`}>{title}</h2>}
<ul class={c.list}>
{
items.map((it) => (
<li class={c.item}>
<a href={it.href} class={c.card}>
<Icon name={it.icon} class={cn(c.icon, c.glyph)} />
<span class={c.name}>{it.title}</span>
<span class={c.text}>{it.text}</span>
<span class={c.go}>{it.label}<Icon name="arrow-right" class={c.arrow} /></span>
</a>
</li>
))
}
</ul>
</div>
</section>import type { IconName } from '@/components/ui/icon/icon.variants';
/** Lift cards: two or three cards with an icon, a title, a few lines and a words-plus-arrow at the foot. The whole
* card is one link; on hover and on keyboard focus it lifts and the arrow slides. Under reduced motion nothing
* moves (the border and shadow still change, so the state shows). */
export const liftCard = {
root: '@container bg-background px-6 py-14 text-foreground',
inner: 'mx-auto max-w-6xl',
title: 'mb-8 text-2xl font-semibold tracking-[-0.02em] text-balance',
list: 'grid list-none gap-4 p-0 @[40rem]:grid-cols-3',
item: 'flex min-w-0',
card: 'group flex w-full flex-col gap-3 rounded-xl border bg-card p-6 text-card-foreground no-underline transition-[box-shadow,border-color] hover:border-foreground/30 hover:shadow-md focus-visible:border-foreground/30 focus-visible:shadow-md active:bg-pressed/40 motion-safe:transition-[transform,box-shadow,border-color] motion-safe:hover:-translate-y-1 motion-safe:focus-visible:-translate-y-1 motion-safe:active:translate-y-0',
icon: 'mb-1 shrink-0 text-foreground',
glyph: 'size-6',
name: 'text-lg font-semibold leading-snug text-balance',
text: 'text-base text-pretty text-muted-foreground',
go: 'mt-auto inline-flex items-center gap-1.5 pt-3 text-sm font-medium text-foreground',
arrow: 'shrink-0 motion-safe:transition-transform motion-safe:group-hover:translate-x-1 motion-safe:group-focus-visible:translate-x-1',
};
export type LiftCardItem = { title: string; text: string; href: string; icon: IconName; label: string };
export type LiftCardContent = { id?: string; label: string; title?: string; items: LiftCardItem[] };<section class="@container bg-background px-6 py-14 text-foreground" id="ex-lift-card" aria-labelledby="ex-lift-card-title">
<div class="mx-auto max-w-6xl">
<h2 class="mb-8 text-2xl font-semibold tracking-[-0.02em] text-balance" id="ex-lift-card-title">Ways to get started</h2>
<ul class="grid list-none gap-4 p-0 @[40rem]:grid-cols-3">
<li class="flex min-w-0">
<a href="#ex-lift-read-the-guide" class="group flex w-full flex-col gap-3 rounded-xl border bg-card p-6 text-card-foreground no-underline transition-[box-shadow,border-color] hover:border-foreground/30 hover:shadow-md focus-visible:border-foreground/30 focus-visible:shadow-md active:bg-pressed/40 motion-safe:transition-[transform,box-shadow,border-color] motion-safe:hover:-translate-y-1 motion-safe:focus-visible:-translate-y-1 motion-safe:active:translate-y-0">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class="inline-block mb-1 shrink-0 text-foreground size-6"><path d="M8 14.5a6.5 6.5 0 1 0 0-13 6.5 6.5 0 0 0 0 13zM8 7.5V11M8 5h.01"></path></svg>
<span class="text-lg font-semibold leading-snug text-balance">Read the guide</span>
<span class="text-base text-pretty text-muted-foreground">A short walk through tokens, themes and the first block.</span>
<span class="mt-auto inline-flex items-center gap-1.5 pt-3 text-sm font-medium text-foreground">
Read the guide
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class="inline-block size-4 shrink-0 motion-safe:transition-transform motion-safe:group-hover:translate-x-1 motion-safe:group-focus-visible:translate-x-1"><path d="M3 8h10M9 4l4 4-4 4"></path></svg>
</span>
</a>
</li>
<li class="flex min-w-0">
<a href="#ex-lift-copy-a-block" class="group flex w-full flex-col gap-3 rounded-xl border bg-card p-6 text-card-foreground no-underline transition-[box-shadow,border-color] hover:border-foreground/30 hover:shadow-md focus-visible:border-foreground/30 focus-visible:shadow-md active:bg-pressed/40 motion-safe:transition-[transform,box-shadow,border-color] motion-safe:hover:-translate-y-1 motion-safe:focus-visible:-translate-y-1 motion-safe:active:translate-y-0">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class="inline-block mb-1 shrink-0 text-foreground size-6"><path d="M5.5 5.5h8v8h-8zM10.5 3.5V3A1.5 1.5 0 0 0 9 1.5H3A1.5 1.5 0 0 0 1.5 3v6A1.5 1.5 0 0 0 3 10.5h.5"></path></svg>
<span class="text-lg font-semibold leading-snug text-balance">Copy a block</span>
<span class="text-base text-pretty text-muted-foreground">Take the Astro or React source, or the plain HTML.</span>
<span class="mt-auto inline-flex items-center gap-1.5 pt-3 text-sm font-medium text-foreground">
Browse the blocks
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class="inline-block size-4 shrink-0 motion-safe:transition-transform motion-safe:group-hover:translate-x-1 motion-safe:group-focus-visible:translate-x-1"><path d="M3 8h10M9 4l4 4-4 4"></path></svg>
</span>
</a>
</li>
<li class="flex min-w-0">
<a href="#ex-lift-subscribe-to-updates" class="group flex w-full flex-col gap-3 rounded-xl border bg-card p-6 text-card-foreground no-underline transition-[box-shadow,border-color] hover:border-foreground/30 hover:shadow-md focus-visible:border-foreground/30 focus-visible:shadow-md active:bg-pressed/40 motion-safe:transition-[transform,box-shadow,border-color] motion-safe:hover:-translate-y-1 motion-safe:focus-visible:-translate-y-1 motion-safe:active:translate-y-0">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class="inline-block mb-1 shrink-0 text-foreground size-6"><path d="M2 3.5h12v9H2zM2.5 4l5.5 4.5L13.5 4"></path></svg>
<span class="text-lg font-semibold leading-snug text-balance">Subscribe to updates</span>
<span class="text-base text-pretty text-muted-foreground">New blocks and changes, once a month and nothing else.</span>
<span class="mt-auto inline-flex items-center gap-1.5 pt-3 text-sm font-medium text-foreground">
Subscribe
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class="inline-block size-4 shrink-0 motion-safe:transition-transform motion-safe:group-hover:translate-x-1 motion-safe:group-focus-visible:translate-x-1"><path d="M3 8h10M9 4l4 4-4 4"></path></svg>
</span>
</a>
</li>
</ul>
</div>
</section>