Cards✨ Premium

Card Morph

The card scales up on click via a back-ease-out curve, fades out its preview and reveals full content with a scale-in fade — closes on an outside click.

JSClickExpand

You'll get access to the interactive demo with a free account.

This effect is part of Effect.Labs — 811 vanilla effects, some free, some premium. Cards has 41 effects, including 6 free. Explore the category →

3 usage examples

SaaS Dashboard — Interactive feature card — Card Morph example 1

① SaaS Dashboard — Interactive feature card

WhenIn a dashboard or pricing page, features are laid out as a grid of compact cards. The user clicks a card to read its full description without leaving the page.
WhyThe elastic curve and purple glow make each card distinctly clickable — detail appears inside the card itself, without a modal or outbound link.
Settingstransform: scale(1.08) to stay within parent grid boundaries, box-shadow in brand color (replace rgba(99,102,241,.3)), transition: .4s ease-out for a crisper open.
Portfolio — Project preview that opens without page navigation — Card Morph example 2

② Portfolio — Project preview that opens without page navigation

WhenOn a project gallery, each card shows a compact preview. The user clicks to read the summary and tech stack before deciding to navigate.
WhyThe scale-in fade of .card-full-content mimics opening a folder — the project visually steps forward without leaving the grid.
Settings--radius-xl: 0.5rem for sharper corners, replace the icon with a thumbnail image inside .card-preview, --text-muted: #a0aec0 for a lighter secondary text.
Music catalog — Album card with quick-view — Card Morph example 3

③ Music catalog — Album card with quick-view

WhenIn a player or storefront grid, clicking an album card opens its details (title, artist, duration) without interrupting current playback.
Whyscale(1.1) and z-index: 10 on the expanded card make the album pop out of the grid — a strong selection signal without a full-screen modal.
Settingstransform: scale(1.15) for a bolder expansion, replace the icon gradient with the album art's dominant color, z-index: 20 on .card-morph-expand.expanded to layer above neighboring cards.

How it works

A click handler on .card-morph-expand calls classList.toggle('expanded'). A second listener on document detects outside clicks and removes the expanded class automatically — no close button needed.

The expansion is driven by a CSS transition: transition: .5s cubic-bezier(.175, .885, .32, 1.275). This back ease-out curve produces a slight overshoot then an elastic settle at the final scale(1.1) — no animation JavaScript at all. A purple drop shadow appears simultaneously via box-shadow.

The content swap relies on two layers stacked with position: absolute; inset: 0. The preview layer (.card-preview) fades to opacity: 0 in 0.3 s. The content layer (.card-full-content) starts at opacity: 0; transform: scale(.9) and transitions to opacity: 1; transform: scale(1) in 0.4 s, creating a centred scale-in reveal.

No requestAnimationFrame is used: the effect is 100% CSS transitions triggered by a class toggle. .card-morph-expand carries overflow: hidden — content never bleeds outside the rounded corners, even at scale(1.1).

Accessibility

  • prefers-reduced-motion absent: the code has no prefers-reduced-motion media query — scale and opacity transitions always run, even when the OS requests reduced motion. Add it manually if needed.
  • The clickable element is a <div> with no role="button" or keydown handler — keyboard-inaccessible (Tab does not reach it, Enter/Space have no effect).
  • No aria-expanded attribute: screen readers are not notified of the open/closed state.
  • Secondary text (--text-muted: #8b8b93) on #12121a background achieves a contrast ratio of approximately 4.7:1 — WCAG AA compliant (≥ 4.5:1).
  • Click target (280 × 180 px by default) far exceeds the 44 × 44 px WCAG 2.5.5 minimum.

Browser compatibility

Requires CSS transitions and classList — supported in all modern browsers since 2017. Zero external dependencies.

Chrome 60+✓ Full
Firefox 55+✓ Full
Safari 12+✓ Full
Edge 79+✓ Full
Mobile iOS✓ Touch OK
Android Chrome✓ Full

Without JS, the card stays in its initial state (preview visible). CSS styles are self-contained — no flash, no broken layout.

The code

HTML structure to paste into your page (CSS + JS available with a premium account):

index.html — structure
<div class="demo-preview">
  <div class="card-demo card-morph-expand" id="morphExpandCard">
    <div class="card-preview">
      <div class="card-preview-icon">
        <!-- 24×24 SVG icon -->
      </div>
      <span>Click</span>
    </div>
    <div class="card-full-content">
      <h5>Revealed Content Title</h5>
      <p>Detailed description shown on expand.</p>
    </div>
  </div>
</div>
🔒 Unlock the full code — from €2.99 the first month

Full HTML + CSS + JS, copy-paste ready — with hundreds of premium effects.

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
--bg-card #12121a Card background color — replace with any color from your design system.
--radius-xl 1rem Corner radius — 0.5rem for a card look, 1.5rem for a pill look.
--border rgba(255,255,255,0.08) Card border color at rest — increase opacity for a more visible outline on light backgrounds.
--text-primary #ffffff Title color in the revealed content.
--text-muted #8b8b93 Secondary text color in the revealed content (WCAG AA ratio ≈ 4.7:1 on default --bg-card).
transition: .5s cubic-bezier(.175,.885,.32,1.275) .5s back-ease-out Duration and curve of the expansion — .3s ease-out for a snappier feel, .7s for a more theatrical gesture.
transform: scale(1.1) scale(1.1) Expansion amount in .card-morph-expand.expandedscale(1.05) for subtle, scale(1.2) for dramatic.
box-shadow rgba(99,102,241,.3) 0 25px 50px Shadow color and spread on expand — change the RGBA color to match your brand accent.
linear-gradient(135deg,#6366f1,#8b5cf6) indigo→violet Gradient of the round icon in .card-preview-icon — replace with brand colors or a background image.

FAQ

The original JS targets the id morphExpandCard — one instance only. For multiple cards, replace the getElementById with a loop: document.querySelectorAll('.card-morph-expand').forEach(card => { card.addEventListener('click', () => card.classList.toggle('expanded')); }); and add document.addEventListener('click', e => document.querySelectorAll('.card-morph-expand').forEach(c => { if (!c.contains(e.target)) c.classList.remove('expanded'); })).
The clickable <div> is not focusable natively. Two fixes: replace it with a <button type="button" class="card-morph-expand"> (natively focusable, Enter/Space trigger click), or add tabindex="0" plus a keydown listener that calls classList.toggle('expanded') on Enter and Space. Also update aria-expanded via JS.
Yes. Split the transitions: on .card-morph-expand define transition: transform .35s ease-in, box-shadow .35s ease-in (fast close), then on .card-morph-expand.expanded override with transition: transform .5s cubic-bezier(.175,.885,.32,1.275), box-shadow .5s ease-out (elastic open). CSS applies the active rule at transition time.