card

Displays a card with header, content, and footer.

PreviousNext
Login to your accountEnter your email below to login to your accountSign upLoginLogin with Google
import { ChangeDetectionStrategy, Component } from '@angular/core';

import { ZardButtonComponent } from '@/shared/components/button/button.component';
import { ZardCardImports } from '@/shared/components/card/card.imports';
import { ZardFieldImports } from '@/shared/components/field/field.imports';
import { ZardInputComponent } from '@/shared/components/input/input.component';

@Component({
  selector: 'z-demo-card-preview',
  imports: [ZardCardImports, ZardButtonComponent, ...ZardFieldImports, ZardInputComponent],
  template: `
    <z-card class="w-full md:w-94">
      <z-card-header>
        <z-card-title zTitle="Login to your account" />
        <z-card-description zDescription="Enter your email below to login to your account" />
        <z-card-action>
          <a z-button zType="link" href="#">Sign up</a>
        </z-card-action>
      </z-card-header>
      <z-card-content>
        <div z-field-group>
          <div z-field>
            <label z-field-label for="card-login-email">Email</label>
            <input z-input id="card-login-email" type="email" placeholder="m@example.com" required />
          </div>
          <div z-field>
            <div class="flex items-center">
              <label z-field-label for="card-login-password">Password</label>
              <a href="#" class="ms-auto text-sm underline-offset-4 hover:underline">Forgot your password?</a>
            </div>
            <input z-input id="card-login-password" type="password" required />
          </div>
        </div>
      </z-card-content>
      <z-card-footer class="flex-col gap-2">
        <z-button class="w-full">Login</z-button>
        <z-button zType="outline" class="w-full">Login with Google</z-button>
      </z-card-footer>
    </z-card>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoCardPreviewComponent {}

Installation

Copy
npx zard-cli@latest add card

Usage

import { ZardCardComponent } from '@/shared/components/card/card.component';
Copy
<z-card zTitle="Card Title" zDescription="Card Description">
  <p>Card Content</p>
</z-card>
Copy

Examples

size

Set zSize="sm" to switch the card to its compact gap and padding scale.
Scheduled reportsWeekly snapshots. No more manual exports.
  • Choose a schedule (daily, or weekly).
  • Send to channels or specific teammates.
  • Include charts, tables, and key metrics.
Set up scheduled reportsSee what's new
import { ChangeDetectionStrategy, Component } from '@angular/core';

import { NgIcon, provideIcons } from '@ng-icons/core';
import { lucideChevronRight } from '@ng-icons/lucide';

import { ZardButtonComponent } from '@/shared/components/button';
import { ZardCardImports } from '@/shared/components/card/card.imports';

@Component({
  selector: 'z-demo-card-size',
  imports: [ZardCardImports, ZardButtonComponent, NgIcon],
  template: `
    <z-card zSize="sm" class="mx-auto w-full max-w-xs">
      <z-card-header>
        <z-card-title [zTitle]="featureName" />
        <z-card-description zDescription="Weekly snapshots. No more manual exports." />
      </z-card-header>
      <z-card-content>
        <ul class="grid gap-2 py-2 text-sm">
          <li class="flex gap-2">
            <ng-icon name="lucideChevronRight" class="text-muted-foreground mt-0.5 size-4 shrink-0" />
            <span>Choose a schedule (daily, or weekly).</span>
          </li>
          <li class="flex gap-2">
            <ng-icon name="lucideChevronRight" class="text-muted-foreground mt-0.5 size-4 shrink-0" />
            <span>Send to channels or specific teammates.</span>
          </li>
          <li class="flex gap-2">
            <ng-icon name="lucideChevronRight" class="text-muted-foreground mt-0.5 size-4 shrink-0" />
            <span>Include charts, tables, and key metrics.</span>
          </li>
        </ul>
      </z-card-content>
      <z-card-footer class="flex-col gap-2">
        <z-button zSize="sm" class="w-full">Set up scheduled reports</z-button>
        <z-button zType="outline" zSize="sm" class="w-full">See what's new</z-button>
      </z-card-footer>
    </z-card>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
  viewProviders: [provideIcons({ lucideChevronRight })],
})
export class ZardDemoCardSizeComponent {
  readonly featureName = 'Scheduled reports';
}

