CSS Day Night Switch (CSS/HTML only) [PART 1]
Tags: tutorial, css, webdev, ui
Published: 2021-01-17 17:15:22

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:

{% youtube qN3SFh0trWk %}

# Result

{% codepen https://codepen.io/crayon-code/pen/PoGVYVq %} 


## 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.

```pug
input(type="checkbox",checked).day-night-switch#day-night
label(for="day-night").day-night-switch
```
```scss
input.day-night-switch {
  display: none;
}
```

## 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`.

```scss
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;
}
```


## Let the sun come out!

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

```pug
label(for="day-night").day-night-switch
  ...
  div.celestial.sun
```


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.

```scss
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;
    }
  }
}
```

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.

```scss
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);
        }
    }
  }
}
```

## A beautiful day in the mountains

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

- markup: mountains
```pug
label(for="day-night").day-night-switch
  ...
  div.mountains
    div
    div
```

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.

```scss
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;
        }
      }
    }
  }
}
```

## A vibing cloud

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

```pug
label(for="day-night").day-night-switch
  ...
  div.decorations
    div.decoration
    div.decoration
    div.decoration
    div.decoration
```

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 😄.

```scss
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);
          }
        }
      }
    }
  }
}
```

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.

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

  100% {
    transform: scale(1.2);
  }
}
```
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.

```scss
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;
          }
        }
      }
    }
  }
}
```



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**.