Emoji Reaction Bar — HTML CSS JS Snippet

Emoji Reaction Bar · Buttons · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Hover + tap reveal
The popover shows on hover for pointers and via an .open class toggled by tap for touch, so it works on every device.
Spring-up popover
The picker animates from scale(.9) with a bottom-right origin, appearing to grow out of the React button.
Scale-and-lift emojis
Each reaction grows to 1.45× and lifts on hover, the tactile bounce that makes the picker feel responsive and fun.
CSS-only tooltips
content: attr(data-label) on a pseudo-element labels each emoji on hover — no tooltip library or extra markup.
Single-reaction toggle
pickReaction enforces one reaction at a time and removes it when re-clicked, matching real social platforms.
Live count and trigger
updateReact rewrites the trigger to your emoji and label and adjusts the total by one in a single pass.
Overlapping count cluster
Top reactions stack as overlapping circles via negative margins; your new reaction is unshifted and ringed with a spring animation.
Outside-click dismissal
A document listener closes the popover on any outside click, with stopPropagation keeping the trigger from self-closing.

About this UI Snippet

Emoji Reaction Bar — Hover-Reveal Popover, Tooltip Reactions & Overlapping Count Cluster

Screenshot of the Emoji Reaction Bar snippet rendered live

A single "Like" button is binary; a reaction bar lets people respond with nuance — love, laughter, surprise, sadness, anger. Popularised by Facebook, the hover-reveal reaction picker is now a standard pattern in social feeds, comment threads, and chat. This snippet implements it in plain HTML, CSS, and vanilla JavaScript: a trigger that reveals a popover of emoji reactions on hover or tap, scale-and-tooltip animations on each emoji, single-reaction toggle logic, and an overlapping count cluster that reflects your choice.

Hover-reveal popover that also works on touch

The reactions live in an .er-pop popover positioned above the trigger. On devices with a pointer it appears on hover via .er-react:hover .er-pop, springing up from scale(.9) with its transform origin at the bottom-right. Hover does not exist on touch screens, so the trigger also has an onclick calling togglePop, which toggles an .open class that reveals the same popover — and a document click listener closes it when tapping elsewhere. stopPropagation on the trigger keeps that outside-click handler from immediately re-closing it.

Tactile emoji interactions

Each reaction is a button that scales to 1.45× and lifts on hover (transform: scale(1.45) translateY(-4px)), the playful "bounce" that makes the picker feel alive. A CSS-only tooltip — content: attr(data-label) on the ::after — shows the reaction name ("Love", "Haha") above the emoji on hover, so the meaning is never ambiguous. No tooltip library, no extra markup.

Single-reaction toggle

Real reaction systems let you have exactly one reaction at a time, and clicking your current reaction removes it. pickReaction implements this with one line: myReaction = (myReaction === emoji) ? null : emoji. updateReact then rewrites the trigger to show your chosen emoji and its label (or reverts to the neutral "React"), adjusts the total count by one, and rebuilds the summary cluster.

Overlapping count cluster

The summary shows the familiar overlapping circle cluster of top reactions with a total count. updateReact builds it from a base set of reactions; when you react with something not already shown, your emoji is unshifted to the front and highlighted with a ring and a spring er-bub animation, so you can immediately see your contribution. The overlap is pure CSS — negative margin-left with white borders to fake the stacked-coins look.

Everything is driven by a single myReaction variable and the updateReact function, so wiring it to a backend is a matter of POSTing the reaction and seeding real counts. Pair this with a favorite button for simple likes, a comment thread for discussions, or a social post card feed.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Rather than tracing the toggle logic by hand, paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly how the one-line ternary in pickReaction turns clicking the same emoji twice into a way to remove your reaction, and how updateReact() decides whether to unshift your reaction to the front of the cluster or leave the base list untouched. The same assistant can help you optimize it, for instance checking whether relying on both a CSS hover rule and a JS-toggled open class for the popover could ever get out of sync on devices that support both touch and a mouse. It's also useful for extending the bar: ask it to wire pickReaction to a real backend so reactions persist and reflect other users' counts, add keyboard accessibility so the popover opens on focus and closes on Escape, or support showing a tooltip listing which specific people reacted with each emoji. Treat the code less like a finished artifact and more like a starting point for a conversation.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a Facebook-style emoji reaction bar in plain HTML, CSS, and JavaScript with no library.

