Starting Point
v0.33.1

Search documentation

Search for a page and jump to it.

Start typing to search...
Go to Page
escClose

Tailwind CSS Cards

Use the card component to group related content on one surface, like a login form, a stat, or a settings section. For a single row with media, text, and actions, use an item.

<div class="card w-full max-w-sm">
  <div class="card-header">
    <h3 class="card-title">Login to your account</h3>
    <p class="card-description">
      Enter your email below to login to your account
    </p>
    <div class="card-action">
      <button class="btn btn-link">Sign Up</button>
    </div>
  </div>
  <div class="card-content">
    <form class="flex flex-col gap-6">
      <div class="field">
        <label class="label" for="card-demo-email">Email</label>
        <input
          class="input"
          id="card-demo-email"
          type="email"
          placeholder="[email protected]"
          required
        />
      </div>
      <div class="field">
        <div class="flex w-full items-center">
          <label class="label" for="card-demo-password">Password</label>
          <a
            href="#"
            class="ml-auto inline-block text-sm underline-offset-4 hover:underline"
          >
            Forgot your password?
          </a>
        </div>
        <input class="input" id="card-demo-password" type="password" required />
      </div>
    </form>
  </div>
  <div class="card-footer flex-col gap-2">
    <button type="submit" class="btn w-full">Login</button>
    <button class="btn btn-outline w-full">Login with Google</button>
  </div>
</div>

Usage

Add the card class to the surface and compose the sections inside.

ClassDescription
cardAdd to the surface, spaced by --card-spacing
card-smAdd to card for compact spacing and a smaller title
card-headerAdd to the top section holding title, description, and action
card-titleAdd to a heading element
card-descriptionAdd to a paragraph with supporting text
card-actionAdd to an element in the header to pin it to the top-right corner
card-contentAdd to the main section
card-footerAdd to the bottom section, a horizontal row by default
<div class="card">
  <div class="card-header">
    <h3 class="card-title">Card Title</h3>
    <p class="card-description">Card Description</p>
    <div class="card-action">Card Action</div>
  </div>
  <div class="card-content">Card Content</div>
  <div class="card-footer">Card Footer</div>
</div>

Examples

Card sizes

Add card-sm for a compact card with tighter spacing and a smaller title.

<div class="card card-sm w-full max-w-xs">
  <div class="card-header">
    <h3 class="card-title">Scheduled reports</h3>
    <p class="card-description">Weekly snapshots. No manual exports.</p>
  </div>
  <div class="card-content">
    <ul class="grid gap-2 py-2 text-sm">
      <li class="flex gap-2">
        <svg class="mt-0.5 size-4 shrink-0 text-muted-foreground" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>
        <span>Choose a schedule (daily, or weekly).</span>
      </li>
      <li class="flex gap-2">
        <svg class="mt-0.5 size-4 shrink-0 text-muted-foreground" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>
        <span>Send to channels or specific teammates.</span>
      </li>
      <li class="flex gap-2">
        <svg class="mt-0.5 size-4 shrink-0 text-muted-foreground" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>
        <span>Include charts, tables, and key metrics.</span>
      </li>
    </ul>
  </div>
  <div class="card-footer flex-col gap-2">
    <button class="btn btn-sm w-full">Set up scheduled reports</button>
    <button class="btn btn-outline btn-sm w-full">See what's new</button>
  </div>
</div>

Card spacing

Set --card-spacing to control the section gaps and edge insets in one place.

<div class="flex w-full max-w-sm flex-col items-center gap-4">
  <div class="btn-group" role="group" aria-label="Card spacing">
    <button
      class="btn btn-outline btn-sm aria-pressed:bg-accent aria-pressed:text-accent-foreground"
      data-spacing="4"
      aria-pressed="false"
    >
      16px
    </button>
    <button
      class="btn btn-outline btn-sm aria-pressed:bg-accent aria-pressed:text-accent-foreground"
      data-spacing="5"
      aria-pressed="false"
    >
      20px
    </button>
    <button
      class="btn btn-outline btn-sm aria-pressed:bg-accent aria-pressed:text-accent-foreground"
      data-spacing="6"
      aria-pressed="true"
    >
      24px
    </button>
    <button
      class="btn btn-outline btn-sm aria-pressed:bg-accent aria-pressed:text-accent-foreground"
      data-spacing="8"
      aria-pressed="false"
    >
      32px
    </button>
  </div>
 
  <div class="card w-full" id="card-spacing-demo">
    <div class="card-header">
      <h3 class="card-title">Login to your account</h3>
      <p class="card-description">
        Enter your email below to login to your account
      </p>
      <div class="card-action">
        <button class="btn btn-link">Sign Up</button>
      </div>
    </div>
    <div class="card-content">
      <form class="flex flex-col gap-6">
        <div class="field">
          <label class="label" for="card-spacing-email">Email</label>
          <input
            class="input"
            id="card-spacing-email"
            type="email"
            placeholder="[email protected]"
            required
          />
        </div>
        <div class="field">
          <div class="flex w-full items-center">
            <label class="label" for="card-spacing-password">Password</label>
            <a
              href="#"
              class="ml-auto inline-block text-sm underline-offset-4 hover:underline"
            >
              Forgot your password?
            </a>
          </div>
          <input
            class="input"
            id="card-spacing-password"
            type="password"
            required
          />
        </div>
      </form>
    </div>
    <div class="card-footer flex-col gap-2">
      <button type="submit" class="btn w-full">Login</button>
      <button class="btn btn-outline w-full">Login with Google</button>
    </div>
  </div>
</div>
 
<script>
  const card = document.getElementById("card-spacing-demo");
