DEV Community

Cover image for Fizzbuzz with CSS and Pug
Gary Byrne
Gary Byrne

Posted on

Fizzbuzz with CSS and Pug

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 of 3 print Fizz instead of the number,
for the multiples of 5 print Buzz and for
multiples of both 5 and 3 print FizzBuzz

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

Enter fullscreen mode Exit fullscreen mode

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;"
}

Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

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';  
}

Enter fullscreen mode Exit fullscreen mode

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';  
}

Enter fullscreen mode Exit fullscreen mode

This gives us the following:

Screenshot 2021-02-03 at 12.18.44.png

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';  
}

Enter fullscreen mode Exit fullscreen mode

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)

Enter fullscreen mode Exit fullscreen mode

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)

Enter fullscreen mode Exit fullscreen mode

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';  
    } 
}

Enter fullscreen mode Exit fullscreen mode

Here is a working example:

Top comments (6)

Collapse
 
joelpatrizio profile image
Joel Patrizio

With CSS? You're not allowed to do that! ... oh

Collapse
 
garybyrne profile image
Gary Byrne

Haha. That is so true. I would love to know if anyone has done this before.

Collapse
 
joelpatrizio profile image
Joel Patrizio

And if anyone has done this in an actual interview. I guess you can nail any frontend interview if you solve a JS problem with just CSS 😁

Thread Thread
 
garybyrne profile image
Gary Byrne

Not to mention the pressure you are already under when in an interviewing environment.

Collapse
 
leobm profile image
Felix Wittmann • Edited

That is the proof, so css is finally turing complete :)

Collapse
 
garybyrne profile image
Gary Byrne

👍 It also makes you think about other common interview questions. Maybe it's possible for others