5

I want a button which pulses multiple times (three or so), like in this Dribble video demo. The button pulses should have an interval between pulses of about 300-400ms, and also if the twisting effect can be achieved somehow that would be great.

I have this code so far, but the pulsing animation is not particularly close to the one described and linked above.

body {
  background: black;
}

.button {
  padding: 0.75em 1.5em;
  border: 3px solid white;
  border-radius: 15px;
}

.button:hover {
  animation: pulse 4s infinite;
  transition: 1s ease all;
}

@keyframes pulse {
  0% {
    box-shadow: 0 0 0 0 rgba(255, 255, 255, 0.7),
      0 0 0 0 rgba(255, 255, 255, 0.5), 0 0 0 0 rgba(255, 255, 255, 0.3);
  }

  50% {
    box-shadow: 0 0 0 10px rgba(255, 255, 255, 0),
      0 0 0 20px rgba(255, 255, 255, 0), 0 0 0 30px rgba(255, 255, 255, 0);
  }

  100% {
    box-shadow: 0 0 0 30px rgba(255, 255, 255, 0),
      0 0 0 40px rgba(255, 255, 255, 0), 0 0 0 50px rgba(255, 255, 255, 0);
  }
}
<button class="button">Button</button>

How can I make my code more closely match the description and video example above?

7
  • 1
    looking at some of the other buttons/effects by the same designer it seems unlikely he was simply using CSS Commented Mar 10 at 10:15
  • If you want 3 pulses, you need to add an element because you can only do before and after Commented Mar 10 at 10:16
  • 2
    I'm not sure why people are voting to close this question. The user made a good attempt at solving the problem themself, and then posted a question containing a minimal reproducible example when they got stuck. This is a good question. Commented Mar 10 at 12:18
  • @VasilNikolov what do you mean by "the twisting effect"? What twisting effect? I don't see one on the Dribble you linked to. Commented Mar 10 at 15:53
  • Related, possible duplicate: stackoverflow.com/questions/44783546/… Commented Mar 10 at 15:59

2 Answers 2

3

Here is an idea using outline and pseudo-elements:

.button {
  padding: 0.75em 1.5em;
  border-radius: 1em;
  font-size: 30px;
  position: relative;
}
.button:before,
.button:after {
  content:"";
  position: absolute;
  inset: 0;
  outline: inherit;
  border-radius: inherit;
}
.button:hover,
.button:hover:before,
.button:hover:after {
  outline: 2px solid #fff0;
  animation: pulse 2s infinite linear;
}
.button:hover:before {
  animation-delay: .4s;
}
.button:hover:after {
  animation-delay: .8s;
}

