AI CSS Animations

Sunset Toggle Switch

A tiny landscape: the sun arcs over the hills and sets while the moon rises on the other side and the sky fades to night. Both ride one rotating arm pinned below the horizon.

Also known as: day to night toggle, sunrise sunset toggle css, landscape theme switch.

HTML

<label class="tgl-sunset-toggle">
  <input type="checkbox" role="switch" aria-label="Night mode">
  <span class="tgl-sunset-toggle-scene" aria-hidden="true">
    <span class="tgl-sunset-toggle-night"></span>
    <span class="tgl-sunset-toggle-arm">
      <span class="tgl-sunset-toggle-sun"></span>
      <span class="tgl-sunset-toggle-moon"></span>
    </span>
    <span class="tgl-sunset-toggle-hills"></span>
  </span>
</label>

CSS

.tgl-sunset-toggle {
  --c: var(--tgl-color, #3b82f6);
  font-size: var(--tgl-size, 24px);
  position: relative;
  display: inline-flex;
  align-items: center;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.tgl-sunset-toggle input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
  z-index: 1;
}
.tgl-sunset-toggle-scene {
  position: relative;
  display: block;
  width: 7.2em;
  height: 3.2em;
  border-radius: 1.6em;
  overflow: hidden;
  isolation: isolate;
  background: linear-gradient(to bottom, color-mix(in srgb, var(--c) 60%, #bae6fd), #fef3c7);
  box-shadow: inset 0 0.1em 0.25em rgba(0, 0, 0, 0.25);
}
.tgl-sunset-toggle-night {
  position: absolute;
  inset: 0;
  background: linear-gradient(to bottom, #0b1026, color-mix(in srgb, var(--c) 35%, #1e1b4b));
  opacity: 0;
  transition: opacity 0.9s ease 0.1s;
}
.tgl-sunset-toggle-night::before {
  content: "";
  position: absolute;
  top: 0.5em;
  left: 2.6em;
  width: 0.12em;
  height: 0.12em;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0.9em 0.4em 0 #fff, 1.8em -0.1em 0 0.02em #fff, 2.7em 0.6em 0 #fff, 3.5em 0.15em 0 0.02em #fff, 1.2em 1.1em 0 #fff, -1.2em 0.8em 0 #fff;
}
/* the arm pivots under the middle of the horizon */
.tgl-sunset-toggle-arm {
  position: absolute;
  left: 50%;
  top: 4.2em;
  width: 0;
  height: 0;
  transform: rotate(-30deg);
  transition: transform 1.1s cubic-bezier(0.55, 0, 0.25, 1);
}
.tgl-sunset-toggle-sun,
.tgl-sunset-toggle-moon {
  position: absolute;
  border-radius: 50%;
}
.tgl-sunset-toggle-sun {
  left: -0.55em;
  top: -3.55em;
  width: 1.1em;
  height: 1.1em;
  background: radial-gradient(circle at 40% 40%, #fff7cc, #fcd34d 55%, #f59e0b);
  box-shadow: 0 0 0.6em 0.15em rgba(252, 211, 77, 0.6);
}
.tgl-sunset-toggle-moon {
  left: -0.5em;
  top: 2.5em;
  width: 1em;
  height: 1em;
  background:
    radial-gradient(circle at 35% 40%, #cbd5e1 0 0.12em, transparent 0.14em),
    radial-gradient(circle at 62% 65%, #cbd5e1 0 0.16em, transparent 0.18em),
    #f1f5f9;
  box-shadow: 0 0 0.5em 0.05em rgba(241, 245, 249, 0.45);
}
.tgl-sunset-toggle-hills::before,
.tgl-sunset-toggle-hills::after {
  content: "";
  position: absolute;
  height: 3em;
  border-radius: 50%;
  transition: background-color 0.9s ease;
}
.tgl-sunset-toggle-hills::before {
  left: -1.2em;
  top: 2.2em;
  width: 5.4em;
  background-color: #86d99b;
}
.tgl-sunset-toggle-hills::after {
  right: -1.5em;
  top: 2.45em;
  width: 6.2em;
  background-color: #3fae63;
}
.tgl-sunset-toggle input:checked + .tgl-sunset-toggle-scene .tgl-sunset-toggle-night { opacity: 1; }
.tgl-sunset-toggle input:checked + .tgl-sunset-toggle-scene .tgl-sunset-toggle-arm { transform: rotate(150deg); }
.tgl-sunset-toggle input:checked + .tgl-sunset-toggle-scene .tgl-sunset-toggle-hills::before { background-color: #23364f; }
.tgl-sunset-toggle input:checked + .tgl-sunset-toggle-scene .tgl-sunset-toggle-hills::after { background-color: #15253a; }
.tgl-sunset-toggle input:focus-visible + .tgl-sunset-toggle-scene {
  outline: 2px solid var(--c);
  outline-offset: 0.2em;
}

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

Customizing this snippet

  • Color: set --tgl-color on the element or a parent. Currently previewing #3b82f6.
  • Size: set --tgl-size. Every other dimension is in em, so the whole thing scales together. Currently previewing 24px.
  • 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 toggle switches