Traffic Light 🚦


Prompt
Create a traffic light using only HTML, CSS, and Vanilla JavaScript, featuring three distinct colors: red 🔴, yellow 🟡, and green 🟢.
Requirements
- A button that randomly turns on one of the lights.
- A button that continuously cycles through lights in sequence (red → yellow → green → red) with a 3 second interval.
- A button that stops the light from turning on.
Let's see your traffic light in action! 🚦
Example
Playground
Important: When adding functions to index.js
, you must attach them to the window
object (e.g., window.functionName = function() {...}
). This is because the StackBlitz playground uses JavaScript modules, which don't automatically expose functions globally. Your HTML onclick
handlers need these functions to be globally accessible. This is only applicable to the Vanilla JavaScript questions.
For styling the traffic light, consider using border-radius: 50%
to create circular lights. You can represent the "off" state of a light by using a darker shade of the color (e.g. #4a4a4a
) or a gray color. When a light is "on", change its background color to the appropriate bright color (red (e.g. #e5484d
), yellow (e.g. #ffe629
), or green (e.g. #30a46c
)).
To randomly select and turn on one of the lights, you'll need to use Math.random()
in combination with Math.floor()
to generate a random index (0, 1, or 2) that corresponds to one of the three lights. Before turning on a new light, make sure to turn off any previously activated light. This can be done by removing a CSS class from all lights and then adding it only to the selected light.
For the continuous light change feature, you'll need to use setInterval()
to repeatedly call your random light function. Store the interval ID in a variable so you can later use it with clearInterval()
when the stop button is clicked. Make sure to check if an interval is already running before starting a new one to prevent multiple intervals from running simultaneously.
Solution
Explanation
HTML Structure
The HTML creates the visual structure of our traffic light and control buttons:
<div class="container">
<div class="traffic-light">
<div class="light red" id="red"></div>
<div class="light yellow" id="yellow"></div>
<div class="light green" id="green"></div>
</div>
<div class="controls">
<button onclick="randomLight()" class="control">Random</button>
<button onclick="startCycling()" class="control">Cycle Lights</button>
<button onclick="stopTrafficLight()" class="control">Stop</button>
</div>
</div>
Each light is a div
with two classes: a general light
class (for shared styling) and a specific color class (red
, yellow
, or green
). We also give each light an id matching its color so we can easily target it with JavaScript.
The buttons have onclick
attributes that call specific JavaScript functions when clicked. This is how we connect user interactions to our code.
CSS Styling
The CSS handles the visual appearance of our traffic light:
.traffic-light {
background-color: var(--frame-color);
padding: 16px;
border-radius: 12px;
display: flex;
flex-direction: column;
gap: 16px;
}
.light {
width: 80px;
height: 80px;
border-radius: 50%;
background-color: var(--light-off);
position: relative;
transition: background-color 0.3s ease;
will-change: background-color;
}
Notice how we use border-radius: 50%
to create perfectly circular lights. By default, all lights have the "off" color defined by the --light-off CSS variable.
The magic happens with these CSS classes:
.light.active.red {
background-color: var(--light-red);
}
.light.active.yellow {
background-color: var(--light-yellow);
}
.light.active.green {
background-color: var(--light-green);
}
When a light has both its color class (e.g., red
) and the active
class, it changes from the "off" color to its bright color. This is how we turn lights on and off without changing the HTML structure.
We also added a smooth transition with transition: background-color 0.3s
ease, which creates a gentle fade effect when lights change state.
JavaScript Functionality
Now let's look at how the JavaScript brings our traffic light to life:
// Global variables
let intervalId = null;
const LIGHTS = ['red', 'yellow', 'green'];
let currentLightIndex = 0;
We start by defining some important variables:
intervalId
will store the ID of our interval when cycling lightsLIGHTS
is an array of our light colors, which makes our code more maintainablecurrentLightIndex
keeps track of which light is currently active in the sequence
Random Light Function
window.randomLight = function () {
// Turn off all lights
clearAllLights();
// Turn on a random light
const randomIndex = Math.floor(Math.random() * LIGHTS.length);
const selectedLight = document.getElementById(LIGHTS[randomIndex]);
selectedLight.classList.add('active');
};
This function does two things:
- Calls
clearAllLights()
to turn off any currentlyactive
lights - Selects a random light and turns it on by adding the
active
class - To select a random light, we use
Math.random()
which gives us a decimal between 0 and 1. We multiply by the number of lights (3) and useMath.floor()
to round down to a whole number (0, 1, or 2). This gives us a random index into ourLIGHTS
array.
Cycling Lights
window.startCycling = function () {
// Don't start a new interval if one is already running
if (intervalId !== null) {
stopTrafficLight();
}
// Reset to start with red light
currentLightIndex = 0;
// Start cycling lights in sequence
cycleLights();
intervalId = setInterval(cycleLights, 3000);
};
window.cycleLights = function () {
// Turn off all lights
clearAllLights();
// Turn on the current light in sequence
const selectedLight = document.getElementById(LIGHTS[currentLightIndex]);
selectedLight.classList.add('active');
// Move to next light (cycle back to red after green)
currentLightIndex = (currentLightIndex + 1) % LIGHTS.length;
};
The startCycling
function:
- Checks if an interval is already running and stops it if needed
- Resets the light sequence to start with red
- Immediately calls
cycleLights()
to show the first light - Sets up an interval to call
cycleLights()
every 3 seconds
The cycleLights
function:
- Turns off all lights
- Activates the current light in the sequence
- Updates
currentLightIndex
to point to the next light
The clever part is how we update the index: currentLightIndex = (currentLightIndex + 1) % LIGHTS.length
. This uses the modulo operator (%) to cycle back to 0 after reaching the end of the array.
So the sequence goes 🔴 → 🟡 → 🟢 → 🔴 → 🟡 → 🟢, and so on.
Stopping the Traffic Light
window.stopTrafficLight = function () {
// Clear the interval
if (intervalId !== null) {
clearInterval(intervalId);
intervalId = null;
}
// Turn off all lights
clearAllLights();
};
This function:
- Checks if an interval is running
- If so, cancels it with
clearInterval
and resetsintervalId
- Turns off all lights
Helper Function
function clearAllLights() {
LIGHTS.forEach((color) => {
document.getElementById(color).classList.remove('active');
});
}
This helper function loops through all lights and removes the active
class, effectively turning them all off. We use this before activating a new light to ensure only one light is on at a time.
Putting It All Together
When a user clicks a button, the corresponding JavaScript function is called:
- Random Button: Turns off all lights and activates a random one
- Cycle Lights Button: Starts a sequence that changes lights every 3 seconds in the traffic light order (red → yellow → green → red)
- Stop Button: Cancels any running interval and turns off all lights
The JavaScript manipulates the DOM by adding or removing the active
class, and the CSS responds by changing the background color of the lights. This separation of concerns (HTML for structure, CSS for appearance, JavaScript for behavior) creates clean, maintainable code.
The use of setInterval
and clearInterval
allows us to create a repeating action (changing lights) and stop it when needed. By storing the interval ID, we ensure we don't create multiple intervals that would cause unpredictable behavior.
This traffic light is a perfect example of how these three technologies work together to create an interactive web application!
Common Pitfalls
- Forgetting to Clear Previous Lights: When turning on a new light in the sequence, make sure you're first turning off any previously active light. Failing to do this will result in multiple lights being on simultaneously.
- Multiple Intervals Running: When implementing the continuous light cycling feature, a common error is starting multiple intervals without clearing previous ones. This can lead to unpredictable behavior where lights change at inconsistent times. Always store your interval ID and check if an interval is already running before starting a new one.
- Incorrect Cycling Order: Make sure to maintain the correct traffic light sequence (red → yellow → green → red) by properly incrementing and resetting the index counter.
Interviewer Criteria
HTML/CSS
Did I create a visually appealing traffic light using CSS with clear styling for active and inactive states?
Have I made good use of CSS variables for colors and maintained consistent naming conventions?
Did I implement smooth transitions when lights change state using CSS transitions?
JavaScript
Did I implement a clean function to randomly select and activate one of the lights?
Have I properly handled the continuous light change using setInterval and clearInterval?
Did I ensure only one interval can run at a time to prevent multiple timers from running simultaneously?
Have I written a function to clear all active lights before activating a new one?
Is my JavaScript code well-organized with meaningful function and variable names?
Did I properly connect DOM elements to JavaScript functionality?
Code Structure and Performance
Have I organized my code with clear separation of concerns between structure (HTML), presentation (CSS), and behavior (JavaScript)?
Is my code DRY (Don't Repeat Yourself) with reusable functions and minimal duplication?
Have I handled potential edge cases, such as rapidly clicking buttons or refreshing the page?
Time Checkpoints
- 10:00 AM
Interview starts 👥
- 10:03 AM
Prompt given by the interviewer
- 10:05 AM
Candidate reads the prompt, asks clarifying questions, and starts coding
- 10:10 AM
HTML structure of traffic light and buttons created
- 10:20 AM
CSS styling of traffic light completed
- 10:25 AM
Button styling completed
- 10:30 AM
Function to randomly activate a light implemented
- 10:35 AM
Logic to clear active lights before activating a new one implemented
- 10:40 AM
Cycle lights functionality implemented
- 10:45 AM
Stop functionality using clearInterval implemented
- 10:50 AM
Testing and debugging the application
- 11:00 AM
Interview ends ✅