@keyframes pulse {
  0%,10%   {outline-offset:0px; outline-color:#fff0}
  50%      {outline-offset:20px;outline-color:#fff9}
  90%,100% {outline-offset:40px;outline-color:#fff0}
}

body {
  margin: 0;
  min-height: 100vh;
  display: grid;
  place-items: center;
  background: #000;
}
<button class="button">Button</button>

Sign up to request clarification or add additional context in comments.

1 Comment

Seems like more of a speedy ripple rather than a pulse, at least compared to the Dribble video OP linked to.
1

I updated trying to match what was in the Dribble, but it's not quite right. I can tell that you'll need to use the outlines of either extra elements and/or pseudo-elements.

Try this:

.button:hover {
  animation: 
    pulse 
    400ms 
    cubic-bezier(1.000, 0.000, 0.000, 1.000) 
    both 
    alternate 
    infinite;
}

The timing function, cubic-bezier(1.000, 0.000, 0.000, 1.000), (aka ease-in-out-expo), has an arrhythmic stutter. The direction, alternate allows the transition to swing from one end of a cycle and continue from the opposite end in the beginning of the next cycle. The fill mode of both allows the animation to extend in both directions.

*,
*::before,
*::after {
  box-sizing: border-box;
}

:root {
  font: 2ch/1.5 'RailroadRoman', sans-serif;
}

body {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  background: #000;
  overflow-x: hidden;
}

main {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-evenly;
  align-items: center;
  align-content: center;
  gap: 2rem;
  width: 100%;
  min-height: 100vh;
  margin: 0 auto;
  background: #000;
}

button {
  position: relative;
  width: min-content;
  width: 5rem;
  height: 2rem;
  padding-inline: 0.5rem;
  border: 2px solid #fff;
  border-radius: 8px;
  outline-width: 2px;
  outline-style: solid;
  outline-color: #fff;
  outline-offset: 0;
  font: inherit;
  cursor: pointer;
}

button i {
  position: absolute;
  top: -0.5em;
  right: -0.5em;
  z-index: 500;
  display: block;
  font-style: normal;
  font-size: 1.5em;
  cursor: pointer;
}


.A.yang:hover {
  animation: pulse 600ms cubic-bezier(1.000, 0.000, 0.000, 1.000) both alternate infinite;
}

.A.ying:hover {
  animation: pulse 2s cubic-bezier(0.445, 0.050, 0.550, 0.950) infinite;
}

@keyframes pulse {
  0% {
    box-shadow:
      0 0 0 0 rgba(255, 255, 255, 0.7),
      0 0 0 0 rgba(255, 255, 255, 0.5),
      0 0 0 0 rgba(255, 255, 255, 0.3);
  }

  50% {
    box-shadow:
      0 0 0 10px rgba(255, 255, 255, 0),
      0 0 0 20px rgba(255, 255, 255, 0),
      0 0 0 30px rgba(255, 255, 255, 0);
  }

  100% {
    box-shadow:
      0 0 0 30px rgba(255, 255, 255, 0),
      0 0 0 40px rgba(255, 255, 255, 0),
      0 0 0 50px rgba(255, 255, 255, 0);
  }
}

.B::before,
.B::after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  display: block;
  width: 5rem;
  height: 2rem;
  border-radius: 8px;
  outline-width: 2px;
  outline-style: solid;
  outline-color: #fff;
  outline-offset: 0;
  background: transparent;
}

.B.ying:hover::before {
  z-index: 80;
  animation: eb 2s ease-in backwards infinite;
}

.B.ying:hover {
  z-index: 80;
  animation: ripple 2s 2s ease-out both alternate infinite;
}

.B.ying:hover::after {
  z-index: 120;
  animation: wave 2s ease-out forwards infinite;
}

.B.yang:hover::before {
  z-index: 110;
  animation: wave 2s 1s ease-out backwards infinite;
}

.B.yang:hover {
  z-index: 140;
  animation: ripple 2s 2s ease-in-out both alternate infinite;
}

.B.yang:hover::after {
  z-index: 130;
  animation: eb 2s ease-out backwards infinite;
}

@keyframes ripple {
  0% {
    outline-width: 2px;
    outline-color: transparent;
    outline-offset: 0;
  }

  50% {
    outline-width: 2px;
    outline-color: #fff;
    outline-offset: 30px;
  }

  70% {
    outline-width: 2px;
    outline-color: rgba(255, 55, 0 0.5);
    outline-offset: 50px;
  }

  100% {
    outline-width: 1px;
    outline-color: transparent;
    outline-offset: 0;
  }
}

@keyframes wave {
  0% {
    outline-width: 1px;
    outline-color: #fff;
    outline-offset: 40px;
  }

  20% {
    outline-width: 2px;
    outline-color: rgba(255, 255, 10, 0.6);
    outline-offset: 30px;
  }

  70% {
    outline-width: 1px;
    outline-color: rgba(255, 200, 5 0.3);
    outline-offset: 10px;
  }

  100% {
    outline-width: 1px;
    outline-color: transparent;
    outline-offset: 0;
  }
}

@keyframes eb {
  0% {
    outline-width: 2px;
    outline-color: transparent;
    outline-offset: 0;
  }

  60% {
    outline-width: 5px;
    outline-color: rgba(55, 25, 255, 0.4);
    outline-offset: 20px;
  }

  80% {
    outline-width: 5px;
    outline-color: #fff;
    outline-offset: 40px;
  }

  100% {
    outline-width: 2px;
    outline-color: rgba(25, 155, 215 0.3);
    outline-offset: 50px;
  }
}
<link href="https://fonts.cdnfonts.com/css/railroadroman" rel="stylesheet">

<main>
  <button class="A yang">
  I <i>💓</i>
</button>

  <button class="A ying">
  II <i>💗</i>
</button>

  <button class="B yang">
  III <i>🖤</i>
</button>

  <button class="B ying">
  IV <i>💙</i>
</button>
</main>

2 Comments

This seems like more of a heartbeat animation (I know there's similarity in the word 'pulse' there, but still) than a steadier, 300ms-spaced out pulse. The speed of the pulse border is far quicker than what's shown in the linked Dribble.
I see, thanks @TylerH, I added some more examples. As always, I like Temani Afif's answer better than my own.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.