Gary Byrne
Posted on February 3, 2021
Back in 2017, I entered a frontend interview and I was asked to write some code on a whiteboard. I was given the Fizzbuzz problem. Without thinking I immediately went for JavaScript. Recently I learned about CSS counters and that got me thinking. What if we can do it with CSS?
In this article, I am going to show you two easy ways to solve the Fizzbuzz problem with CSS.
What is the Fizzbuzz question?
Write a program that prints the numbers from 1 to 100.
For multiples of3
printFizz
instead of the number,
for the multiples of5
printBuzz
and for
multiples of both5 and 3
printFizzBuzz
Solution 1: Using CSS Counters
We can loop in Pug
Loop for 1 to 100 and create an HTML element. In this example, we are creating a div. We will use each div to hold the counter.
- const range = 100;
- for(let i = 1; i <= range; i++)
.fizz
We are using a for loop here. We could also write an each
iteration such as each _ in Array(100)
instead.
Using CSS counters
To get started, we must first create a counter-reset
. This will initialize our CSS counter, giving it a name and setting its value. By default, the value is 0.
body {
counter-reset: fizz;
// counter initialized with value of 0, same as saying "counter-reset: fizz 0;"
}
Once a counter has been created, we can use counter-increment
to increment its value.
For every fizz
div, let's update the counter.
.fizz {
counter-increment: fizz;
}
Next, we will set the content for our divs. We can utilize CSS before
and after
pseudo-elements to set the content of the div.
If the div is a multiple of 3, set its ::before
content to 'Fizz '. If the div is a multiple of 5, set is ::after
content to 'Buzz'. If a div is a multiple of both 3 and 5, the before and after pseudo-elements will combine to form Fizz Buzz
.
.fizz:nth-child(3n)::before {
content: 'fizz ';
}
.fizz:nth-child(5n)::after {
content: 'buzz';
}
Now we want to set the counter value for those other divs that are not a multiple of 3 or 5. To set the ::before
content of the div to use the counter value, we use counter
.
.fizz::before {
content: counter(fizz);
}
.fizz:nth-child(3n)::before {
content: 'fizz ';
}
.fizz:nth-child(5n)::after {
content: 'buzz';
}
This gives us the following:
Note how the divs with a multiple of 5 and not a multiple of 3 still get the number applied. To fix this we can use the not pseudo-class. When the div is not
a multiple of 5, then set the before content to 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
In some cases, an interviewer might ask to change your code to work for a different range instead of 1 to 100. We can write a Pug mixin to take a min and max value.
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.
Solution 2: Using an Ordered List
Let's copy the same pug from the last example.
mixin fizzbuzz(min, max)
ol
- for(let i = min; i <= max; i++)
li
+fizzbuzz(1, 100)
So we have an ordered list here which returns a list of numbered elements. So if we render 100 list items in that ordered list, we have the numbers already available to view. Although it makes no sense semantically, it is just to show how we might do this.
In this ordered list, we can set the list-style-position of each list item to inside
. This will set the position of everything inside, relative to the list which created the nice alignment. We then just set the list style to none to remove the numbering on those items that need the text.
//scss
li {
list-style-position: inside;
&:nth-child(3n), &:nth-child(5n){
list-style-type: none;
}
&:nth-child(3n)::before {
content: 'Fizz';
}
&:nth-child(5n)::after {
content: ' Buzz';
}
}
Here is a working example:
Posted on February 3, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.