DEV Community

Cover image for A CSS-only reaction component using emoji
Temani Afif
Temani Afif

Posted on Edited on

A CSS-only reaction component using emoji

Like star rating, reaction components are something widely used in many websites so here I am with my own implementation: A CSS-only reaction component that works with emojis or any kind of custom icons.

No need to bother yourself dealing with SVG or PNG images. Grab your favorite emoji and build your own component.

See it in play:

In the above, you will find no @keyframes. All the animations are done using only CSS transition


How does it work?

The above rely on the very basic HTML code:

<input type="checkbox" id="r1">
<label for="r1" class="react">
<i data-icon="💯"></i>
</label>
🏆 Live Soccer TV: afif/a css only reaction component using emoji i - Info Live Streaming 2026 🏆 Live Soccer TV: afif/a css only reaction component using emoji i - Info Live Streaming 2026

Nothing complex. An <input> with its corresponding <label> and a <i> element having our Emoji as data attribute. That's it!

All the magic is done using CSS and we have different layers illustrated like below:

<input type="checkbox" id="r1">
<label for="r1" class="react">
  ::before [1]
  <i data-icon="💯">
    ::before [2]
    ::after [3]
  </i>
  ::after [4]
</label>
🏆 Live Soccer TV: afif/a css only reaction component using emoji i - Info Live Streaming 2026 🏆 Live Soccer TV: afif/a css only reaction component using emoji i - Info Live Streaming 2026

reactions layers

All the layers are above each other thanks to the use of display:inline-grid and grid-area:1/1

All layers

[1] is a basic circle created like below:

.react:before {
  content:"";
  grid-area:1/1;
  border-radius:50%;
  border:solid orange calc(var(--r)/2);
}
🏆 Live Soccer TV: afif/a css only reaction component using emoji i - Info Live Streaming 2026 🏆 Live Soccer TV: afif/a css only reaction component using emoji i - Info Live Streaming 2026

Its transition is a scale one (from 0 to 1) then a border-width (from calc(var(--r)/2) to 0)

transition:
    transform 0.5s,
    border-width 0.5s 0.5s;
🏆 Live Soccer TV: afif/a css only reaction component using emoji i - Info Live Streaming 2026 🏆 Live Soccer TV: afif/a css only reaction component using emoji i - Info Live Streaming 2026

--r is the variable that will control all the sizing of our component. Yes, it's a scalable one! You only need to adjust one variable to control the overall size.

[2] is our emoji. Nothing complex here:

.react i:before {
  content:attr(data-icon);
  grid-area:1/1;
  font-style:normal;
  font-size:calc(0.6*var(--r));
}
🏆 Live Soccer TV: afif/a css only reaction component using emoji i - Info Live Streaming 2026 🏆 Live Soccer TV: afif/a css only reaction component using emoji i - Info Live Streaming 2026

The fun start with [3] and [4]. Those small circles are built using multiple radial-gradient. The difference between [3] and [4] is the circle size and a small rotation to avoid the overlap:

