AvesraAvesrabeta

Card

Flexible container for grouping related content and actions. Cards extend the surface system with header, title, description, content, and footer slots plus semantic surface variants.

Import

import { AvCardImports } from '@avesra/angular';

Usage

Apply av-card to a container and compose optional header, content, and footer regions.

Become an Acme Creator!

Visit the Acme Creator Hub to sign up today and start earning credits from your fans and followers.

import { Component } from '@angular/core';
import {
  AvCardImports,
  AvLinkImports,
} from '@avesra/angular';

import { AppIconComponent } from '../../components/app-icon/app-icon.component';

@Component({
  selector: 'app-card-basic-demo',
  imports: [
    AvCardImports,
    AvLinkImports,
    AppIconComponent,
  ],
  template: `<div av-card class="w-[400px]">
      <span class="self-start" role="img" aria-label="Dollar sign icon">
        <app-icon icon="solar:dollar-linear" size="24" class="text-foreground" />
      </span>
      <div av-card-header>
        <h3 av-card-title>Become an Acme Creator!</h3>
        <p av-card-description>
          Visit the Acme Creator Hub to sign up today and start earning credits from your fans and
          followers.
        </p>
      </div>
      <div av-card-footer>
        <a
          av-link
          aria-label="Go to Acme Creator Hub (opens in new tab)"
          href="https://github.com/"
          rel="noopener noreferrer"
          target="_blank"
        >
          Creator Hub
          <span av-link-icon></span>
        </a>
      </div>
    </div>`,
})
export class CardBasicDemo {}

Anatomy

Import the Card components and compose the layout with av-card, av-card-header, av-card-title, av-card-description, av-card-content, and av-card-footer.

<div av-card>
  <div av-card-header>
    <h3 av-card-title></h3>
    <p av-card-description></p>
  </div>
  <div av-card-content></div>
  <div av-card-footer></div>
</div>

Variants

Cards come in semantic variants that describe their prominence level rather than specific visual styles. This allows themes to interpret them differently:

Transparent

Minimal prominence with transparent background

Use for less important content or nested cards

Default

Standard card appearance (bg-surface)

The default card variant for most use cases

Secondary

Medium prominence (bg-surface-secondary)

Use to draw moderate attention

Tertiary

Higher prominence (bg-surface-tertiary)

Use for primary or featured content

import { Component } from '@angular/core';
import { AvCardImports } from '@avesra/angular';

@Component({
  selector: 'app-card-variants-demo',
  imports: [AvCardImports],
  template: `<div class="flex flex-col gap-4">
      <div av-card class="w-[320px]" variant="transparent">
        <div av-card-header>
          <h3 av-card-title>Transparent</h3>
          <p av-card-description>Minimal prominence with transparent background</p>
        </div>
        <div av-card-content>
          <p>Use for less important content or nested cards</p>
        </div>
      </div>

      <div av-card class="w-[320px]" variant="default">
        <div av-card-header>
          <h3 av-card-title>Default</h3>
          <p av-card-description>Standard card appearance (bg-surface)</p>
        </div>
        <div av-card-content>
          <p>The default card variant for most use cases</p>
        </div>
      </div>

      <div av-card class="w-[320px]" variant="secondary">
        <div av-card-header>
          <h3 av-card-title>Secondary</h3>
          <p av-card-description>Medium prominence (bg-surface-secondary)</p>
        </div>
        <div av-card-content>
          <p>Use to draw moderate attention</p>
        </div>
      </div>

      <div av-card class="w-[320px]" variant="tertiary">
        <div av-card-header>
          <h3 av-card-title>Tertiary</h3>
          <p av-card-description>Higher prominence (bg-surface-tertiary)</p>
        </div>
        <div av-card-content>
          <p>Use for primary or featured content</p>
        </div>
      </div>
    </div>`,
})
export class CardVariantsDemo {}
  • transparent — Minimal prominence, transparent background (great for nested cards)
  • default — Standard card for most use cases (surface-secondary)
  • secondary — Medium prominence to draw moderate attention (surface-tertiary)
  • tertiary — Higher prominence for important content (surface-tertiary)

