AI CSS Animations

Wallpaper Picker Image Radios

Radio buttons as images: three wallpaper thumbnails painted with layered CSS gradients. An outline ring springs out around the pick, and the chosen wallpaper slowly drifts as if it were live.

Also known as: radio button images, radio buttons with images css, image radio button picker, wallpaper picker css.

HTML

<div class="rad-wallpaper-image-radio" role="radiogroup" aria-label="Wallpaper">
  <label><input type="radio" name="rad-wallpaper-image-radio" value="dune"><span class="rad-wallpaper-image-radio-pic rad-wallpaper-image-radio-dune" aria-hidden="true"></span><span class="rad-wallpaper-image-radio-name">Dune</span></label>
  <label><input type="radio" name="rad-wallpaper-image-radio" value="aurora" checked><span class="rad-wallpaper-image-radio-pic rad-wallpaper-image-radio-aurora" aria-hidden="true"></span><span class="rad-wallpaper-image-radio-name">Aurora</span></label>
  <label><input type="radio" name="rad-wallpaper-image-radio" value="tide"><span class="rad-wallpaper-image-radio-pic rad-wallpaper-image-radio-tide" aria-hidden="true"></span><span class="rad-wallpaper-image-radio-name">Tide</span></label>
</div>

CSS

.rad-wallpaper-image-radio {
  --c: var(--rad-color, #3b82f6);
  font-size: var(--rad-size, 18px);
  display: inline-flex;
  gap: 1em;
  font-family: system-ui, sans-serif;
  line-height: 1.2;
}
.rad-wallpaper-image-radio label {
  position: relative;
  display: grid;
  justify-items: center;
  gap: 0.65em;
  cursor: pointer;
}
.rad-wallpaper-image-radio input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
  z-index: 1;
}
.rad-wallpaper-image-radio-pic {
  position: relative;
  display: block;
  width: 5.2em;
  height: 3.4em;
  overflow: hidden;
  border-radius: 0.6em;
  box-shadow: 0 0.3em 0.7em -0.4em rgb(0 0 0 / 0.35);
  outline: 0.13em solid transparent;
  outline-offset: 0;
  transition: outline-color 0.2s ease, outline-offset 0.45s cubic-bezier(0.34, 1.56, 0.64, 1), transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
}
/* the picture itself, on a layer that can drift inside the rounded frame */
.rad-wallpaper-image-radio-pic::before {
  content: "";
  position: absolute;
  inset: 0;
  filter: saturate(0.75) brightness(0.97);
  transition: filter 0.3s ease, transform 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}
.rad-wallpaper-image-radio-pic::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: inset 0 0 0 1px rgb(255 255 255 / 0.18);
}
.rad-wallpaper-image-radio-dune::before {
  background:
    radial-gradient(130% 34% at 40% 106%, #9a3412 0 97%, transparent 100%),
    radial-gradient(80% 48% at 88% 100%, #c2410c 0 97%, transparent 100%),
    radial-gradient(95% 58% at 12% 100%, #ea580c 0 97%, transparent 100%),
    radial-gradient(circle at 72% 32%, #fffbeb 0 0.42em, transparent 0.5em),
    linear-gradient(#fef3c7, #fdba74);
}
.rad-wallpaper-image-radio-aurora::before {
  background:
    radial-gradient(55% 75% at 22% 35%, #22d3ee, transparent 70%),
    radial-gradient(55% 70% at 78% 30%, #a78bfa, transparent 70%),
    radial-gradient(80% 55% at 50% 105%, #34d399, transparent 70%),
    linear-gradient(#0b1026, #111c3f);
}
.rad-wallpaper-image-radio-tide::before {
  background:
    radial-gradient(140% 30% at 50% 108%, #1e3a8a 0 97%, transparent 100%),
    radial-gradient(75% 46% at 85% 100%, #2563eb 0 97%, transparent 100%),
    radial-gradient(90% 56% at 15% 100%, #38bdf8 0 97%, transparent 100%),
    radial-gradient(circle at 30% 30%, #ffffff 0 0.3em, transparent 0.38em),
    linear-gradient(#f0f9ff, #bae6fd);
}
.rad-wallpaper-image-radio-name {
  font-size: 0.78em;
  font-weight: 600;
  color: color-mix(in srgb, currentColor 55%, transparent);
  transition: color 0.25s ease;
}
.rad-wallpaper-image-radio label:hover .rad-wallpaper-image-radio-pic {
  transform: translateY(-0.1em);
}
.rad-wallpaper-image-radio label:hover .rad-wallpaper-image-radio-pic::before {
  filter: none;
}
.rad-wallpaper-image-radio input:checked + .rad-wallpaper-image-radio-pic {
  outline-color: var(--c);
  outline-offset: 0.2em;
}
.rad-wallpaper-image-radio input:checked + .rad-wallpaper-image-radio-pic::before {
  filter: none;
  animation: rad-wallpaper-image-radio-drift 6s ease-in-out infinite alternate;
}
.rad-wallpaper-image-radio input:checked ~ .rad-wallpaper-image-radio-name {
  color: currentColor;
}
.rad-wallpaper-image-radio input:focus-visible ~ .rad-wallpaper-image-radio-name {
  outline: 0.12em solid var(--c);
  outline-offset: 0.25em;
  border-radius: 0.2em;
}
@keyframes rad-wallpaper-image-radio-drift {
  from { transform: scale(1.04); }
  to { transform: scale(1.16) translate(-4%, -3%); }
}
@media (prefers-reduced-motion: reduce) {
  .rad-wallpaper-image-radio input:checked + .rad-wallpaper-image-radio-pic::before {
    animation: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .rad-wallpaper-image-radio, .rad-wallpaper-image-radio *, .rad-wallpaper-image-radio::before, .rad-wallpaper-image-radio::after, .rad-wallpaper-image-radio *::before, .rad-wallpaper-image-radio *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

Customizing this snippet

  • Color: set --rad-color on the element or a parent. Currently previewing #3b82f6.
  • Size: set --rad-size. Every other dimension is in em, so the whole thing scales together. Currently previewing 18px.
  • Speed: change the duration on the transition or animation shorthand. Staggered delays are relative, so they keep their rhythm.
  • Reduced motion: the rule at the end of the CSS turns the motion off for people who asked their OS for less of it. Keep it.

Similar radio buttons