# Lift cards (themes.dev UI block)

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. Black and white, dark mode, shadcn-compatible; React, Astro and plain HTML.

74% of the 2,073 sites in The UI Index have a card (1,540 sites).

## 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`
- HTML: https://www.themes.dev/ui/r/html/lift-card.html (with https://www.themes.dev/ui/r/html/caseroom.css)

Page: https://www.themes.dev/ui/blocks/lift-card/ · Rules for agents: https://www.themes.dev/ui/AGENT.md

## Code

### lift-card.astro

```astro
---
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>
```

### lift-card.classes.ts

```tsx
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[] };
```

### lift-card.tsx

```tsx
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>
  );
}
```
