We're live-coding on YouTube! Join us!

Build a Lightsaber with CSS and a Checkbox (Solution to Code Challenge #3)

Build a Lightsaber with CSS and a Checkbox (Solution to Code Challenge #3)

Last week, we started Code Challenge #3. The goal was to build these CSS Lightsabers:

Let's take a look at the solution and build these with pure CSS. After we're all done, we'll add in some JavaScript to add the lightsaber sounds for when we click the lightsaber.

Video!

For those that like videos, we're starting to put our more video content. Check out our YouTube channel

The HTML

We'll start with the HTML for just one of the lightsabers. This will be fairly simple and will use some BEM Classes.

<div class="lightsaber">

  <label>Yoda</label>
  <input type="checkbox" checked>

</div>

That will be the base for our lightsaber. We also want to make sure that we can check/uncheck the checkbox when we click the label. To do this, HTML let's us add a for attribute to the label and an id to the checkbox:

<div class="lightsaber">

  <label for="yoda">Yoda</label>
  <input id="yoda" type="checkbox" checked>

</div>

Now when we click on the label, this will check and uncheck the checkbox.

Essential Reading: Learn React from Scratch! (2019 Edition)

The Technique

The technique we'll use here is a simple one. This is the technique used when you see all the styled out radio and checkboxes across the web. We're going to style the label as our lightsaber and we'll hide the checkbox.

Hide the checkbox. Use the label to toggle the checkbox.

Since we are able to use the label to toggle our checkbox, we don't actually need the checkbox itself. Checkboxes are harder to style out anyway since they come with their own set of base styles per browser.

Adding a Container for the Saber

We'll style the label as the hilt of our lightsaber and we'll add another div to house the actual plasma/colored part.

<div class="lightsaber">

  <label for="yoda">Yoda</label>
  <input id="yoda" type="checkbox" checked>
  <div class="plasma"></div>

</div>

We'll also add a class to this lightsaber so that we know its the Yoda version:

<div class="lightsaber lightsaber--yoda">

  <label for="yoda">Yoda</label>
  <input id="yoda" type="checkbox" checked>
  <div class="plasma"></div>

</div>

IMAGE HERE

The BEM style of classes let us know with the double dashes that this is a modifier to the main .lightsaber class. Our lightsaber looks nothing like a lightsaber just yet. The next bit of magic will happen in the CSS.

The CSS

Now that our HTML is ready, let's move on to the CSS part. We'll set some base styles for the hilt/label first:

/* make sure that all things are positioned relative to the parent lightsaber */
.lightsaber   {
  position: relative;
}

.lightsaber label {
  position: absolute;
  width: 15px;
  height: 50px;
  background: #DDD;
}

This next part is optional but adds that extra bit of depth to our lightsaber hilt. We'll use a CSS gradient for our hilt:

.lightsaber label {
  position: absolute;
  width: 15px;
  height: 50px;
  background: #DDD;
  /* gradient goes here */

}

Hiding the Checkbox

We don't need to the checkbox to be visually available. We'll hide it using the opacity:

.lightsaber input[type="checkbox"] {
  opacity: 0;
}

Styling the Plasma

We have our .plasma div that we need to style next. This is the piece that we will show and hide when the checkbox is toggled. This will be the green glowing part for our Yoda saber. Since we already have our HTML checkbox set to the checked state, we'll style it as if it was on already.

.lightsaber .plasma   {
  transition: height 0.3s;
  position: absolute;
  width: 10px;
  height: 0;
  filter: blur(1px); /* neat css trick to get that glow */
}

Notice that we have added a transition to the height. This is what will get transitioned from 0 height to full height. That's how we get the lightsaber opening and closing effect.

The Yoda Styling

We want this saber to be for Yoda and we have a class specifically for that purpose. We'll use that to style the green saber.

.lightsaber--yoda .plasma {
    background: rgb(135, 220, 90);
    background: linear-gradient(
    to right, 
    rgb(135, 220, 90) 0%, 
    rgb(254, 254, 254) 30%, 
    rgb(254, 254, 254) 50%, 
    rgb(254, 254, 254) 70%, 
    rgb(135, 220, 90) 100%
  );
}

.lightsaber--vader .plasma {
    background: rgb(229, 17, 21);
    background: linear-gradient(
    to right, 
    rgba(229, 17, 21, 1) 0%, 
    rgba(254, 254, 254, 1) 30%, 
    rgba(254, 254, 254, 1) 47%, 
    rgba(254, 254, 254, 1) 71%, 
    rgba(229, 17, 21, 1) 100%
  );
}

.lightsaber--windu .plasma {
    background: rgb(202, 116, 221);
    background: linear-gradient(
    to right, 
    rgba(202,116,221,1) 0%,
    rgba(254,254,254,1) 30%,
    rgba(254,254,254,1) 47%,
    rgba(254,254,254,1) 71%,
    rgba(202,116,221,1) 100%
  );
}

I've also added the styles for both vader and windu in there.

Toggling the Checkbox On or Off

Based on the state of the checkbox, we'll set the height of the plasma. This is how we can set height from 0px to 50px of the .plasma:

.lightsaber input[type="checkbox"]:checked ~ .plasma {
  height: 55px;
}

We will check if the checkbox is checked using the checked psuedo class and then set the sibling .plasma to full height!

Bonus: Adding Sound on Click

As a bonus, we can bring in some JavaScript to play a sound whenever this saber is opened/closed. The gist of what we will do is:

  • create an <audio> element
  • set the src of that to our lightsaber sound file: FILE HERE
  • play the audio file
// grab the checkbox
const lightsaber = document.querySelector('.lightsaber input[type="checkbox"]');
lightsaber.addEventListener('change', playWhoosh);

function playWhoosh(e) {
  // first check to see if this checkbox is on or off
  const isOn = e.target.checked;
  if (isOn) {
    const whooshSound = document.createElement('audio');
    whooshSound.src = 'https://scotch.io/audio/lightsaber.wav';
    whooshSound.play();
  }
}

Now we have this cool whoosh sound!

Conclusion

That ends the Code Challenge for this week. Hope you found that helpful and got some CSS techniques out of it. Until next week!

Like this article? Follow @chrisoncode on Twitter