Creating dice using CSS grid 🎲

ekeijl

Edwin

Posted on February 2, 2020

Creating dice using CSS grid 🎲

Recently, I felt like creating a game as a nice little side project. The game involves dice, so I needed some way to visualise them. This article will explain how you can create dice using only HTML and CSS.

Flexing with CSS 💪

Why reinvent the wheel if other people have already solved the problem for us, right? A quick search around the web led me to this article by Landon Schropp, which describes how to make great looking dice using CSS flexbox.

He implemented the face of a die by simply positioning a span element for each pip of the die inside of a div. The div.column contains the vertically aligned pips.



<div class="fourth-face">
  <div class="column">
    <span class="pip"></span>
    <span class="pip"></span>
  </div>
  <div class="column">
    <span class="pip"></span>
    <span class="pip"></span>
  </div>
</div>


Enter fullscreen mode Exit fullscreen mode

These pips are positioned using flexbox and pushed to opposite sides of the die using the justify-content: space-between property. His final solution requires quite a bit of CSS to correctly style every possible face value.

We can do better with CSS grid 🔥

While I was playing around with the code, I realised that the pips of a traditional die are aligned in three rows and three columns, which is a great opportunity to use CSS grid.

Imagine the face of a die as a 3x3 grid, where each cell represents the position of a pip:



+---+---+---+
| a | b | c |
+---+---+---+
| d | e | f |
+---+---+---+
| g | h | i |
+---+---+---+


Enter fullscreen mode Exit fullscreen mode

CSS grid is supported by all evergreen browsers, but as you may expect, Internet Explorer offers only very basic support. The final result will therefore not work in IE11.
For a full guide on CSS grid, please refer to the amazing Grid by Example, by Rachel Andrews.

Creating the grid layout

To create a simple 3 by 3 grid using CSS, the only thing we need to do is set a container element to display: grid and tell it that we want three equally sized rows and columns:



.face {
    display: grid;
    grid-template-rows: 1fr 1fr 1fr;
    grid-template-columns: 1fr 1fr 1fr;
}


Enter fullscreen mode Exit fullscreen mode

The fr unit allows you to set the size of a row or column as a fraction of the free space of the grid container; in our case we want one third of the available space, so we use 1fr three times.

Instead of writing 1fr 1fr 1fr we can use repeat(3, 1fr) to repeat the 1fr unit three times. We also use the grid-template shorthand property that defines rows / columns:



.face {
    display: grid;
    grid-template: repeat(3, 1fr) / repeat(3, 1fr);
}


Enter fullscreen mode Exit fullscreen mode

The only HTML that we need is a div.face container with the above CSS and a span.pip for each pip:



<div class="face">
    <span class="pip"></span>
    <span class="pip"></span>
    <span class="pip"></span>
    <span class="pip"></span>
</div>


Enter fullscreen mode Exit fullscreen mode

The pips will be automatically placed in each of the cells, from left to right:
Alt Text

Positioning the pips

Now we get to the point where we need to position the pips for each of the dice values. It would be nice if the spans automatically flowed to the correct positions in the grid for each value. Sadly, we will need to set the position of each of the pips individually.

Recall the ASCII table at the beginning of the article? We are going to create something very similar using CSS. Instead of labeling the cells in the row order, we use this specific order so we only need a minimal amount of CSS to fix the edge cases:



+---+---+---+
| a |   | c |
+---+---+---+
| e | g | f |
+---+---+---+
| d |   | b |
+---+---+---+


Enter fullscreen mode Exit fullscreen mode

Two of the cells are left empty, because they are never used on our dice.

Grid template areas

We can translate this layout to CSS using the magical grid-template-areas property (which replaces the grid-template used above):



.face {
    display: grid;
    grid-template-areas:
        "a . c"
        "e g f"
        "d . b";
}


Enter fullscreen mode Exit fullscreen mode

So instead of using traditional units to size our rows and columns, we can just refer to each cell with a name. The syntax itself provides a visualization of the structure of the grid, just like our ASCII table. The names are defined by the grid-area property of the grid item. The period in the middle column signifies an empty cell.

Placing pips in an area

We use the grid-area property to give a name to this grid item. The grid template (above) can then reference the item by its name to place it in a specific area in the grid. The :nth-child() pseudo selector allows us to target each pip individually:



.pip:nth-child(2) {
    grid-area: b;
}
.pip:nth-child(3) {
    grid-area: c;
}
.pip:nth-child(4) {
    grid-area: d;
}
.pip:nth-child(5) {
    grid-area: e;
}
.pip:nth-child(6) {
    grid-area: f;
}


Enter fullscreen mode Exit fullscreen mode

We are getting pretty close!
Alt Text

As you can see, the values 1, 3 and 5 are still incorrect. Because of the order of the grid-template-areas that we chose earlier, we only need to reposition the last pip of each of these dice. To get the result that we want, we combine the :nth-child(odd) and :last-child pseudo selectors:



.pip:nth-child(odd):last-child {
    grid-area: g;
}


Enter fullscreen mode Exit fullscreen mode

And we have our final result!
Alt Text

Setting the position of each element individually does not scale well. But for our goal, the number of cases is very limited, it works for all dice values and it allows us to keep our HTML simple. It feels like a cleaner solution than the flexbox version above. It is also easier to translate into components using a JavaScript framework such as React as you will see below.

Final result 🎲

The implementation above only uses HTML and CSS, but I also created a very basic React app to show how you can use the dice as components.

💖 💪 🙅 🚩
ekeijl
Edwin

Posted on February 2, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related