Card

Stable

A contained surface for grouping related content. Commonly used for blog posts, services, team members, or any repeating content unit.


Recent decision: card media is content-driven (no fixed height), and the focus ring uses the canonical interactive.focus role. See Decisions.

Anatomy

Cards are built from slots. Mix and match — you don't need all of them. The only required piece is vi-card on the wrapper.

vi-card__media
vi-card__header New
vi-card__eyebrow

vi-card__title

This is the vi-card__text slot. It carries supporting copy and sits inside vi-card__body.

HTML — full anatomy
<article class="vi-card vi-card--raised">

  <!-- Optional: image or video at top -->
  <div class="vi-card__media">
    <img src="..." alt="...">
  </div>

  <!-- Optional: badges or labels above body -->
  <div class="vi-card__header">
    <span class="vi-badge vi-badge--brand">New</span>
  </div>

  <!-- Main content -->
  <div class="vi-card__body">
    <span class="vi-card__eyebrow">Category</span>
    <h3 class="vi-card__title">Card Title</h3>
    <p class="vi-card__text">Supporting description copy.</p>
    <div class="vi-card__actions">
      <button class="vi-btn vi-btn--secondary vi-btn--sm">Learn More</button>
    </div>
  </div>

  <!-- Optional: action buttons pinned to bottom -->
  <div class="vi-card__footer">
    <button class="vi-btn vi-btn--ghost vi-btn--sm">Cancel</button>
    <button class="vi-btn vi-btn--primary vi-btn--sm">Confirm</button>
  </div>

</article>

Variants

Flat

vi-card--flat

Border only, no shadow. Good on white backgrounds where shadows feel too heavy.

Raised

vi-card--raised

Subtle shadow, no border. Best for cards on subtle/grey page backgrounds.

Bordered

vi-card--bordered

Strong 2px border. Use for selected states, featured items, or form-like cards.

HTML
<article class="vi-card vi-card--flat">...</article>
<article class="vi-card vi-card--raised">...</article>
<article class="vi-card vi-card--bordered">...</article>

Interactive cards

Add vi-card--interactive when the entire card is clickable (e.g. blog post cards, service tiles). Hover to see the lift effect.

HTML — whole card as link
<a href="/post" class="vi-card vi-card--raised vi-card--interactive">
  <div class="vi-card__body">
    <span class="vi-card__eyebrow">Blog Post</span>
    <h3 class="vi-card__title">Post Title</h3>
    <p class="vi-card__text">Excerpt copy here.</p>
  </div>
</a>

Minimal card

You don't need all the slots. A card with just a body is completely valid.

Simple Card

Just a title and text. No media, no footer, no eyebrow needed.

HTML — minimal
<div class="vi-card vi-card--flat">
  <div class="vi-card__body">
    <h3 class="vi-card__title">Simple Card</h3>
    <p class="vi-card__text">Just title and text.</p>
  </div>
</div>

WordPress usage

PHP — WordPress post loop
<?php if ( have_posts() ) : while ( have_posts() ) : the_post(); ?>

  <article class="vi-card vi-card--raised vi-card--interactive">
    <?php if ( has_post_thumbnail() ) : ?>
    <div class="vi-card__media">
      <?php the_post_thumbnail('medium'); ?>
    </div>
    <?php endif; ?>

    <div class="vi-card__body">
      <span class="vi-card__eyebrow"><?php the_category(', '); ?></span>
      <h3 class="vi-card__title">
        <a href="<?php the_permalink(); ?>" style="color:inherit; text-decoration:none;">
          <?php the_title(); ?>
        </a>
      </h3>
      <p class="vi-card__text"><?php the_excerpt(); ?></p>
    </div>
  </article>

<?php endwhile; endif; ?>

Class reference

ClassTypeDescription
vi-cardBaseRequired wrapper. Sets background, border-radius, overflow, flex column layout.
vi-card--flatVariantBorder only, no shadow.
vi-card--raisedVariantDrop shadow, no border. Default feel.
vi-card--borderedVariantStrong 2px border.
vi-card--interactiveModifierHover lift animation. Use when whole card is clickable.
vi-card__mediaSlotImage/video area at top of card. Content-driven — media keeps its intrinsic height.
vi-card__headerSlotRow above body — for badges or labels.
vi-card__bodySlotMain content area. Flex column with gap.
vi-card__footerSlotActions row pinned to bottom. Grey background.
vi-card__footer--space-betweenSlot modifierJustify footer content to space-between.
vi-card__eyebrowTypographySmall all-caps label above title.
vi-card__titleTypographyCard heading.
vi-card__textTypographySupporting copy. Grows to fill available body space.
vi-card__actionsTypographyButton row inside body (above footer).