Вертикальный виджет с анимацией

by Denis Tverdokhlebov

HTML

<div class="bulgy-radios" role="radiogroup" aria-labelledby="bulgy-radios-label">
  <h2 id="bulgy-radios-label">Select an option</h2>
  <label>
    <input type="radio" name="options" checked />
    <span class="radio"></span>
    <span class="label">First option</span>
  </label>
  <label>
    <input type="radio" name="options" />
    <span class="radio"></span>
    <span class="label">Second option</span>
  </label>
  <label>
    <input type="radio" name="options" />
    <span class="radio"></span>
    <span class="label">Third option</span>
  </label>
  <label>
    <input type="radio" name="options" />
    <span class="radio"></span>
    <span class="label">Fourth option</span>
  </label>
  <label>
    <input type="radio" name="options" />
    <span class="radio"></span>
    <span class="label">Last option</span>
  </label>
</div>

CSS

html {
  box-sizing: border-box;
  height: 100%;
  font-size: 10px;
}

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

body {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  font-family: 'Lato', sans-serif;
  background: #2f2f2f;
}

h2 {
  font-size: 3rem;
  font-weight: bold;
  margin: 0 0 3rem;
}

.bulgy-radios {
  width: 38rem;
  background: #fff;
  padding: 3rem 0 3rem 5rem;
  border-radius: 1rem;
}

.bulgy-radios label {
  display: block;
  position: relative;
  height: 1em;
  padding-left: 4rem;
  margin-bottom: 1.75rem;
  cursor: pointer;
  font-size: 2.5rem;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
  color: #555;
  letter-spacing: 1px;
}

.bulgy-radios label:hover input:not(:checked)~.radio {
  opacity: 0.8;
}

.bulgy-radios .label {
  display: flex;
  align-items: center;
  padding-right: 3rem;
}

.bulgy-radios .label span {
  line-height: 1em;
}

.bulgy-radios input {
  position: absolute;
  cursor: pointer;
  height: 0;
  width: 0;
  left: -2000px;
}

.bulgy-radios input:checked~.radio {
  background-color: #0ac07d;
  transition: background .3s;
}

.bulgy-radios input:checked~.radio::after {
  opacity: 1;
}

.bulgy-radios input:checked~.label {
  color: #0bae72;
}

.bulgy-radios input:checked~.label span {
  -webkit-animation: bulge .5s forwards;
  animation: bulge .5s forwards;
}

.bulgy-radios input:checked~.label span:nth-child(1) {
  -webkit-animation-delay: 0.025s;
  animation-delay: 0.025s;
}

.bulgy-radios input:checked~.label span:nth-child(2) {
  -webkit-animation-delay: 0.05s;
  animation-delay: 0.05s;
}

.bulgy-radios input:checked~.label span:nth-child(3) {
  -webkit-animation-delay: 0.075s;
  animation-delay: 0.075s;
}

.bulgy-radios input:checked~.label span:nth-child(4) {
  -webkit-animation-delay: 0.1s;
  animation-delay: 0.1s;
}

.bulgy-radios input:checked~.label span:nth-child(5) {
  -webkit-animation-delay: 0.125s;
 ...

JavaScript

//This is sort of CSS-only; the JS below just sticks a span around each letter, so i can animate each independantly.
//(oh for an :nth-letter selector!)
const labels = document.querySelectorAll('.label');
labels.forEach(label => {
  const chars = label.textContent.split('');
  label.innerHTML = '';
  chars.forEach(char => {
    label.innerHTML += `<span>${char === ' ' ? '&nbsp' : char}</span>`;
  });
})