Gary Byrne's Blog

Gary Byrne's Blog

Fizzbuzz with CSS and Pug

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

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:

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

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:

#web-development#webdev#css#interview#html5
 
Share this