Solving Fizzbuzz with CSS Counters and Pug
Back in 2017, during a frontend interview, I was asked to solve a coding challenge on a whiteboard. The task was the classic FizzBuzz problem. Instinctively, I reached for JavaScript to write my solution. Recently, however, I discovered CSS counters, which sparked an intriguing question in my mind: Can we solve FizzBuzz using only CSS?
In this article, we will solve the FizzBuzz problem using CSS Counters. In part 2 we will explore solving it with an HTML ordered list.
What is the Fizzbuzz Problem?
Write a program that outputs the numbers from 1 to 100. For numbers that are multiples of 3, display “Fizz” instead of the number. For multiples of 5, display “Buzz.” For numbers that are multiples of both 3 and 5, display “FizzBuzz.”
Using CSS Counters
Loop from 1 to 100 and create an HTML element for each number. In this example, we’ll create a div for each element to hold the counter.
const range = 100;
for(let i = 1; i <= range; i++)
.fizz
Here, we’re using a for loop to iterate through the numbers. Alternatively, we could use a different iteration method.
Array(100).forEach((_, i) => { ... }).
To begin, we need to initiate a counter-reset to establish our CSS counter, assigning it a name and initializing its value. By default, the initial value is 0.
body {
counter-reset: fizz;
/* counter initialized with a value of 0 */
}
Once the counter is set up, we can use counter-increment to increment its value whenever an element with the class .fizz appears.
.fizz {
counter-increment: fizz;
}
Next, we configure the content for our divs using CSS ::before and ::after pseudo-elements.
- If a div’s counter value is a multiple of 3, its ::before content will be set to 'Fizz '.
- If a div’s counter value is a multiple of 5, its ::after content will be set to ‘Buzz’.
- If a div’s counter value is a multiple of both 3 and 5, the ::before and ::after pseudo-elements will combine to display ‘Fizz Buzz’.
.fizz:nth-child(3n)::before {
content: 'fizz ';
}
.fizz:nth-child(5n)::after {
content: 'buzz';
}
Now, for the remaining divs that are not multiples of 3 or 5, we set the ::before content of the div to display the current counter value using the counter function.
.fizz::before {
content: counter(fizz);
}
.fizz:nth-child(3n)::before {
content: 'fizz ';
}
.fizz:nth-child(5n)::after {
content: 'buzz';
}
This gives us the following:
Notice how even the divs that are multiples of 5 but not multiples of 3 still display the counter number. To address this, we can utilize the :not pseudo-class. When a div is not a multiple of 5, we set its ::before content to display the counter number.
.fizz:not(:nth-child(5n))::before {
content: counter(fizz);
}
.fizz:nth-child(3n)::before {
content: 'fizz ';
}
.fizz:nth-child(5n)::after {
content: 'buzz';
}
Here is a working example in codepen:
Refactoring our Pug
Sometimes, an interviewer may request modifications to your code to accommodate a different range instead of the default 1 to 100. To handle this, we can create a Pug mixin that accepts minimum and maximum values as parameters.
mixin fizzbuzz(min, max)
- for(let i = min; i <= max; i++)
.fizz
+fizzbuzz(1, 100)
Here is a working codepen example:
Browser Support
At the time of writing, CSS Counters are supported in most major browsers. See Can I Use for more information.
Check out part 2 of this article where I solve Fizzbuzz using ordered lists.