Creating dice using CSS grid 🎲
Tags: css, react, dice, grid
Published: 2020-02-02 20:59:49

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](https://davidwalsh.name/flexbox-dice), 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.

```html
<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>
```

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.

{% codepen https://codepen.io/LandonSchropp/pen/KpzzGo %}

# 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 |
+---+---+---+
```

CSS grid is supported by [all evergreen browsers](https://caniuse.com/#feat=css-grid), 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](https://gridbyexample.com/), 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:
```css
.face {
	display: grid;
	grid-template-rows: 1fr 1fr 1fr;
	grid-template-columns: 1fr 1fr 1fr;
}
```

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`:

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

The only HTML that we need is a `div.face` container with the above CSS and a `span.pip` for each pip:
```html
<div class="face">
	<span class="pip"></span>
	<span class="pip"></span>
	<span class="pip"></span>
	<span class="pip"></span>
</div>
```

The pips will be automatically placed in each of the cells, from left to right:
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/hokhomruu4h7zpltsfex.PNG)

# 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 `span`s 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 |
+---+---+---+
```
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):
```css
.face {
	display: grid;
	grid-template-areas:
		"a . c"
		"e g f"
		"d . b";
}
``` 
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:
```css
.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;
}
```

We are getting pretty close!
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/u9unq2dtyihqjsbtm29d.PNG)

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:

```css
.pip:nth-child(odd):last-child {
	grid-area: g;
}
```
And we have our final result!
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/z01znj7zx8c043zux1wy.PNG)

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.

{% codesandbox cly4v %}