Horizontal Layout

Yellow pears

Become an ACME Creator!

Lorem ipsum dolor sit amet consectetur. Sed arcu donec id aliquam dolor sed amet faucibus etiam.

import { Component } from '@angular/core';
import {
  AvButtonComponent,
  AvCardImports,
  AvCloseButtonComponent,
} from '@avesra/angular';

@Component({
  selector: 'app-card-horizontal-with-image-demo',
  imports: [
    AvCardImports,
    AvButtonComponent,
    AvCloseButtonComponent,
  ],
  template: `<div av-card class="w-full items-stretch md:flex-row">
      <div
        class="relative h-[140px] w-full shrink-0 overflow-hidden rounded-2xl sm:h-[120px] sm:w-[120px]"
      >
        <img
          alt="Yellow pears"
          class="pointer-events-none absolute inset-0 h-full w-full scale-125 object-cover select-none"
          loading="lazy"
          src="/images/food/food-yellow-pears-pair.png"
        />
      </div>
      <div class="flex flex-1 flex-col gap-3">
        <div av-card-header class="gap-1">
          <h3 av-card-title class="pr-8">Become an ACME Creator!</h3>
          <p av-card-description>
            Lorem ipsum dolor sit amet consectetur. Sed arcu donec id aliquam dolor sed amet
            faucibus etiam.
          </p>
          <button
            av-close-button
            aria-label="Close banner"
            class="absolute top-3 right-3"
          ></button>
        </div>
        <div
          av-card-footer
          class="mt-auto flex w-full flex-col items-start gap-3 sm:flex-row sm:items-center sm:justify-between"
        >
          <div class="flex flex-col">
            <span class="text-sm font-medium text-foreground">Only 10 spots</span>
            <span class="text-xs text-muted">Submission ends Oct 10.</span>
          </div>
          <button av-button class="w-full sm:w-auto">Apply Now</button>
        </div>
      </div>
    </div>`,
})
export class CardHorizontalWithImageDemo {}

With Avatar

Indie Hackers community

Indie Hackers

148 members

AI Builders community

AI Builders

362 members

import { Component } from '@angular/core';
import {
  AvAvatarImports,
  AvCardImports,
} from '@avesra/angular';

@Component({
  selector: 'app-card-with-avatar-demo',
  imports: [
    AvCardImports,
    AvAvatarImports,
  ],
  template: `<div class="flex flex-wrap gap-4">
      <div av-card class="w-[200px] gap-2">
        <img
          alt="Indie Hackers community"
          class="pointer-events-none aspect-square w-14 rounded-2xl object-cover select-none"
          loading="lazy"
          src="/images/objects/object-yellow-mushroom-lamp.png"
        />
        <div av-card-header>
          <h3 av-card-title>Indie Hackers</h3>
          <p av-card-description>148 members</p>
        </div>
        <div av-card-footer class="flex gap-2">
          <span av-avatar class="size-5" aria-label="Martha's profile picture">
            <img
              av-avatar-image
              alt="Martha's avatar"
              src="/images/gradients/gradient-pink-magenta.png"
            />
            <span av-avatar-fallback class="text-xs">IH</span>
          </span>
          <span class="text-xs">By Martha</span>
        </div>
      </div>

      <div av-card class="w-[200px] gap-2">
        <img
          alt="AI Builders community"
          class="pointer-events-none aspect-square w-14 rounded-2xl object-cover select-none"
          loading="lazy"
          src="/images/avatars/avatar-woman-orange-jacket.png"
        />
        <div av-card-header>
          <h3 av-card-title>AI Builders</h3>
          <p av-card-description>362 members</p>
        </div>
        <div av-card-footer class="flex gap-2">
          <span av-avatar class="size-5" aria-label="John's profile picture">
            <img
              av-avatar-image
              alt="John's avatar - blue themed"
              src="/images/gradients/gradient-blue-cyan.png"
            />
            <span av-avatar-fallback class="text-xs">B</span>
          </span>
          <span class="text-xs">By John</span>
        </div>
      </div>
    </div>`,
})
export class CardWithAvatarDemo {}

With Images

