DEV Community

loading...
Cover image for CSS Day Night Switch (CSS/HTML only) [PART 1]

CSS Day Night Switch (CSS/HTML only) [PART 1]

crayoncode profile image crayoncode ・5 min read

Today let's build a nice little day night switch with sunset and sunrise and a lot of CSS transitions. In this first part we're going to take care of the day phase of the switch, so we start with a checkbox in checked state.

Read the full article or watch me code this on Youtube:

Result

Basic Markup

The first thing we need is a checkbox and a label. The most important part here is that the ID of the input matches the value of the for attribute of the label, because the checkbox will not be visible and all of the styled content is going to be inside the label. So when the label is clicked, the checkbox needs to be triggered, which is why the for value needs to match the ID of the checkbox.

input(type="checkbox",checked).day-night-switch#day-night
label(for="day-night").day-night-switch
Enter fullscreen mode Exit fullscreen mode
input.day-night-switch {
  display: none;
}
Enter fullscreen mode Exit fullscreen mode

Basic Styling

Let's define a few variables which make resizing much easier. The --size CSS variable is what every other dimension is derived from, so the switch can be resized to whatever size you want it to be.

To create the shape of the switch it's going to have half of the width as the height and the border-radius is set to be the same as the height to give it the typical switchy shape.

Since we're going to use the transition duration a lot, it's wrapped up in variable called --transition-duration.

The actual size of the moving part of the switch is calculated to fit nicely into the shape of the container. Because a few calculations are done relatively to the switch size (scalability), it's put into a variable called --switch-size.

label.day-night-switch {
  --size: 400px;
  --height: calc(var(--size) / 2);
  --padding: calc(var(--size) * 0.04); //8px;
  --border-width: calc(var(--size) * 0.02); //4px;
  --transition-duration: 250ms;
  --switch-size: calc(
    var(--height) - 2 * var(--padding) - 2 * var(--border-width)
  );

  width: var(--size);
  height: var(--height);
  //background: rgba(white, 0.2);
  border-radius: var(--height);
  border: var(--border-width) solid white;
  position: relative;
  transition: all var(--transition-duration) ease-in-out;
  cursor: pointer;
}
Enter fullscreen mode Exit fullscreen mode

Let the sun come out!

The sun is just a plain div.celestial.sun:

label(for="day-night").day-night-switch
  ...
  div.celestial.sun
Enter fullscreen mode Exit fullscreen mode

Since the sun will be put on the right, let's calculate its position relative to the --switch-size and use it for later reference. Both moon and sun share the same basic styling, so the class .celestial sets up the dimensions, border radius positioning mode. To make the sun actually look like a sun, it's getting a nice yellow color with a slightly darker border.