Requirements:
- A "React" trigger button that reveals a row of six emoji reaction buttons in a popover positioned above it, showing the popover both on CSS hover (for pointer devices) and via a JavaScript-toggled open class from a click (for touch devices), so both interaction methods work without conflicting.
- Each emoji button in the popover must visually scale up and lift slightly on hover, and show a small tooltip label naming the reaction (e.g. "Love", "Haha") built purely from a CSS pseudo-element reading a data attribute, with no separate tooltip markup or library.
- Track the current user's reaction as a single value (not a set), and clicking an emoji must toggle it: clicking a different emoji than the current one switches to it, and clicking the same emoji the user already picked removes the reaction entirely, all with one compact conditional rather than separate add/remove code paths.
- Whenever the reaction changes, rewrite the trigger button's label and icon to reflect the current reaction (or revert to a neutral default icon and label when no reaction is set), adjust a total reaction count by exactly one in the corresponding direction, and close the popover.
- Render a small cluster of overlapping circular avatars representing the top reactions using negative margins and white borders (not flexbox gap) to create the stacked-coin visual effect, and when the current user's reaction is not already among the displayed top reactions, insert it at the front of that cluster with a distinct highlighted ring and a brief spring-in scale animation.
- Close the popover automatically on any click outside the whole reaction control, using a single document-level click listener with a containment check, while making sure a click on the trigger button itself does not immediately re-close the popover it just opened.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSA social post card appears with a reaction summary (24 reactions) and a "🙂 React" button in the footer.
  2. 2
    Reveal the reactionsHover the React button (or tap it on touch) — a rounded popover of six emoji springs up above it.
  3. 3
    Hover an emojiEach emoji scales up and lifts, and a tooltip shows its name ("Love", "Haha", "Wow") above it.
  4. 4
    Pick a reactionClick one — the trigger changes to that emoji and label, the count ticks to 25, and your reaction joins the cluster with a highlighted ring.
  5. 5
    Change or remove itPick a different emoji to switch, or click your current reaction again to remove it — the count and cluster update accordingly.
  6. 6
    Dismiss the popoverClick anywhere outside to close the picker without changing your reaction.

Real-world uses

Common Use Cases

Social feeds and posts
Let users react to posts with nuance beyond a like. Drop it into a social post card or activity feed.
Comment threads
Add reactions to individual comments; pair with a comment thread so each reply can be reacted to.
Chat and messaging
Tap-and-hold style emoji reactions on chat bubbles — the popover and toggle logic map directly to message reactions.
Docs and knowledge bases
"Was this helpful?" with emoji reactions instead of a plain thumbs pair; a richer alternative to a favorite button.
Polls and feedback widgets
Quick sentiment capture on announcements or changelog entries; combine with a poll widget for structured votes.
Live events and streams
Floating emoji reactions during a broadcast; reuse the picker to choose which reaction to send.

Got questions?

Frequently Asked Questions

In pickReaction, after updating myReaction, POST the change to your API (the post id and the new reaction, or null to remove). Update optimistically as the snippet does, and reconcile with the server's authoritative counts on response. Seed BASE_COUNT and the cluster from real data on load so the UI reflects everyone's reactions, not just yours.

Replace the static BASE array and BASE_COUNT with data from your API: sort reaction types by count, take the top three for the cluster, and use the true total. Rebuild the cluster in updateReact from that data, still unshifting and highlighting the current user's reaction so it stands out.

Hover is fast and discoverable on desktop, but touch devices have no hover state — relying on it alone makes the picker unusable on phones. The snippet shows the popover on hover via CSS and also toggles it with a tap via togglePop, so both input types get a natural way to open it.

Make the emoji buttons keyboard-reachable (they are real <button>s) and give each an aria-label matching its data-label, since an emoji alone is not descriptive. Open the popover on focus as well as hover, support Escape to close, and announce reaction/count changes via an aria-live region so screen-reader users get feedback.

In React, hold myReaction and open in useState, derive the trigger label and cluster from myReaction, and add a click-outside effect. In Vue, use refs with @click/@mouseenter and a computed cluster. In Angular, track state on the component and bind [class.open]/[class.reacted]. The popover and emoji-hover CSS port unchanged.