<div id="root"></div>
* {
  box-sizing: border-box;
}

:root {
  --dice-size: 100px;
  --dice-scale: 0.5;
  --dot-size: 8px;
  --dot: radial-gradient(currentColor var(--dot-size), transparent 0);
  --face-background: radial-gradient(
    rgb(165 41 41 / 69%) 50%,
    rgb(127 13 13 / 69%)
  );

  color: rgba(255, 255, 255, 0.87);
  background-image: radial-gradient(#114410 1px, #102e15 0);
  background-size: 10px 10px;
}

#root {
  max-width: 1280px;
  margin: 0 auto;
  padding: 2rem;
  text-align: center;
}

body {
  font-family: "Host Grotesk", serif;
}

button {
  font-family: inherit;
}

.main-form {
  margin: 0 auto 4rem;
  display: flex;
  flex-direction: column;
  gap: 1rem;
  max-width: 10rem;

  label {
    display: flex;
    flex-direction: column;
    gap: 0.625rem;
    font-size: 1.2rem;
  }

  input {
    border-radius: 8px;
    padding: 0.5rem;
    font-size: 1rem;
  }
}

.primary-button {
  padding: 0.5rem 1rem;
  font-size: 1rem;
  letter-spacing: 1px;
  font-weight: bold;
  background: linear-gradient(145deg, #d4b03d, #c66a1a);
  color: white;
  border: none;
  border-radius: 15px;
  cursor: pointer;
  transition: background 300ms;
  text-transform: uppercase;
}

.primary-button:hover {
  background: linear-gradient(145deg, #e6bc54, #d68910);
}

.dice-container {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  justify-content: center;
}

.dice {
  width: var(--dice-size);
  height: var(--dice-size);
  perspective: 1000px;
  filter: drop-shadow(0 20px 15px rgba(0, 0, 0, 0.5));
  opacity: 0;
  animation: fade-in 250ms var(--animation-delay) ease-in forwards;
}

.dice__cube {
  width: 100%;
  height: 100%;
  position: relative;
  transform-style: preserve-3d;
  animation-duration: 800ms;
  animation-delay: var(--animation-delay);
  animation-timing-function: cubic-bezier(0.36, 0, 0.66, 1);
  animation-fill-mode: forwards;
}

.dice__face {
  display: grid;
  place-items: center;
  position: absolute;
  width: var(--dice-size);
  height: var(--dice-size);
  background: rgba(97, 10, 10, 0.8);
  color: rgb(243, 244, 217);
  border-radius: 16px;
  font-size: 34px;
}

/* front */
.dice__face:nth-child(2) {
  translate: 0 0 calc(var(--dice-size) / 2);
  background-image: var(--dot), var(--dot), var(--face-background);
  background-size: 50% 50%, 50% 50%, 100% 100%;
  background-repeat: no-repeat;
  background-position: 0 0, 100% 100%, 50% 50%;
}

/* right */
.dice__face:nth-child(3) {
  transform: rotateY(90deg) translateZ(calc(var(--dice-size) / 2));
  background-image: var(--dot), var(--dot), var(--dot), var(--face-background);
  background-size: 50% 50%, 50% 50%, 50% 50%, 100% 100%;
  background-repeat: no-repeat;
  background-position: 0 100%, 50% 50%, 100% 0, 50% 50%;
}

/* back */
.dice__face:nth-child(5) {
  transform: rotateY(180deg) translateZ(calc(var(--dice-size) / 2));
  background-image: var(--dot), var(--dot), var(--dot), var(--dot), var(--dot),
    var(--face-background);
  background-size: 50% 50%, 50% 50%, 50% 50%, 50% 50%, 50% 50%, 100% 100%;
  background-repeat: no-repeat;
  background-position: 0 0, 0 100%, 50% 50%, 100% 0, 100% 100%, 50% 50%;
}

/* left */
.dice__face:nth-child(4) {
  transform: rotateY(-90deg) translateZ(calc(var(--dice-size) / 2));
  background-image: var(--dot), var(--face-background);
  background-size: 50% 50%, 100% 100%;
}

/* top */
.dice__face:nth-child(1) {
  transform: rotateX(90deg) translateZ(calc(var(--dice-size) / 2));
  background-image: var(--dot), var(--face-background);
}

/* bottom */
.dice__face:nth-child(6) {
  transform: rotateX(-90deg) translateZ(calc(var(--dice-size) / 2));
  background-image: var(--dot), var(--dot), var(--dot), var(--dot), var(--dot),
    var(--dot), var(--face-background);
  background-size: 50% 50%, 50% 50%, 50% 50%, 50% 50%, 50% 50%, 50% 50%,
    100% 100%;
  background-repeat: no-repeat;
  background-position: 0 0, 0 50%, 0 100%, 100% 0, 100% 50%, 100% 100%, 50% 50%;
}

.dice--1 {
  .dice__cube {
    transform: scale(var(--dice-scale)) rotateX(-60deg) rotateY(-45deg)
      translateY(-200px) rotateZ(0deg);
    animation-name: throw-dice;
  }
}

.dice--2 {
  .dice__cube {
    transform: scale(var(--dice-scale)) rotateX(30deg) rotateZ(-45deg)
      translateZ(200px) rotateX(0deg);
    animation-name: throw-dice-2;
  }
}

.dice--3 {
  .dice__cube {
    transform: scale(var(--dice-scale)) rotateY(-90deg) rotateZ(-30deg)
      rotateX(45deg) translateX(200px) rotateY(0deg);
    animation-name: throw-dice-3;
  }
}

.dice--4 {
  .dice__cube {
    transform: scale(var(--dice-scale)) rotateY(90deg) rotateZ(30deg)
      rotateX(-45deg) translateX(-200px) rotateY(0deg);
    animation-name: throw-dice-4;
  }
}

.dice--5 {
  .dice__cube {
    transform: scale(var(--dice-scale)) rotateX(-60deg) rotateY(-45deg)
      rotateX(-90deg) translateZ(-200px) rotateY(0deg);
    animation-name: throw-dice-5;
  }
}

.dice--6 {
  .dice__cube {
    transform: scale(var(--dice-scale)) rotateX(-60deg) rotateY(-45deg)
      rotateX(180deg) translateY(200px) rotateZ(0deg);
    animation-name: throw-dice-6;
  }
}

@keyframes fade-in {
  0% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
}

@keyframes throw-dice {
  35% {
    transform: scale(var(--dice-scale)) rotateX(0deg) rotateY(-45deg)
      translateY(0) rotateZ(360deg);
  }
  50% {
    transform: scale(var(--dice-scale)) rotateX(-20deg) rotateY(-45deg)
      translateY(-60px) rotateZ(540deg);
  }
  65% {
    transform: scale(var(--dice-scale)) rotateX(-40deg) rotateY(-45deg)
      translateY(0) rotateZ(630deg);
  }
  80% {
    transform: scale(var(--dice-scale)) rotateX(-50deg) rotateY(-45deg)
      translateY(-20px) rotateZ(690deg);
  }
  100% {
    transform: scale(var(--dice-scale)) rotateX(-60deg) rotateY(-45deg)
      translateY(0) rotateZ(720deg);
  }
}

@keyframes throw-dice-2 {
  35% {
    transform: scale(var(--dice-scale)) rotateX(90deg) rotateZ(-45deg)
      translateZ(0) rotateX(-360deg);
  }
  50% {
    transform: scale(var(--dice-scale)) rotateX(70deg) rotateZ(-45deg)
      translateZ(60px) rotateX(-540deg);
  }
  65% {
    transform: scale(var(--dice-scale)) rotateX(60deg) rotateZ(-45deg)
      translateZ(0) rotateX(-630deg);
  }
  80% {
    transform: scale(var(--dice-scale)) rotateX(50deg) rotateZ(-45deg)
      translateZ(20px) rotateX(-690deg);
  }
  100% {
    transform: scale(var(--dice-scale)) rotateX(30deg) rotateZ(-45deg)
      translateZ(0) rotateX(-720deg);
  }
}

@keyframes throw-dice-3 {
  35% {
    transform: scale(var(--dice-scale)) rotateY(-90deg) rotateZ(-30deg)
      rotateX(60deg) translateX(0) rotateY(360deg);
  }
  50% {
    transform: scale(var(--dice-scale)) rotateY(-90deg) rotateZ(-30deg)
      rotateX(50deg) translateX(60px) rotateY(540deg);
  }
  65% {
    transform: scale(var(--dice-scale)) rotateY(-90deg) rotateZ(-30deg)
      rotateX(30deg) translateX(0) rotateY(630deg);
  }
  80% {
    transform: scale(var(--dice-scale)) rotateY(-90deg) rotateZ(-30deg)
      rotateX(40deg) translateX(20px) rotateY(690deg);
  }
  100% {
    transform: scale(var(--dice-scale)) rotateY(-90deg) rotateZ(-30deg)
      rotateX(45deg) translateX(0) rotateY(720deg);
  }
}

@keyframes throw-dice-4 {
  35% {
    transform: scale(var(--dice-scale)) rotateY(90deg) rotateZ(30deg)
      rotateX(-10deg) translateX(0) rotateY(360deg);
  }
  50% {
    transform: scale(var(--dice-scale)) rotateY(90deg) rotateZ(30deg)
      rotateX(0deg) translateX(-60px) rotateY(540deg);
  }
  65% {
    transform: scale(var(--dice-scale)) rotateY(90deg) rotateZ(30deg)
      rotateX(-25deg) translateX(0) rotateY(630deg);
  }
  80% {
    transform: scale(var(--dice-scale)) rotateY(90deg) rotateZ(30deg)
      rotateX(-40deg) translateX(-20px) rotateY(690deg);
  }
  100% {
    transform: scale(var(--dice-scale)) rotateY(90deg) rotateZ(30deg)
      rotateX(-45deg) translateX(0) rotateY(720deg);
  }
}

@keyframes throw-dice-5 {
  35% {
    transform: scale(var(--dice-scale)) rotateX(-60deg) rotateY(-45deg)
      rotateX(-60deg) translateZ(0) rotateY(-360deg);
  }
  50% {
    transform: scale(var(--dice-scale)) rotateX(-60deg) rotateY(-45deg)
      rotateX(-80deg) translateZ(-60px) rotateY(-540deg);
  }
  65% {
    transform: scale(var(--dice-scale)) rotateX(-60deg) rotateY(-45deg)
      rotateX(-60deg) translateZ(0) rotateY(-630deg);
  }
  80% {
    transform: scale(var(--dice-scale)) rotateX(-60deg) rotateY(-45deg)
      rotateX(-80deg) translateZ(-20px) rotateY(-690deg);
  }
  100% {
    transform: scale(var(--dice-scale)) rotateX(-60deg) rotateY(-45deg)
      rotateX(-90deg) translateZ(0) rotateY(-720deg);
  }
}

@keyframes throw-dice-6 {
  35% {
    transform: scale(var(--dice-scale)) rotateX(-60deg) rotateY(-45deg)
      rotateX(250deg) translateY(0) rotateZ(-360deg);
  }
  50% {
    transform: scale(var(--dice-scale)) rotateX(-60deg) rotateY(-45deg)
      rotateX(220deg) translateY(60px) rotateZ(-540deg);
  }
  65% {
    transform: scale(var(--dice-scale)) rotateX(-60deg) rotateY(-45deg)
      rotateX(200deg) translateY(0) rotateZ(-630deg);
  }
  80% {
    transform: scale(var(--dice-scale)) rotateX(-60deg) rotateY(-45deg)
      rotateX(190deg) translateY(20px) rotateZ(-690deg);
  }
  100% {
    transform: scale(var(--dice-scale)) rotateX(-60deg) rotateY(-45deg)
      rotateX(180deg) translateY(0) rotateZ(-720deg);
  }
}
/*
* https://frontendeval.com/questions/rolling-dice
*
* Create a dice roller application that can roll anywhere from 1-99 six-sided dice
*/


function Dice({ value, index }: { value: number; index: number }) {
  const className = `dice dice--${value}`;
  const styles = {
    "--animation-delay": `${index * 50}ms`,
  } as React.CSSProperties;

  return (
    <div className={className} style={styles}>
      <div className="dice__cube">
        {Array(6)
          .fill("")
          .map((_, index) => (
            <div key={index} className="dice__face"></div>
          ))}
      </div>
    </div>
  );
}

function App() {
  const [numberOfDice, setNumberOfDice] = React.useState<string>("");
  const [diceValues, setDiceValues] = React.useState<number[]>([
    1, 2, 3, 4, 5, 6,
  ]);
  const [round, setRound] = React.useState<number>(0);

  function handleNumberOfDiceChange(
    event: React.ChangeEvent<HTMLInputElement>
  ) {
    setNumberOfDice(event.target.value);
  }

  function handleNumberOfDiceSubmit(event: React.FormEvent<HTMLFormElement>) {
    event.preventDefault();

    if (!numberOfDice) {
      return;
    }

    const newDiceValues = Array.from(
      { length: parseInt(numberOfDice) },
      () => Math.floor(Math.random() * 6) + 1
    );

    console.log(newDiceValues);
    setDiceValues(newDiceValues);
    setRound((round) => round + 1);
  }

  return (
    <>
      <form className="main-form" onSubmit={handleNumberOfDiceSubmit}>
        <label>
          Number of dice:
          <input
            type="number"
            max={99}
            min={1}
            name="numberOfDice"
            value={numberOfDice}
            onChange={handleNumberOfDiceChange}
          />
        </label>
        <button className="primary-button" type="submit">
          Roll
        </button>
      </form>
      <div className="dice-container">
        {diceValues.map((value, index) => (
          <Dice key={`${index}-${round}`} value={value} index={index} />
        ))}
      </div>
    </>
  );
}

ReactDOM.render(<App />, document.getElementById('root'));
View Compiled
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js