.react:after,
.react i:after{
  content:"";
  grid-area:1/1;
  margin: calc(var(--r)/-2);
  --c1:radial-gradient(red    50%,#0000 60%);
  --c2:radial-gradient(orange 50%,#0000 60%);
  background:
    var(--c1),var(--c1),var(--c1),var(--c1),
    var(--c2),var(--c2),var(--c2),var(--c2); 
  background-size:calc(var(--r)/6) calc(var(--r)/6); 
  background-position:
    calc(50% - var(--r)/2) calc(50% - var(--r)/2),
    calc(50% + var(--r)/2) calc(50% - var(--r)/2),
    calc(50% - var(--r)/2) calc(50% + var(--r)/2),
    calc(50% + var(--r)/2) calc(50% + var(--r)/2),
    calc(50% +  0px) calc(50% + var(--r)*0.707),
    calc(50% + var(--r)*0.707) calc(50% +  0px),
    calc(50% - var(--r)*0.707) calc(50% +  0px),
    calc(50% +  0px) calc(50% - var(--r)*0.707);
  background-repeat:no-repeat;
  transform:scale(0);
}
.react i:after {
  background-size:calc(var(--r)/8) calc(var(--r)/8);
  transform:rotate(60deg) scale(0);
}
🏆 Live Soccer TV: afif/a css only reaction component using emoji i - Info Live Streaming 2026 🏆 Live Soccer TV: afif/a css only reaction component using emoji i - Info Live Streaming 2026

Each one is made with 8 circles (4 red & 4 orange) placed in a circular shape. The Math-folks will notice the value of sin() and cos() for the angles N*45deg.

Their transition is a scale one (from 0 to 1) then a background-size one (from the value defined above to 0) combined with opacity

transition:
    transform 0.5s 0.5s,
    opacity   0.4s 0.9s,
    background-size 0.5s 0.9s;
🏆 Live Soccer TV: afif/a css only reaction component using emoji i - Info Live Streaming 2026 🏆 Live Soccer TV: afif/a css only reaction component using emoji i - Info Live Streaming 2026

Finally, we have the transition applied to the <i> element that will affect both [2] and [3]. We have the filter one that allows us to have the grey coloration that transitions to the original color of the emoji and a scale one.

For the scale effect, I am using a magic cubic-bezier function to create a transition from 1 to 0 to 1.

  transition:
     filter .5s .5s,
     transform 1s cubic-bezier(0,26.67,.5,26.67);
🏆 Live Soccer TV: afif/a css only reaction component using emoji i - Info Live Streaming 2026 🏆 Live Soccer TV: afif/a css only reaction component using emoji i - Info Live Streaming 2026

I am detailing this technique in the below post:

I am also adding a shake effect on hover where I am using another cubic-bezier detailed in the same post.

transition:transform 0.25s cubic-bezier(0.5,400,0.5,-400);
🏆 Live Soccer TV: afif/a css only reaction component using emoji i - Info Live Streaming 2026 🏆 Live Soccer TV: afif/a css only reaction component using emoji i - Info Live Streaming 2026

That's it!

All you have to do is to pick your emoji or an icon from Font Awesome and adjust the --r to control the size.

I made some trivial changes to the initial code to be able to use Font Awesome icons as we don't need to define content since the CSS of Font Awesome will do it for us.

PS: The idea was inspired by a Stack Overflow question I answered a while ago: How to make a sparkling button animation?. It's the same effect with a few adjustments to my old code.

Top comments (22)

🏆 Live Soccer TV: afif/a css only reaction component using emoji i - Info Live Streaming 2026
 
grahamthedev profile image
GrahamTheDev • • Edited

Wait wait wait, did you...did you use the accessibility / inclusion emoji....and make it not accessible.

I mean, are you now trying to hurt me? I thought we were friends?

Well despite your betrayal I will still give your article a ❤ and a 🦄 as I am a nice guy 😉

🏆 Live Soccer TV: afif/a css only reaction component using emoji i - Info Live Streaming 2026
 
afif profile image
Temani Afif •

Oh, That was the accessibility icon ?? didn't know (🤥🤥🤥)

🏆 Live Soccer TV: afif/a css only reaction component using emoji i - Info Live Streaming 2026
 
grahamthedev profile image
GrahamTheDev •

Yeah it is the "universal access", "access for all" or similar, I think it stems from Apple, although they could have stolen the icon knowing them 😉. I just know you will see it a lot in accessibility related stuff (as you can imagine I see it all the time!).

The best part about this posts is that after the explanation in your last post it actually makes sense!

Previously I would have had to play with the values for 10 minutes until I got it!

So thanks for the previous post...I will recover from this post...I suppose you owe me a heart attack after my SVG antics causing you so much pain 😋🤣

🏆 Live Soccer TV: afif/a css only reaction component using emoji i - Info Live Streaming 2026 Thread
 
afif profile image
Temani Afif •

well, I knew it was the accessibility icon 😝 .. that "Pinocchio" emoji wasn't clear in my last comment 🤔

By the way, I am almost accessible, I used a label 😋. Missing some aria-things and it should be done

🏆 Live Soccer TV: afif/a css only reaction component using emoji i - Info Live Streaming 2026
 
madsstoumann profile image
Mads Stoumann •

Nice animations! 👍🏻 I think my only grudge is the inconsistency of emojis on various OS’es, they will not look the same. And a small thing: add -webkit-tap-highlight-color: transparent; (I forgot that too on mine!)

🏆 Live Soccer TV: afif/a css only reaction component using emoji i - Info Live Streaming 2026
 
afif profile image
Temani Afif •

True, that's why I also made the custom icon version in case someone want to have better control over the icon.

🏆 Live Soccer TV: afif/a css only reaction component using emoji i - Info Live Streaming 2026
 
thebugcoder profile image
TheBugCoder •

Uh... There's a bug... if you click multiple times it's growing really big... :P

🏆 Live Soccer TV: afif/a css only reaction component using emoji i - Info Live Streaming 2026
 
grahamthedev profile image
GrahamTheDev •

Its not a bug it is a feature! Seeing the icons at 2000% size and upside down is an Easter egg Temani added just for you!

🏆 Live Soccer TV: afif/a css only reaction component using emoji i - Info Live Streaming 2026
 
link2twenty profile image
Andrew Bone •

And what a feature it is!

totally a feature (sarcasm)

🏆 Live Soccer TV: afif/a css only reaction component using emoji i - Info Live Streaming 2026 Thread
 
afif profile image
Temani Afif •

it's not really a bug. It's the logical result of using my magic cubic-bezier(). There is a math explanation behind it 😉 so yes it's a hidden feature you just discovered!

🏆 Live Soccer TV: afif/a css only reaction component using emoji i - Info Live Streaming 2026 Thread
 
thebugcoder profile image
TheBugCoder •

Ok! :P

🏆 Live Soccer TV: afif/a css only reaction component using emoji i - Info Live Streaming 2026
 
afif profile image
Temani Afif •

Mr XX: But why there is no reaction count in your post?
Me: I will not use all my weapons at once. A war is made with different battles and we need a good strategie to win!
Mr XX: Oh! I see I see ..

🏆 Live Soccer TV: afif/a css only reaction component using emoji i - Info Live Streaming 2026
 
siddharthshyniben profile image
Siddharth •

typo: strategie

(I keep finding typos)

🏆 Live Soccer TV: afif/a css only reaction component using emoji i - Info Live Streaming 2026
 
link2twenty profile image
Andrew Bone •

Very cool, I like animating gradient backgrounds. It can look really impressive.

🏆 Live Soccer TV: afif/a css only reaction component using emoji i - Info Live Streaming 2026
 
siddharthshyniben profile image
Siddharth •

Cool effect! Reminds me of DEV reactions

🏆 Live Soccer TV: afif/a css only reaction component using emoji i - Info Live Streaming 2026
 
afif profile image
Temani Afif •

come on! mine are better 😜

🏆 Live Soccer TV: afif/a css only reaction component using emoji i - Info Live Streaming 2026
 
posandu profile image
Posandu •

Amazing

🏆 Live Soccer TV: afif/a css only reaction component using emoji i - Info Live Streaming 2026
 
evelinchamp profile image
EvelinCHamp •

Is this a one-time animation upon click like in Facebook or something consistent like in Slack or Discord? It looks really good. Maybe I’ll try to expand on it if you will allow me.

🏆 Live Soccer TV: afif/a css only reaction component using emoji i - Info Live Streaming 2026
 
afif profile image
Temani Afif •

feel free to do and show me what you get ;)

🏆 Live Soccer TV: afif/a css only reaction component using emoji i - Info Live Streaming 2026
 
mafee6 profile image
Mafee7 •

👍Good One!

🏆 Live Soccer TV: afif/a css only reaction component using emoji i - Info Live Streaming 2026
 
mafee6 profile image
Mafee7 •

I found a bug: I you spam the reaction button, the emoji becomes really big and also goes out of viewport

🏆 Live Soccer TV: afif/a css only reaction component using emoji i - Info Live Streaming 2026
 
afif profile image
Temani Afif •

yes I am aware of it ;) it's a side effect of the cubic-bezier() I am using