Yellow pears

Become an ACME Creator!

Lorem ipsum dolor sit amet consectetur. Sed arcu donec id aliquam dolor sed amet faucibus etiam.

PAYMENT

You can now withdraw on crypto

Add your wallet in settings to withdraw

Indie HackersJK

Indie Hackers

148 members

AI BuildersAB

AI Builders

362 members

NEO

Home Robot

Astronaut

Bridging the Future

Today, 6:30 PM

Peaches on ice

Avocado Hackathon

Wed, 4:30 PM

Blood orange

Sound Electro | Beyond art

Fri, 8:00 PM

import { Component } from '@angular/core';
import {
  AvAvatarImports,
  AvButtonComponent,
  AvCardImports,
  AvCloseButtonComponent,
  AvLinkImports,
} from '@avesra/angular';

import { AppIconComponent } from '../../components/app-icon/app-icon.component';

@Component({
  selector: 'app-card-with-images-demo',
  imports: [
    AvCardImports,
    AvButtonComponent,
    AvCloseButtonComponent,
    AvLinkImports,
    AvAvatarImports,
    AppIconComponent,
  ],
  template: `<div class="flex w-full items-center justify-center">
      <div class="grid w-full max-w-2xl grid-cols-12 gap-4 p-4">
        <div av-card class="col-span-12 flex h-auto min-h-[152px] flex-col sm:flex-row">
          <div
            class="relative h-[140px] w-full shrink-0 overflow-hidden rounded-2xl sm:h-[120px] sm:w-[120px]"
          >
            <img
              alt="Yellow pears"
              class="pointer-events-none absolute inset-0 h-full w-full scale-125 object-cover select-none"
              loading="lazy"
              src="/images/food/food-yellow-pears-pair.png"
            />
          </div>
          <div class="flex flex-1 flex-col gap-3">
            <div av-card-header class="gap-1">
              <h3 av-card-title class="pr-8">Become an ACME Creator!</h3>
              <p av-card-description>
                Lorem ipsum dolor sit amet consectetur. Sed arcu donec id aliquam dolor sed amet
                faucibus etiam.
              </p>
              <button
                av-close-button
                aria-label="Close banner"
                class="absolute top-3 right-3"
              ></button>
            </div>
            <div
              av-card-footer
              class="mt-auto flex w-full flex-col items-start gap-3 sm:flex-row sm:items-center sm:justify-between"
            >
              <div class="flex flex-col">
                <span class="text-sm font-medium text-foreground">Only 10 spots</span>
                <span class="text-xs text-muted">Submission ends Oct 10.</span>
              </div>
              <button av-button class="w-full sm:w-auto">Apply Now</button>
            </div>
          </div>
        </div>

        <div class="col-span-12 grid grid-cols-12 gap-4">
          <div class="col-span-12 grid grid-cols-12 gap-4 lg:col-span-6">
            <div av-card class="col-span-12">
              <div class="absolute top-3 right-3 z-10">
                <button av-close-button aria-label="Close notification"></button>
              </div>
              <div av-card-header class="flex-row items-start gap-3">
                <span role="img" aria-label="Dollar sign icon" class="shrink-0">
                  <app-icon icon="solar:dollar-linear" size="32" class="text-accent" />
                </span>
                <div class="flex flex-col gap-1">
                  <span class="text-xs font-medium text-muted uppercase">PAYMENT</span>
                  <h3 av-card-title class="pr-8 text-sm sm:text-base">
                    You can now withdraw on crypto
                  </h3>
                  <p av-card-description class="text-xs sm:text-sm">
                    Add your wallet in settings to withdraw
                  </p>
                </div>
              </div>
              <div av-card-footer>
                <a av-link aria-label="Go to settings" href="#" rel="noopener noreferrer">
                  Go to settings
                  <span av-link-icon></span>
                </a>
              </div>
            </div>

            <div class="col-span-12 grid grid-cols-12 gap-4">
              <div av-card class="col-span-12 gap-2 sm:col-span-6">
                <div av-card-header>
                  <span av-avatar class="size-[56px] rounded-xl">
                    <img
                      av-avatar-image
                      alt="Indie Hackers"
                      src="/images/objects/object-yellow-mushroom-lamp.png"
                    />
                    <span av-avatar-fallback>JK</span>
                  </span>
                </div>
                <div av-card-content class="mt-1">
                  <p class="text-sm leading-4 font-medium">Indie Hackers</p>
                  <p class="text-xs text-muted">148 members</p>
                </div>
                <div av-card-footer class="flex items-center gap-2">
                  <span av-avatar class="size-4">
                    <img
                      av-avatar-image
                      alt="John"
                      src="/images/gradients/gradient-pink-magenta.png"
                    />
                    <span av-avatar-fallback>JK</span>
                  </span>
                  <p class="text-xs text-muted">By John</p>
                </div>
              </div>

              <div av-card class="col-span-12 gap-2 sm:col-span-6">
                <div av-card-header>
                  <span av-avatar class="size-[56px] rounded-xl">
                    <img
                      av-avatar-image
                      alt="AI Builders"
                      src="/images/avatars/avatar-woman-orange-jacket.png"
                    />
                    <span av-avatar-fallback>AB</span>
                  </span>
                </div>
                <div av-card-content class="mt-1">
                  <p class="text-sm leading-4 font-medium">AI Builders</p>
                  <p class="text-xs text-muted">362 members</p>
                </div>
                <div av-card-footer class="flex items-center gap-2">
                  <span av-avatar class="size-4">
                    <img
                      av-avatar-image
                      alt="John"
                      src="/images/gradients/gradient-blue-cyan.png"
                    />
                    <span av-avatar-fallback>M</span>
                  </span>
                  <p class="text-xs text-muted">By Martha</p>
                </div>
              </div>
            </div>
          </div>

          <div
            av-card
            class="col-span-12 min-h-[200px] overflow-hidden rounded-3xl lg:col-span-6"
          >
            <img
              alt="NEO Home Robot"
              aria-hidden="true"
              class="absolute inset-0 h-full w-full object-cover"
              src="/images/characters/character-robot-rear-gold-slit.png"
            />
            <div av-card-header class="z-10 text-white">
              <h3 av-card-title class="text-xs font-semibold tracking-wide text-black/70">NEO</h3>
              <p av-card-description class="text-sm leading-5 font-medium text-black/50">
                Home Robot
              </p>
            </div>
            <div av-card-footer class="z-10 mt-auto flex items-center justify-between">
              <div>
                <div class="text-sm font-medium text-black">Available soon</div>
                <div class="text-xs text-black/60">Get notified</div>
              </div>
              <button av-button class="bg-white text-black" size="sm" variant="tertiary">
                Notify me
              </button>
            </div>
          </div>
        </div>

        <div class="col-span-12 grid grid-cols-12 gap-4">
          <div
            av-card
            class="relative col-span-12 h-[250px] sm:h-[300px] md:col-span-8 md:h-[350px]"
          >
            <img
              alt="NEO Home Robot"
              aria-hidden="true"
              class="absolute inset-0 h-full w-full object-cover"
              src="/images/characters/character-robot-front-eyes.png"
            />
            <div av-card-footer class="z-10 mt-auto flex items-end justify-between">
              <div>
                <div class="text-base font-medium text-black sm:text-lg">NEO</div>
                <div class="text-xs font-medium text-black/50 sm:text-sm">$499/m</div>
              </div>
              <button av-button class="bg-white text-black" size="sm" variant="tertiary">
                Get now
              </button>
            </div>
          </div>

          <div
            class="col-span-12 flex flex-col gap-2 md:col-span-4 md:justify-between md:gap-0 md:py-2"
          >
            <div av-card class="flex flex-row gap-3 p-1" variant="transparent">
              <img
                alt="Astronaut"
                class="aspect-square h-16 w-16 shrink-0 rounded-xl object-cover select-none sm:h-20 sm:w-20"
                loading="lazy"
                src="/images/characters/character-astronaut-white-suit.png"
              />
              <div class="flex flex-1 flex-col justify-center gap-1">
                <h3 av-card-title class="text-sm">Bridging the Future</h3>
                <p av-card-description class="text-xs">Today, 6:30 PM</p>
              </div>
            </div>
            <div av-card class="flex flex-row gap-3 p-1" variant="transparent">
              <img
                alt="Peaches on ice"
                class="aspect-square h-16 w-16 shrink-0 rounded-xl object-cover select-none sm:h-20 sm:w-20"
                loading="lazy"
                src="/images/food/food-peaches-ice-blue.png"
              />
              <div class="flex flex-1 flex-col justify-center gap-1">
                <h3 av-card-title class="text-sm">Avocado Hackathon</h3>
                <p av-card-description class="text-xs">Wed, 4:30 PM</p>
              </div>
            </div>
            <div av-card class="flex flex-row gap-3 p-1" variant="transparent">
              <img
                alt="Blood orange"
                class="aspect-square h-16 w-16 shrink-0 rounded-xl object-cover select-none sm:h-20 sm:w-20"
                loading="lazy"
                src="/images/food/food-blood-orange-half.png"
              />
              <div class="flex flex-1 flex-col justify-center gap-1">
                <h3 av-card-title class="text-sm">Sound Electro | Beyond art</h3>
                <p av-card-description class="text-xs">Fri, 8:00 PM</p>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>`,
})
export class CardWithImagesDemo {}

