M4RKYU.SYSEdition 2027
Skip to content
LOCEN/Ontario · CA/▸logs · create a heart shaped animation with html5 css3 and javascript 1pjhStandbyOK/--:--:--EST
M4M4RK_YUportfolio
  • BuildBuild
    BuildOverview
    • WorkSelected case studies and write-ups
    • GamesPlayable prototypes and game-dev logs
  • GalleryGallery
    GalleryOverview
    • PhotosPhoto collections and visual experiments
    • ShopPrints, posters, and one-off objects
  • WritingWriting
    WritingOverview
    • BlogLong-form devlogs and field notes
    • NotesShort observations, links, snippets
  • ResourcesResources
    ResourcesOverview
    • Tools38 in-browser developer utilities
    • LinksDaily-use dev and design bookmarks
  • AboutAbout
  • ContactContact
中文

syndicated · dev.to / @markyu

CSS Heart Animation: Small Demo, Real Animation Lessons

A cleaner CSS heart animation tutorial focused on transform, pseudo-elements, keyframes, and the small mistakes that break simple UI animations.

Published
May 22 '24
·
Reading time
2 min read
·
Reactions
9
cssjavascriptfrontendanimation
View on dev.to

On this page

  • The HTML
  • The CSS Shape
  • Add the Pulse
  • Add a Click State With JavaScript
  • Respect Reduced Motion
  • What This Demo Teaches
  • Final Thought

Small CSS animations are underrated.

They look like toy demos, but they teach the same things you need for real UI work: transforms, timing, layout, pseudo-elements, and not accidentally triggering expensive repaint work.

Let’s build a heart animation with mostly CSS.

The HTML

Keep the markup boring:

<main class="stage">
  <button class="heart-button" aria-label="Like">
    <span class="heart"></span>
  </button>
</main>

The button gives us keyboard accessibility. The span handles the shape.

The CSS Shape

A heart is basically a rotated square with two circles attached.

.stage {
  min-height: 100vh;
  display: grid;
  place-items: center;
  background: #111827;
}

.heart-button {
  border: 0;
  background: transparent;
  cursor: pointer;
  padding: 48px;
}

.heart {
  position: relative;
  display: block;
  width: 80px;
  height: 80px;
  background: #ef4444;
  transform: rotate(45deg);
}

.heart::before,
.heart::after {
  content: "";
  position: absolute;
  width: 80px;
  height: 80px;
  border-radius: 50%;
  background: #ef4444;
}

.heart::before {
  left: -40px;
}

.heart::after {
  top: -40px;
}

Visual model:

circle + circle
   \     /
 rotated square

That is the whole trick.

Add the Pulse

Use transform, not layout properties.

.heart-button:hover .heart,
.heart-button:focus-visible .heart {
  animation: pulse 700ms ease-in-out infinite;
}

@keyframes pulse {
  0% {
    transform: rotate(45deg) scale(1);
  }
  45% {
    transform: rotate(45deg) scale(1.18);
  }
  100% {
    transform: rotate(45deg) scale(1);
  }
}

The common mistake is forgetting the rotation inside the keyframes. If you animate only scale(), the heart can snap back because transform gets replaced.

Bad:

transform: scale(1.2);

Good:

transform: rotate(45deg) scale(1.2);

Add a Click State With JavaScript

CSS hover is nice, but click feedback feels better.

const button = document.querySelector(".heart-button");

button.addEventListener("click", () => {
  button.classList.toggle("is-liked");
});
.heart-button.is-liked .heart,
.heart-button.is-liked .heart::before,
.heart-button.is-liked .heart::after {
  background: #fb7185;
}

.heart-button.is-liked .heart {
  box-shadow: 0 0 48px rgba(251, 113, 133, 0.55);
}

Respect Reduced Motion

This is a small demo, but accessibility still counts.

@media (prefers-reduced-motion: reduce) {
  .heart-button:hover .heart,
  .heart-button:focus-visible .heart {
    animation: none;
  }
}

I would add this even for playful UI. Not every user wants pulsing motion.

What This Demo Teaches

TechniqueReal UI use
::before / ::aftericons, badges, decorative layers
transformcheap animation
@keyframesreusable motion
focus-visiblekeyboard-friendly interaction
reduced motionaccessibility polish

Final Thought

This is not just a heart animation. It is a tiny lab for learning how browser motion behaves.

What small CSS animation taught you more than you expected?

Related reading

CSS 3D Transform Bugs Usually Come From Perspective

A practical CSS 3D transform guide explaining perspective, rotateX, rotateY, transform-style, backface visibility, and debugging layout.

css

React Loading Screens Are a State Machine Problem

A practical React loading screen guide using hooks, request states, error states, CSS animation, and why spinners alone are not enough.

react

Frontend Linear Data Structures Deep Dive: Arrays, Stacks, Queues, and Linked Lists

The Big Picture Before diving into stacks, queues, and linked lists, it helps to know...

computerscience

originally published

This post first ran on dev.to. Comments and reactions live there.

Continue on dev.to
PreviousDebug a Slow MySQL Query Before You Guess at IndexesA practical MySQL workflow for finding slow queries, reading EXPLAIN output, and deciding whether an index actually helps.
Back to all posts
NextJavaScript Memory Leaks Usually Start With One ReferenceA practical JavaScript memory guide covering stack, heap, garbage collection, closures, event listeners, timers, and leak debugging.
Back to archive
M4RKYUM4RKYUM4RKYUM4RKYUM4RKYUM4RKYUM4RKYUM4RKYU
Crafted since 2024
ZhenXiao Mark YuZhenXiao Mark Yu
get in touch

Saw something here?Tell me about it.

It's a portfolio, not a service · but I read every note — drop a line if anything here resonated, or just to say hi.

Start a conversation
open channel

say hi anytime · 2026

--:--:--ESTOntario, Canada
  • Email
  • GitHub
  • dev.to
  • LinkedIn
  • Twitter / X
  • Instagram
  • Facebook
  • YouTube
  • CodePen
  • Spotify
  • Snapchat

Newsletter

Get the occasional dispatch

Notes and logs from m4rkyu.com — short, dated, no noise. Unsubscribe anytime.

Work

Production builds, games, and visual archives.

  • Projects
  • Games
  • Archive
  • Logs

Resources

Daily-use tools and a personal link library.

  • Search
  • Latest
  • Tools
  • Links
  • Notes
  • Topics
  • Shop
RSSJSON feed

Studio

Background, contact, and channels for collaboration.

  • About
  • Contact
  • Changelog
  • Colophon
  • Resumepending

Socials

Find me on the usual feeds.

  • GitHub
  • dev.to
  • LinkedIn
  • Twitter / X
  • Instagram
  • Facebook
  • YouTube
  • CodePen
  • Spotify
  • Snapchat
  • Email
© 2026 ZhenXiao Mark Yumarkyu0615@gmail.com
  • Email
  • GitHub
  • dev.to
  • LinkedIn
  • Twitter / X
  • Instagram
  • Facebook
  • YouTube
  • CodePen
  • Spotify
  • Snapchat
PrivacyTermsBuilt with Next.js 16 · React 19 · Tailwind 4