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

.container {
  width: 50%;
  min-width: 300px;
  margin: 0 auto;
  margin-top: 64px;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.dice-form {
  display: flex;
  flex-direction: column;
  width: 200px;
  margin-bottom: 32px;
}

.dice-form label {
  margin-bottom: 16px;
  display: flex;
  flex-direction: column;
}

.dice-form label input {
  margin-top: 8px;
}

.dice-container {
  display: flex;
  flex-wrap: wrap;
  width: 300px;
}

.die {
  width: 64px;
  height: 64px;
  border: 1px solid black;
  border-radius: 5px;
  margin: 16px;
  padding: 8px;
  display: flex;
}

.dot-container {
  position: relative;
  width: 100%;
  height: 100%;
}

.dot {
  position: absolute;
  width: 12px;
  height: 12px;
  background-color: #000;
  border-radius: 50%;
}

.dot.center {
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

.dot.top-right {
  top: 0;
  right: 0;
}

.dot.bottom-left {
  bottom: 0;
  left: 0;
}

.dot.bottom-right {
  bottom: 0;
  right: 0;
}

.dot.center-left {
  top: 50%;
  transform: translateY(-50%);
  left: 0;
}

.dot.center-right {
  top: 50%;
  transform: translateY(-50%);
  right: 0;
}
const { useEffect, useState } = React;

const DIE_FACES = [
  ['center'],
  ['top-right', 'bottom-left'],
  ['top-right', 'center', 'bottom-left'],
  ['top-left', 'top-right', 'bottom-left', 'bottom-right'],
  ['top-left', 'top-right', 'center', 'bottom-left', 'bottom-right'],
  ['top-left', 'top-right', 'center-left', 'center-right', 'bottom-left', 'bottom-right'],
];

const generateDice = (quantity) => {
  const NUM_SIDES = 6;
  
  let emptyDiceArray = new Array(quantity);
  return emptyDiceArray.fill().map(_ => Math.ceil(Math.random() * NUM_SIDES));
}

const DiceForm = ({ onSubmit }) => {
  const [numDice, setNumDice] = useState(undefined);
  
  const handleSubmit = (e) => {
    e.preventDefault();
    
    if (!numDice) {
      return;
    }
    
    onSubmit(numDice);
  }
  
  return (
    <form className="dice-form" onSubmit={handleSubmit}>
      <label>
        Number of dice
        <input type="number" min="1" max="99" onChange={(e) => setNumDice(Number(e.target.value))}/>
      </label>
      <button type="submit" disabled={!numDice}>Roll</button>
    </form>
  );
}

const Die = ({ value }) => {
  const dots = DIE_FACES[value - 1];
  return (
    <div className={`die face-${value}`}>
      <div className="dot-container">
        {dots.map(dotPosition => <div className={`dot ${dotPosition}`} />)}
      </div>
    </div>
  );
}

const Dice = ({ values }) => {
  return (
    <div className="dice-container">
      {values.map(value => <Die value={value} />)}
    </div>
  );
}

const App = () => {
  const [diceValues, setDiceValues] = useState(new Array(3).fill(6));
  const handleDiceFormSubmit = (numDice) => {
    setDiceValues(generateDice(numDice));
  }
  
  return (
    <div className="container">
      <DiceForm onSubmit={handleDiceFormSubmit} />
      <Dice values={diceValues} />
    </div>
  );
}

ReactDOM.render(
  <App/>, 
  document.querySelector('#app')
);
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

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