With Form

Login

Enter your credentials to access your account

import { Component } from '@angular/core';
import {
  AvButtonComponent,
  AvCardImports,
  AvFormComponent,
  AvInputComponent,
  AvLabelComponent,
  AvLinkImports,
} from '@avesra/angular';

@Component({
  selector: 'app-card-with-form-demo',
  imports: [
    AvCardImports,
    AvFormComponent,
    AvLabelComponent,
    AvInputComponent,
    AvButtonComponent,
    AvLinkImports,
  ],
  template: `<div av-card class="w-full max-w-md">
      <div av-card-header>
        <h3 av-card-title>Login</h3>
        <p av-card-description>Enter your credentials to access your account</p>
      </div>
      <form av-form (submit)="onSubmit($event)">
        <div av-card-content>
          <div class="flex flex-col gap-4">
            <div class="flex flex-col gap-1">
              <label av-label for="card-form-email">Email</label>
              <input
                av-input
                full-width
                id="card-form-email"
                name="email"
                type="email"
                placeholder="email&#64;example.com"
                variant="secondary"
              />
            </div>
            <div class="flex flex-col gap-1">
              <label av-label for="card-form-password">Password</label>
              <input
                av-input
                full-width
                id="card-form-password"
                name="password"
                type="password"
                placeholder="••••••••"
                variant="secondary"
              />
            </div>
          </div>
        </div>
        <div av-card-footer class="mt-4 flex flex-col gap-2">
          <button av-button class="w-full" type="submit">Sign In</button>
          <a av-link class="text-center text-sm" href="#">Forgot password?</a>
        </div>
      </form>
    </div>`,
})
export class CardWithFormDemo {
  onSubmit(event: Event): void {
    event.preventDefault();

    const form = event.target as HTMLFormElement;
    const formData = new FormData(form);
    const data: Record<string, string> = {};

    formData.forEach((value, key) => {
      data[key] = value.toString();
    });

    alert('Form submitted successfully!');
  }
}

Styling

Pass Tailwind utilities on the card host, or override BEM classes in @layer components.

CSS Classes

  • .av-card — Base card container
  • .av-card__header, .av-card__title, .av-card__description — Header region
  • .av-card__content — Main content area
  • .av-card__footer — Footer actions row
  • .av-card--transparent, .av-card--default, .av-card--secondary, .av-card--tertiary — Surface variants

Accessibility

Cards are presentational containers. For interactive cards, use semantic elements (such as <a>) with an aria-label, or add role="article" with aria-labelledby pointing at av-card-title.

API

Card root accepts a variant input; header, title, description, content, and footer parts have no inputs.

PropTypeDefaultDescription
variant'transparent' | 'default' | 'secondary' | 'tertiary''default'Visual surface variant (div[av-card]).

Made with ❤ by SyntaxHertz. Open source and available on GitHub.