label.day-night-switch {
  --pos-right: calc(
    var(--size) - var(--switch-size) - var(--padding) - 2 * var(--border-width)
  );

  > .celestial {
    transition: all var(--transition-duration) ease-in-out;
    width: var(--switch-size);
    height: var(--switch-size);
    border: var(--border-width) solid green;
    position: absolute;
    border-radius: var(--switch-size);
    background: white;

    &.sun {
      background-color: #fdc82e;
      border-color: #e3ad0d;
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

The day state is given through the checked state and therefore let's group all styles that are important for it together. So, the position on the top right of the switch is given and during day the sky is bright, which is why the switch also has a bright blue color.

input.day-night-switch {
  // checked styles
  &:checked {
    + label.day-night-switch {
      border-color: #3190bf;
      background-color: #6cbde5;

      > .celestial {
        &.sun {
          transition-delay: var(--transition-duration);
          left: var(--pos-right);
          top: var(--padding);
          transform: scale(1);
        }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

A beautiful day in the mountains

Let's not just have a switch, but also a beautiful scenery, so we're adding two divs for the mountains.

  • markup: mountains
label(for="day-night").day-night-switch
  ...
  div.mountains
    div
    div
Enter fullscreen mode Exit fullscreen mode

The mountains are basically just rotated by 45 degrees and positioned in a way such that only around one half of them is visible. Additionally the cap is slightly rounded off to make it look less edgy.

In order to maintain scalability all dimensions are calculated relatively to the switch size.

label.day-night-switch {
 > .mountains {
    display: inline-block;
    position: absolute;
    top: calc(var(--switch-size) * 0.85);
    left: calc(var(--switch-size) * 0.7);

    > * {
      position: absolute;
      display: inline-block;
      border-width: var(--border-width);
      border-style: solid;
      transform: rotate(45deg);
      transition: var(--transition-duration) all ease-in-out;
      border-top-left-radius: calc(var(--switch-size) * 0.1);
      background-color: white;
      border-color: black;

      &:nth-child(1) {
        width: calc(var(--switch-size) * 0.9);
        height: calc(var(--switch-size) * 0.9);
        top: calc(var(--switch-size) * 0.1);
        left: 0;
      }

      &:nth-child(2) {
        width: calc(var(--switch-size) * 0.45);
        height: calc(var(--switch-size) * 0.45);
        top: calc(var(--switch-size) * 0.2);
        left: calc(var(--switch-size) * 0.6);
      }
    }
  }
}

input.day-night-switch {
  // checked styles
  &:checked {
    + label.day-night-switch {
      > .mountains {
        > * {
          background-color: #d4d4d4;
          border-color: #a8a8a8;
        }
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

A vibing cloud

To make the left side of the switch look less empty, let's add a cloud. It consists of four divs, simply being rounded off and positioned together to create a nice little "good weather cloud".

label(for="day-night").day-night-switch
  ...
  div.decorations
    div.decoration
    div.decoration
    div.decoration
    div.decoration
Enter fullscreen mode Exit fullscreen mode

Just like the mountains they are dimensionsed and positioned absolutely relative (pun intended) to the --switch-size. If you may wonder why they also need transitions, continue reading part 2 😄.

label.day-night-switch {
  > .decorations {
    > .decoration {
      transition: all var(--transition-duration) ease-in-out;
      position: absolute;
    }
  }
}

input.day-night-switch {
  // checked styles
  &:checked {
    + label.day-night-switch {
      > .decorations {
        > .decoration {
          position: absolute;
          background-color: white;
          border-radius: 50%;
          width: calc(max(var(--border-width) * 0.75, 2px));
          height: calc(max(var(--border-width) * 0.75, 2px));

          &:nth-child(1) {
            border-radius: calc(var(--switch-size) * 0.3);
            width: calc(var(--switch-size) * 0.85);
            height: calc(var(--switch-size) * 0.3);
            top: calc(var(--switch-size) * 0.6);
            left: calc(var(--switch-size) * 0.45);
          }
          &:nth-child(2) {
            border-radius: 50%;
            width: calc(var(--switch-size) * 0.35);
            height: calc(var(--switch-size) * 0.35);
            top: calc(var(--switch-size) * 0.5);
            left: calc(var(--switch-size) * 0.35);
          }
          &:nth-child(3) {
            border-radius: 50%;
            width: calc(var(--switch-size) * 0.3);
            height: calc(var(--switch-size) * 0.3);
            top: calc(var(--switch-size) * 0.5);
            left: calc(var(--switch-size) * 0.85);
          }
          &:nth-child(4) {
            border-radius: 50%;
            width: calc(var(--switch-size) * 0.4);
            height: calc(var(--switch-size) * 0.4);
            top: calc(var(--switch-size) * 0.4);
            left: calc(var(--switch-size) * 0.55);
          }
        }
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Now, let's make the clouds extra playful, by adding a slightly pulsating/vibing animation to each part of it. Therefore the animation is setup to scale between 1.0 and 1.2.

@keyframes vibe {
  0% {
    transform: scale(1);
  }

  100% {
    transform: scale(1.2);
  }
}
Enter fullscreen mode Exit fullscreen mode

The animation is set for each individual part of the cloud and by adding some animation delays and variation in duration, it looks like proper cozy vibing cloud. Most importantly animation-direction: alternate; will make the animation first play in one direction and then again backwards, so it constanlty alternates slowly between a scale of 1.0 and 1.2.

input.day-night-switch {
  // checked styles
  &:checked {
    + label.day-night-switch {
      > .decorations {
        > .decoration {
          animation: 4s vibe ease-in-out infinite;
          animation-direction: alternate;

          &:nth-child(2) {
            animation-delay: 300ms;
            animation-duration: 2.5s;
          }
          &:nth-child(3) {
            animation-delay: 800ms;
            animation-duration: 3.5s;
          }
          &:nth-child(4) {
            animation-delay: 1400ms;
            animation-duration: 3s;
          }
        }
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

So, that's what's needed for the day phase. Now let's head right over to the night phase and read on in Part 2.

Discussion (0)

pic
Editor guide