Utilities✨ Premium

Like / Heart Button

Heart-shaped Like button: on click it turns red, beats, throws five little hearts around it and adds 1 to the counter; a second click removes the like.

JSClickParticles

Updated

This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Utilities has 43 effects, including 3 free. Explore the category →

3 usage examples

Liking a photo in a photography app feed — Like / Heart Button example 1

① Liking a photo in a photography app feed

WhenScrolling through their feed, the follower stops on a sunrise over a lake and taps the heart under the picture: it turns red, the little hearts burst out, 128 becomes 129.
WhyThis is where a like is expected, right under the photo, next to the comments; the burst gives the gesture visible feedback without a popup or message.
Settings--lkh-size: 40px, --lkh-gap: 8px, --lkh-count-size: .95rem, starting count 128, demo frame set to zero. In the capture, the scene clicks the real button every 1.3 s (like, then removal).
Small hearts under each comment of an article — Like / Heart Button example 2

② Small hearts under each comment of an article

WhenUnder an article about new bus routes, each comment has its small heart and its total on the right; the reader approves the third one, then the first.
WhyFour independent buttons in a list: each keeps its total and state, and the burst stays readable even at 30 px.
SettingsFour roots (totals 14, 9, 3, 0); --lkh-size: 30px, --lkh-btn: #f1f2f5, --lkh-off: #6b6f80, --lkh-btn-on: #fde8e8, --lkh-on: #dc2626, --lkh-count-size: .8rem, --lkh-count-color: #4b5060 for the white background. The scene clicks comments 3 and 1 in turn every 0.9 s.
Audience vote for a finalist cake — Like / Heart Button example 3

③ Audience vote for a finalist cake

WhenDuring the final of a pastry contest, the voting screen shows candidate no. 3's layer cake and a very large heart: each viewer taps it from their phone to vote.
WhyAt 96 px, the heart is the only action on screen; the large total and the burst reward the vote. A second tap removes that viewer's vote, like a like.
Settings--lkh-size: 96px, --lkh-gap: 18px, --lkh-btn: #2e1a24, --lkh-off: #c4a6b2, --lkh-count-size: 2.6rem, starting total 348. Click on the real button every 1.1 s in the capture.

How it works

The button is a real toggle button: aria-pressed goes to true on click, Enter or Space. The heart turns red (#ef4444), the button background turns pink and the heart beats (scaled to 130 %, then 90 %, then 120 %, in 0.6 s). At the same moment, five little hearts fly upwards, each in its own direction, shrink and fade in 0.6 s.

The burst is started by the lkh-burst class, which the script removes and adds back on every like: the burst replays each time. Removing the like does not fire a burst.

The counter starts from the number written in .lkh-num (42 in the sample HTML): +1 on like, −1 on removal, never below zero. The starting state is read from aria-pressed, so a like already given can be shown. Every toggle fires the fx0742:change event (detail.liked, detail.count) from the root: that is where you tell your server. Every size follows --lkh-size.

Accessibility

  • Name and state: the button is called "Like" (visually hidden text) and announces "toggle button, pressed / not pressed". The drawn hearts are hidden from screen readers.
  • Total announced: the counter is an aria-live="polite" area; after a click, the screen reader says "43 likes".
  • Keyboard: real <button>, Enter and Space, lavender ring on keyboard focus.
  • Contrast: the off heart is #8a8aa3, 4.2:1 on the button; the red heart is about 4:1 on the pink background. The state does not rely on colour alone: it is also given by aria-pressed.
  • Reduced motion: no beat and no burst; the heart turns red and the counter changes, without animation.

Browser compatibility

CSS and 1.5 KB of dependency-free JavaScript. Measured in Chromium 151, Firefox 153 and WebKit 26.5 (Playwright): code pasted twice (each button counts on its own), burst invisible on load then visible 120 ms after a click (opacity 0.66 to 0.74), no burst on removal, keyboard, touch at 390 px, reduced motion, 0 errors, no name added to window.

Chrome 86+✓ Full (measured)
Firefox 85+✓ Full (measured)
Safari 15.4+✓ Full (measured in WebKit)
Edge 86+✓ Full (Chromium engine)
Mobile iOS 15.4+✓ WebKit engine, not checked on a device
Android Chrome 86+✓ Full (touch measured)

Without JavaScript, the button stays grey and the counter does not move. The burst uses CSS variables inside @keyframes (Chrome 49, Firefox 31, Safari 9.1); the focus ring needs :focus-visible (Chrome 86, Firefox 85, Safari 15.4).

The code

HTML structure to paste into your page (the CSS + JS come with your purchase):

index.html — structure
<div class="fx-0742-root">
  <div class="lkh-demo">
    <button type="button" class="lkh-btn" aria-pressed="false">
      <span class="lkh-heart" aria-hidden="true">❤</span>
      <span class="lkh-particles" aria-hidden="true">
        <span class="lkh-particle">❤</span> (× 5)
      </span>
      <span class="lkh-vh">Like</span>
    </button>
    <span class="lkh-count" aria-live="polite" aria-atomic="true"><span class="lkh-num">42</span><span class="lkh-vh"> likes</span></span>
  </div>
</div>

One-time payment, no subscription — the code is yours forever

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
Starting total (HTML — text of .lkh-num) 42 Write the real total from your database; only digits are read.
Already liked (HTML — aria-pressed="true" on the button) false Shows a like already given by this visitor.
Spoken name (HTML — text of .lkh-vh) "Like" · "likes" Adapt it: "Vote for this cake", "votes".
Size (CSS — --lkh-size, --lkh-gap) 60 px · 20 px Heart, burst and distances follow the button size.
Colours (CSS — --lkh-btn, --lkh-btn-on, --lkh-off, --lkh-on) #2a2a3e · red at 10 % · #8a8aa3 · #ef4444 Button background at rest and liked, heart off and on (the burst takes the on colour).
Counter (CSS — --lkh-count-size, --lkh-count-color) 1.2rem · white Darken it on a light background.
Frame (CSS — --lkh-height, --lkh-page, --lkh-panel, --lkh-pad) 300 px · #0a0a0f · #1a1a2e · 20 px 0 and transparent to place it under a picture or in a list.
Server (JS — fx0742:change event) — root.addEventListener('fx0742:change', e => fetch('/api/like', { method: 'POST', body: JSON.stringify(e.detail) })).

FAQ

No, the button only counts on the page. Listen to fx0742:change to send the like to your server, and write the up-to-date total in .lkh-num when you build the page.
The heart is the ❤ character followed by the U+FE0E selector, which asks for the "text" drawing: it then takes the CSS colour. A few fonts or older systems ignore it and show their emoji, which keeps its own colour. For the same look everywhere, put a heart SVG icon with fill: currentColor in place of the character.
The script only reads digits (1,204 becomes 1204) and writes the number back without a separator. For a formatted total, replace String(count) with count.toLocaleString('en-US') in the script.