spacing

Every gap and padding of the card reads the --card-spacing CSS variable. Override it on the root with a class such as [--card-spacing:--spacing(6)] to widen or tighten the whole card at once.
Login to your accountEnter your email below to login to your accountSign UpLoginLogin with Google
import { ChangeDetectionStrategy, Component, computed, signal } from '@angular/core';

import { ZardButtonComponent } from '@/shared/components/button';
import { ZardCardImports } from '@/shared/components/card/card.imports';
import { ZardFieldImports } from '@/shared/components/field/field.imports';
import { ZardInputComponent } from '@/shared/components/input/input.component';
import {
  ZardToggleGroupComponent,
  type ZardToggleGroupItem,
} from '@/shared/components/toggle-group/toggle-group.component';

const SPACING_OPTIONS = [
  { className: '[--card-spacing:--spacing(4)]', label: '16px', value: '4' },
  { className: '[--card-spacing:--spacing(5)]', label: '20px', value: '5' },
  { className: '[--card-spacing:--spacing(6)]', label: '24px', value: '6' },
  { className: '[--card-spacing:--spacing(8)]', label: '32px', value: '8' },
];

@Component({
  selector: 'z-demo-card-spacing',
  imports: [ZardCardImports, ZardButtonComponent, ...ZardFieldImports, ZardInputComponent, ZardToggleGroupComponent],
  template: `
    <div class="mx-auto grid w-full min-w-sm gap-4">
      <z-toggle-group
        zMode="single"
        zType="outline"
        zSize="sm"
        class="justify-center"
        [zItems]="items"
        [zValue]="spacing()"
        (valueChange)="onSpacingChange($event)"
      />
      <z-card [class]="selectedSpacing()">
        <z-card-header>
          <z-card-title zTitle="Login to your account" />
          <z-card-description zDescription="Enter your email below to login to your account" />
          <z-card-action>
            <a z-button zType="link" href="#">Sign Up</a>
          </z-card-action>
        </z-card-header>
        <z-card-content>
          <div z-field-group>
            <div z-field>
              <label z-field-label for="card-spacing-email">Email</label>
              <input z-input id="card-spacing-email" type="email" placeholder="m@example.com" required />
            </div>
            <div z-field>
              <div class="flex items-center">
                <label z-field-label for="card-spacing-password">Password</label>
                <a href="#" class="ms-auto text-sm underline-offset-4 hover:underline">Forgot your password?</a>
              </div>
              <input z-input id="card-spacing-password" type="password" required />
            </div>
          </div>
        </z-card-content>
        <z-card-footer class="flex-col gap-2">
          <z-button class="w-full">Login</z-button>
          <z-button zType="outline" class="w-full">Login with Google</z-button>
        </z-card-footer>
      </z-card>
    </div>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoCardSpacingComponent {
  readonly items: ZardToggleGroupItem[] = SPACING_OPTIONS.map(({ value, label }) => ({ value, label }));

  readonly spacing = signal('4');

  readonly selectedSpacing = computed(() => SPACING_OPTIONS.find(option => option.value === this.spacing())?.className);

  onSpacingChange(value: string | string[]) {
    const next = Array.isArray(value) ? value[0] : value;
    if (next) {
      this.spacing.set(next);
    }
  }
}

terms of service

A card with a scrollable content section and two footer actions, composed from z-card-content and a zFooterBorder-divided z-card-footer.
Terms of ServiceReview the terms before accepting the agreement.

These terms govern your use of the workspace, including access to shared documents, project files, and collaboration tools.

You are responsible for the content you upload and for ensuring that your team has the appropriate permissions to view or edit it.

We may update features or limits as the service evolves. When those changes materially affect your workflow, we will notify your workspace administrators.

By continuing, you agree to keep your account credentials secure and to follow your organization's acceptable use policies.

DeclineAccept
import { ChangeDetectionStrategy, Component } from '@angular/core';

import { ZardButtonComponent } from '@/shared/components/button';
import { ZardCardImports } from '@/shared/components/card/card.imports';

@Component({
  selector: 'z-demo-card-terms-of-service',
  imports: [ZardCardImports, ZardButtonComponent],
  template: `
    <z-card class="mx-auto w-full max-w-sm">
      <z-card-header>
        <z-card-title zTitle="Terms of Service" />
        <z-card-description zDescription="Review the terms before accepting the agreement." />
      </z-card-header>
      <z-card-content class="-mb-4">
        <div class="bg-muted/50 -mx-4 max-h-48 space-y-4 overflow-y-auto border-t p-4 text-sm/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>
      </z-card-content>
      <z-card-footer zFooterBorder class="justify-end gap-2">
        <z-button zType="outline">Decline</z-button>
        <z-button>Accept</z-button>
      </z-card-footer>
    </z-card>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoCardTermsOfServiceComponent {}

image

Add an image before the card header to create a card with an image.
Event coverFeaturedDesign systems meetupA practical talk on component APIs, accessibility, and shipping faster.View Event
import { NgOptimizedImage } from '@angular/common';
import { ChangeDetectionStrategy, Component } from '@angular/core';

import { ZardBadgeComponent } from '@/shared/components/badge';
import { ZardButtonComponent } from '@/shared/components/button';
import { ZardCardImports } from '@/shared/components/card/card.imports';

@Component({
  selector: 'z-demo-card-image',
  imports: [ZardCardImports, ZardButtonComponent, ZardBadgeComponent, NgOptimizedImage],
  template: `
    <z-card class="relative mx-auto w-full min-w-sm pt-0">
      <div class="absolute inset-0 z-30 aspect-video bg-black/35"></div>
      <img
        ngSrc="https://avatar.vercel.sh/zardui"
        alt="Event cover"
        class="relative z-20 aspect-video w-full object-cover brightness-60 grayscale dark:brightness-40"
        width="640"
        height="360"
      />
      <z-card-header>
        <z-card-action>
          <z-badge zType="secondary">Featured</z-badge>
        </z-card-action>
        <z-card-title zTitle="Design systems meetup" />
        <z-card-description
          zDescription="A practical talk on component APIs, accessibility, and shipping
          faster."
        />
      </z-card-header>
      <z-card-footer>
        <z-button class="w-full">View Event</z-button>
      </z-card-footer>
    </z-card>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoCardImageComponent {}

rtl

To enable RTL support in zard/ui, see the RTL configuration guide.
تسجيل الدخول إلى حسابكأدخل بريدك الإلكتروني أدناه لتسجيل الدخول إلى حسابكإنشاء حساب
تسجيل الدخولتسجيل الدخول باستخدام Google
// You will notice this example is rendered inside a wrapper that sets the dir and data-lang attributes, and
// reads its text from a translations helper. This is because this site is not RTL by default.
// In your application you won't need either: set dir="rtl" on <html> (or use the CDK Dir directive on a subtree)
// and write the text of your own language. See the RTL guide: https://zardui.com/docs/rtl

import { ChangeDetectionStrategy, Component } from '@angular/core';

import { ZardButtonComponent } from '@/shared/components/button/button.component';
import { ZardCardImports } from '@/shared/components/card/card.imports';
import { ZardFieldImports } from '@/shared/components/field/field.imports';
import { ZardInputComponent } from '@/shared/components/input/input.component';
import { injectRtlTranslation, type ZardRtlTranslations } from '@/shared/demo-utils/rtl-translation';

interface CardRtlValues {
  title: string;
  description: string;
  signUp: string;
  email: string;
  emailPlaceholder: string;
  password: string;
  forgotPassword: string;
  login: string;
  loginWithGoogle: string;
}

const translations: ZardRtlTranslations<CardRtlValues> = {
  en: {
    dir: 'ltr',
    values: {
      title: 'Login to your account',
      description: 'Enter your email below to login to your account',
      signUp: 'Sign Up',
      email: 'Email',
      emailPlaceholder: 'm@example.com',
      password: 'Password',
      forgotPassword: 'Forgot your password?',
      login: 'Login',
      loginWithGoogle: 'Login with Google',
    },
  },
  ar: {
    dir: 'rtl',
    values: {
      title: 'تسجيل الدخول إلى حسابك',
      description: 'أدخل بريدك الإلكتروني أدناه لتسجيل الدخول إلى حسابك',
      signUp: 'إنشاء حساب',
      email: 'البريد الإلكتروني',
      emailPlaceholder: 'm@example.com',
      password: 'كلمة المرور',
      forgotPassword: 'نسيت كلمة المرور؟',
      login: 'تسجيل الدخول',
      loginWithGoogle: 'تسجيل الدخول باستخدام Google',
    },
  },
  he: {
    dir: 'rtl',
    values: {
      title: 'התחבר לחשבון שלך',
      description: 'הזן את האימייל שלך למטה כדי להתחבר לחשבון שלך',
      signUp: 'הירשם',
      email: 'אימייל',
      emailPlaceholder: 'm@example.com',
      password: 'סיסמה',
      forgotPassword: 'שכחת את הסיסמה?',
      login: 'התחבר',
      loginWithGoogle: 'התחבר עם Google',
    },
  },
};

@Component({
  selector: 'z-demo-card-rtl',
  imports: [ZardCardImports, ZardButtonComponent, ...ZardFieldImports, ZardInputComponent],
  template: `
    <z-card class="w-full md:w-94">
      <z-card-header>
        <z-card-title [zTitle]="i18n.t().title" />
        <z-card-description [zDescription]="i18n.t().description" />
        <z-card-action>
          <a z-button zType="link" href="#">{{ i18n.t().signUp }}</a>
        </z-card-action>
      </z-card-header>
      <z-card-content>
        <div z-field-group>
          <div z-field>
            <label z-field-label for="card-rtl-email">{{ i18n.t().email }}</label>
            <input z-input id="card-rtl-email" type="email" [placeholder]="i18n.t().emailPlaceholder" required />
          </div>
          <div z-field>
            <div class="flex items-center">
              <label z-field-label for="card-rtl-password">{{ i18n.t().password }}</label>
              <a href="#" class="ms-auto text-sm underline-offset-4 hover:underline">{{ i18n.t().forgotPassword }}</a>
            </div>
            <input z-input id="card-rtl-password" type="password" required />
          </div>
        </div>
      </z-card-content>
      <z-card-footer class="flex-col gap-2">
        <z-button class="w-full">{{ i18n.t().login }}</z-button>
        <z-button zType="outline" class="w-full">{{ i18n.t().loginWithGoogle }}</z-button>
      </z-card-footer>
    </z-card>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoCardRtlComponent {
  protected readonly i18n = injectRtlTranslation(translations);
}

API Reference

z-card, [z-card]Component

A structured container for displaying content with optional header and footer sections.

PropertyDescriptionTypeDefault
[class] Custom CSS classes ClassValue ''
[zSize] Size variant of the card. `sm` sets `--card-spacing` to `--spacing(3)` and shrinks the title 'default' | 'sm' 'default'
[--card-spacing] CSS variable behind every gap and padding of the card and its sections, e.g. `class="[--card-spacing:--spacing(6)]"` length --spacing(4)

z-card-header, [z-card-header]Component

Container for card title, description, and optional action.

PropertyDescriptionTypeDefault
[class] Custom CSS classes ClassValue ''
[zHeaderBorder] Adds a bottom border to the header boolean false

z-card-title, [z-card-title]Component

Card title text or template.

PropertyDescriptionTypeDefault
[class] Custom CSS classes ClassValue ''
[zTitle] Title content — string or template reference string | TemplateRef<void> | undefined -

z-card-description, [z-card-description]Component

Card description text or template.

PropertyDescriptionTypeDefault
[class] Custom CSS classes ClassValue ''
[zDescription] Description content — string or template reference string | TemplateRef<void> | undefined -

z-card-action, [z-card-action]Component

Action button displayed in the card header.

PropertyDescriptionTypeDefault
[class] Custom CSS classes ClassValue ''

z-card-content, [z-card-content]Component

Main content area of the card.

PropertyDescriptionTypeDefault
[class] Custom CSS classes ClassValue ''

z-card-footer, [z-card-footer]Component

Footer section of the card.

PropertyDescriptionTypeDefault
[class] Custom CSS classes ClassValue ''
[zFooterBorder] Adds a top border to the footer boolean false
github iconwhatsapp icondiscord iconX icon

Made with ❤ in Brazil. Open source and available on GitHub .