Robert Birming

Bear Halloween upvote button

As you may know, I made a Bear Halloween theme last year. It still holds up, which is almost a shame, since I love making things like that.

So, upvote button to the rescue! Styled for the spookiest season of the year: a jack-o'-lantern that lights up when you upvote a post. And as it turns out, something was hiding inside...

Go ahead, give it a try below, if you dare. 🦇

How it works

Before you vote, the pumpkin sits there unlit, with dark, hollow eyes. Hover over it, and it starts to flicker, as if someone's about to light it. Upvote, and the face starts glowing, and the light startles a bat that was sleeping inside.1 It pops out, hovers for a second, and flaps off into the night.

Visitors who prefer reduced motion get the lit pumpkin, without the flicker or the bat.

Add it to your blog

It's pure CSS and works with any Bear theme. Paste it at the end of your styles.

/* Halloween upvote: a jack-o'-lantern that lights up, and a startled bat
   Robert Birming | robertbirming.com */
#upvote-form .upvote-button {
  --pumpkin-unlit: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 7.6c0-2 .7-3.6 2.4-4.4' fill='none' stroke='%236b7f2a' stroke-width='1.6' stroke-linecap='round'/%3E%3Cg fill='%23c9621c' stroke='%23a24d12' stroke-width='.8'%3E%3Cellipse cx='7.6' cy='14.6' rx='4.6' ry='6.4'/%3E%3Cellipse cx='16.4' cy='14.6' rx='4.6' ry='6.4'/%3E%3Cellipse cx='12' cy='14.6' rx='4.4' ry='6.9'/%3E%3C/g%3E%3Cg fill='%234a2208'%3E%3Cpath d='M8.4 12.8 10 10.4l1.6 2.4z'/%3E%3Cpath d='M12.4 12.8 14 10.4l1.6 2.4z'/%3E%3Cpath d='M8.2 15.8c1.5 2.3 6.1 2.3 7.6 0-1.9.9-5.7.9-7.6 0z'/%3E%3C/g%3E%3C/svg%3E");
  --pumpkin-lit: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 7.6c0-2 .7-3.6 2.4-4.4' fill='none' stroke='%237a9a2e' stroke-width='1.6' stroke-linecap='round'/%3E%3Cg fill='%23f0882a' stroke='%23c8641a' stroke-width='.8'%3E%3Cellipse cx='7.6' cy='14.6' rx='4.6' ry='6.4'/%3E%3Cellipse cx='16.4' cy='14.6' rx='4.6' ry='6.4'/%3E%3Cellipse cx='12' cy='14.6' rx='4.4' ry='6.9'/%3E%3C/g%3E%3Cg fill='%23ffd75e'%3E%3Cpath d='M8.4 12.8 10 10.4l1.6 2.4z'/%3E%3Cpath d='M12.4 12.8 14 10.4l1.6 2.4z'/%3E%3Cpath d='M8.2 15.8c1.5 2.3 6.1 2.3 7.6 0-1.9.9-5.7.9-7.6 0z'/%3E%3C/g%3E%3C/svg%3E");
  --bat: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 12'%3E%3Cpath d='M12 4c.6-1 1.4-1.4 2-1.2-.4.6-.3 1.3.2 1.6 1.6-1.6 4.4-2.6 7.8-2.2-1.6.8-2.6 2.2-2.8 4-1-.8-2.4-.8-3.4.2-.6-.8-1.6-.9-2.4-.2-.4-.6-1-.8-1.4-.8s-1 .2-1.4.8c-.8-.7-1.8-.6-2.4.2-1-1-2.4-1-3.4-.2C4.6 4.4 3.6 3 2 2.2c3.4-.4 6.2.6 7.8 2.2.5-.3.6-1 .2-1.6.6-.2 1.4.2 2 1.2z'/%3E%3C/svg%3E");
  --bat-color: #1e1828;
  position: relative;
  display: inline-flex;
  flex-direction: row;
  align-items: center;
  gap: 0.4em;
  padding: 0;
  border: 0;
  font: inherit;
  color: #c9621c;
  background: none;
  cursor: pointer;
}

@media (prefers-color-scheme: dark) {
  #upvote-form .upvote-button {
    --bat-color: #ece6f5;
  }
}

#upvote-form .upvote-button svg {
  display: none;
}

/* The pumpkin */
#upvote-form .upvote-button::before {
  content: "";
  width: 1.75em;
  aspect-ratio: 1;
  background: var(--pumpkin-unlit) center / contain no-repeat;
}

/* The bat, hidden until the pumpkin lights up */
#upvote-form .upvote-button::after {
  content: "";
  position: absolute;
  inset-block-start: 0.1em;
  inset-inline-start: -0.1em;
  width: 2em;
  aspect-ratio: 2;
  background-color: var(--bat-color);
  mask: var(--bat) center / contain no-repeat;
  opacity: 0;
  pointer-events: none;
}

#upvote-form .upvote-count {
  margin: 0;
  font-size: 0.9em;
  font-variant-numeric: tabular-nums;
  translate: 0 0.3em;
}

/* Hover: a flicker, as if someone's about to light it */
@media (hover: hover) {
  #upvote-form .upvote-button:not(:disabled):hover::before {
    animation: pumpkin-flicker 0.9s steps(2) infinite;
  }
}

/* Voted: lit pumpkin, warm glow, and the bat takes off */
#upvote-form .upvote-button:is(:disabled, .upvoted) {
  color: #e8781e !important;
  cursor: default;
}

#upvote-form .upvote-button:is(:disabled, .upvoted)::before {
  background-image: var(--pumpkin-lit);
  animation: pumpkin-glow 2.4s ease-in-out infinite;
}

#upvote-form .upvote-button:is(:disabled, .upvoted)::after {
  animation: bat-takeoff 2.6s ease-in-out 0.4s forwards;
}

@keyframes pumpkin-flicker {
  50% { filter: brightness(1.15) drop-shadow(0 0 4px rgb(255 170 40 / 0.5)); }
}

@keyframes pumpkin-glow {
  0%, 100% { filter: drop-shadow(0 0 5px rgb(255 170 40 / 0.6)); }
  50% { filter: drop-shadow(0 0 9px rgb(255 190 60 / 0.9)); }
}

@keyframes bat-takeoff {
  0%   { opacity: 0; transform: translate(0, 0.2em) scale(0.3); }
  12%  { opacity: 1; transform: translate(0, -0.6em) scale(1); }
  18%  { transform: translate(0, -0.7em) scale(1, 0.4); }
  24%  { transform: translate(0, -0.6em) scale(1); }
  30%  { transform: translate(0, -0.7em) scale(1, 0.4); }
  40%  { transform: translate(0.6em, -1.4em) scale(1); }
  50%  { transform: translate(1.3em, -2em) scale(1, 0.4); }
  60%  { transform: translate(2em, -2.6em) scale(1); }
  70%  { transform: translate(2.8em, -3.1em) scale(1, 0.4); }
  80%  { opacity: 1; transform: translate(3.6em, -3.7em) scale(1); }
  90%  { transform: translate(4.4em, -4.2em) scale(1, 0.4); }
  100% { opacity: 0; transform: translate(5.2em, -4.8em) scale(1); }
}

@media (prefers-reduced-motion: reduce) {
  #upvote-form .upvote-button::before {
    animation: none !important;
  }

  #upvote-form .upvote-button::after {
    display: none;
  }
}

Fair warning: the bat flies every time you load a post you've already upvoted. Consider it a returning guest. 😄

Happy Halloween! 👻

  1. No bats or other creatures were harmed in the making of this button.↩