Dice Face
A fair die with six possible states. Click to roll — the pip positions are correct, the animation is physical, the number updates. One honest square doing exactly one honest job.
Dice Face the 18th of 30 designs in the 30 CSS Badges collection. The design pairs CSS styling with a small amount of JavaScript for interactivity. Copy the HTML, CSS and JavaScript panels below into your project — the JS is self-contained, has zero dependencies, and is safe to drop into any framework (React, Vue, Svelte, plain HTML). The design honours prefers-reduced-motion and uses real semantic markup, so it ships accessibility-ready out of the box.
Live preview
The code
<div class="die-stage">
<div class="die-wrap">
<div class="die-cube" id="die-cube">
<div class="die-pip" data-pos="tl"></div>
<div class="die-pip" data-pos="tc"></div>
<div class="die-pip" data-pos="tr"></div>
<div class="die-pip" data-pos="ml"></div>
<div class="die-pip" data-pos="mc"></div>
<div class="die-pip" data-pos="mr"></div>
<div class="die-pip" data-pos="bl"></div>
<div class="die-pip" data-pos="bc"></div>
<div class="die-pip" data-pos="br"></div>
</div>
</div>
<div class="die-face-num" id="die-face-num">—</div>
<div class="die-caption">click the die to roll</div>
</div> .die-stage {
background: #fff;
padding: 60px 48px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 24px;
min-height: 360px;
}
.die-wrap { perspective: 600px; }
.die-cube {
width: 110px;
height: 110px;
background: #fff;
border: 2.5px solid #1a1612;
border-radius: 18px;
display: grid;
grid-template-areas:
"tl tc tr"
"ml mc mr"
"bl bc br";
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: 1fr 1fr 1fr;
padding: 14px;
cursor: pointer;
box-shadow:
4px 4px 0 #1a1612,
5px 5px 8px rgba(0,0,0,0.12);
transition: transform 0.15s, box-shadow 0.15s;
position: relative;
overflow: hidden;
}
.die-cube:active {
transform: translate(3px, 3px);
box-shadow: 1px 1px 0 #1a1612, 2px 2px 4px rgba(0,0,0,0.1);
}
.die-cube.is-rolling {
animation: die-roll 0.5s cubic-bezier(0.36, 0.07, 0.19, 0.97);
}
@keyframes die-roll {
0% { transform: rotate(0deg) scale(1); }
20% { transform: rotate(-8deg) scale(0.95); }
50% { transform: rotate(12deg) scale(0.92); }
75% { transform: rotate(-5deg) scale(0.97); }
100% { transform: rotate(0deg) scale(1); }
}
@media (prefers-reduced-motion: reduce) {
.die-cube.is-rolling { animation: none; }
}
.die-pip {
display: flex;
align-items: center;
justify-content: center;
}
.die-pip::after {
content: '';
width: 14px;
height: 14px;
border-radius: 50%;
background: transparent;
transition: background 0.2s;
}
.die-pip.is-on::after { background: #1a1612; }
.die-pip[data-pos="tl"] { grid-area: tl; }
.die-pip[data-pos="tc"] { grid-area: tc; }
.die-pip[data-pos="tr"] { grid-area: tr; }
.die-pip[data-pos="ml"] { grid-area: ml; }
.die-pip[data-pos="mc"] { grid-area: mc; }
.die-pip[data-pos="mr"] { grid-area: mr; }
.die-pip[data-pos="bl"] { grid-area: bl; }
.die-pip[data-pos="bc"] { grid-area: bc; }
.die-pip[data-pos="br"] { grid-area: br; }
.die-caption {
font-family: system-ui, "Bricolage Grotesque", sans-serif;
font-size: 11px;
letter-spacing: 0.2em;
text-transform: uppercase;
color: #aaa;
text-align: center;
}
.die-face-num {
font-family: Georgia, "Fraunces", serif;
font-size: 48px;
font-style: italic;
font-weight: 300;
color: #1a1612;
line-height: 1;
text-align: center;
} // Click the die to roll. Pip positions per face are encoded as
// arrays of grid-area names; the .is-on class lights up matching pips.
var dieFaces = {
1: ['mc'],
2: ['tr', 'bl'],
3: ['tr', 'mc', 'bl'],
4: ['tl', 'tr', 'bl', 'br'],
5: ['tl', 'tr', 'mc', 'bl', 'br'],
6: ['tl', 'tr', 'ml', 'mr', 'bl', 'br']
};
var dieCube = document.getElementById('die-cube');
var dieFaceNum = document.getElementById('die-face-num');
var dieRolling = false;
function dieSetFace(n) {
var pips = dieFaces[n];
dieCube.querySelectorAll('.die-pip').forEach(function (p) {
p.classList.toggle('is-on', pips.indexOf(p.dataset.pos) !== -1);
});
dieFaceNum.textContent = n;
}
if (dieCube) {
dieCube.addEventListener('click', function () {
if (dieRolling) return;
dieRolling = true;
dieCube.classList.add('is-rolling');
setTimeout(function () {
var n = Math.floor(Math.random() * 6) + 1;
dieSetFace(n);
dieCube.classList.remove('is-rolling');
dieRolling = false;
}, 280);
});
}