const buttons = document.querySelectorAll("[data-spacing]");
buttons.forEach((button) => {
  button.addEventListener("click", () => {
    buttons.forEach((b) =>
      b.setAttribute("aria-pressed", String(b === button)));
    card.style.setProperty(
      "--card-spacing",
      `calc(var(--spacing) * ${button.dataset.spacing})`,
    );
  });
});
</script>

Edge to edge content

Use negative margins with -mx-(--card-spacing) to run content to the card edges while keeping it aligned with the inset.

<div class="card w-full max-w-sm">
  <div class="card-header">
    <h3 class="card-title">Terms of Service</h3>
    <p class="card-description">
      Review the terms before accepting the agreement.
    </p>
  </div>
  <div class="card-content">
    <div
      class="-mx-(--card-spacing) max-h-48 space-y-4 overflow-y-scroll border-y bg-muted/50 px-(--card-spacing) py-4 text-sm leading-relaxed"
    >
      <p>
        These terms govern your use of the workspace, including access to shared
        documents, project files, and collaboration tools.
      </p>
      <p>
        You are responsible for the content you upload and for ensuring that
        your team has the appropriate permissions to view or edit it.
      </p>
      <p>
        We may update features or limits as the service evolves. When those
        changes materially affect your workflow, we will notify your workspace
        administrators.
      </p>
      <p>
        By continuing, you agree to keep your account credentials secure and to
        follow your organization's acceptable use policies.
      </p>
    </div>
  </div>
  <div class="card-footer justify-end gap-2">
    <button class="btn btn-outline">Decline</button>
    <button class="btn">Accept</button>
  </div>
</div>

Card with an image

Place an image before the card header.

<div class="card relative w-full max-w-sm pt-0">
  <div class="absolute inset-0 z-30 aspect-video bg-black/35"></div>
  <img
    src="https://avatar.vercel.sh/shadcn1"
    alt="Event cover"
    class="relative z-20 aspect-video w-full object-cover brightness-60 grayscale dark:brightness-40"
  />
  <div class="card-header">
    <div class="card-action">
      <span class="badge badge-secondary">Featured</span>
    </div>
    <h3 class="card-title">Design systems meetup</h3>
    <p class="card-description">
      A practical talk on component APIs, accessibility, and shipping faster.
    </p>
  </div>
  <div class="card-footer">
    <button class="btn w-full">View Event</button>
  </div>
</div>

RTL

Add dir="rtl" to any parent element, typically <html>, to render right-to-left. Use logical utilities like ms-auto so alignment follows the text direction. See the RTL guide for details.

<div id="card-rtl-demo" dir="rtl" class="w-full max-w-sm">
  <div class="card">
    <div class="card-header">
      <h3 class="card-title" data-i18n="title">تسجيل الدخول إلى حسابك</h3>
      <p class="card-description" data-i18n="description">أدخل بريدك الإلكتروني أدناه لتسجيل الدخول إلى حسابك</p>
      <div class="card-action">
        <button class="btn btn-link" data-i18n="signUp">إنشاء حساب</button>
      </div>
    </div>
    <div class="card-content">
      <form class="flex flex-col gap-6">
        <div class="field">
          <label class="label" for="card-rtl-email" data-i18n="email">البريد الإلكتروني</label>
          <input class="input" id="card-rtl-email" type="email" placeholder="[email protected]" required />
        </div>
        <div class="field">
          <div class="flex w-full items-center">
            <label class="label" for="card-rtl-password" data-i18n="password">كلمة المرور</label>
            <a href="#" class="ms-auto inline-block text-sm underline-offset-4 hover:underline" data-i18n="forgotPassword">نسيت كلمة المرور؟</a>
          </div>
          <input class="input" id="card-rtl-password" type="password" required />
        </div>
      </form>
    </div>
    <div class="card-footer flex-col gap-2">
      <button type="submit" class="btn w-full" data-i18n="login">تسجيل الدخول</button>
      <button class="btn btn-outline w-full" data-i18n="loginWithGoogle">تسجيل الدخول باستخدام Google</button>
    </div>
  </div>
</div>
 
<script>
  const translations = {
    ar: { dir: "rtl", title: "تسجيل الدخول إلى حسابك", description: "أدخل بريدك الإلكتروني أدناه لتسجيل الدخول إلى حسابك", signUp: "إنشاء حساب", email: "البريد الإلكتروني", password: "كلمة المرور", forgotPassword: "نسيت كلمة المرور؟", login: "تسجيل الدخول", loginWithGoogle: "تسجيل الدخول باستخدام Google" },
    he: { dir: "rtl", title: "התחבר לחשבון שלך", description: "הזן את האימייל שלך למטה כדי להתחבר לחשבון שלך", signUp: "הירשם", email: "אימייל", password: "סיסמה", forgotPassword: "שכחת את הסיסמה?", login: "התחבר", loginWithGoogle: "התחבר עם Google" },
    en: { dir: "ltr", title: "Login to your account", description: "Enter your email below to login to your account", signUp: "Sign Up", email: "Email", password: "Password", forgotPassword: "Forgot your password?", login: "Login", loginWithGoogle: "Login with Google" },
  };
  window.addEventListener("message", (e) => {
    const t = translations[e.data?.lang];
    if (e.data?.type !== "sp-language" || !t) return;
    document.getElementById("card-rtl-demo").dir = t.dir;
    document.querySelectorAll("[data-i18n]").forEach((el) => {
      el.textContent = t[el.dataset.i18n];
    });
  });
</script>

CSS variables

One variable sets the card's padding, section gap, and section insets. Override it per card with an arbitrary property, or for the whole app with a CSS rule.

VariableDefaultDescription
--card-spacing1.5rem (1rem with card-sm)Padding and gap rhythm of the card and its sections
<div class="card [--card-spacing:--spacing(8)]"></div>