JSFiddle - React, Tailwind, and code Playground

HTML

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="./static/css/main.css" />
    <script src="./static/js/main.js" defer></script>

    <link rel="apple-touch-icon" sizes="180x180" href="../../public/images/favicon/apple-touch-icon.png">
    <link rel="icon" type="image/png" sizes="32x32" href="../../public/images/favicon/favicon-32x32.png">
    <link rel="icon" type="image/png" sizes="16x16" href="../../public/images/favicon/favicon-16x16.png">
    <link rel="manifest" href="../../public/images/favicon/site.webmanifest">
    <link rel="mask-icon" href="../../public/images/favicon/safari-pinned-tab.svg" color="#5bbad5">
    <link rel="shortcut icon" href="../../public/images/favicon/favicon.ico">
    <meta name="msapplication-TileColor" content="#da532c">
    <meta name="msapplication-config" content="../../public/images/favicon/browserconfig.xml">
    <meta name="theme-color" content="#ffffff">

    <title>ThinkTap</title>
</head>
<h1>ThinkTap</h1>

<body>
    <div id="game">
        <div class="grid-cell" data-value="0"></div>
        <div class="grid-cell" data-value="1"></div>
        <div class="grid-cell" data-value="2"></div>
        <div class="grid-cell" data-value="3"></div>
        <div class="grid-cell" data-value="4"></div>
        <div class="grid-cell" data-value="5"></div>
        <div class="grid-cell" data-value="6"></div>
        <div class="grid-cell" data-value="7"></div>
        <div class="grid-cell" data-value="8"></div>
    </div>
    <div class="game-over">
        <span class="game-over-text"></span>
        <button class="restart">opnieuw spelen</button>
    </div>
    <div class="scoreboard">
        <div class="score-x-score">X: 0</div>
        <div class="score-o-score">O: 0</div>
    </div>
    <button class="reset"...

CSS

h1 {
  color: white;
  text-align: center;
  font-family: Dongle;
  font-weight: bold;
  font-size: 5rem;
}

#game {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  display: grid;
  grid-template-columns: repeat(3, auto);
  background-color: white;
  border-radius: 1rem;
}

.scoreboard {
  border: solid white;
  color: white;
  text-align: center;
  font-family: sans-serif;
  font-weight: bold;
  font-size: 2rem;
  width: 8rem;
  float:right;
  margin-right: 3rem;
  background-color: black;
  border-radius: 1rem;
}

.score-x-score {
border-bottom: solid white;
}

.grid-cell {
  border: 0.063rem solid #000;
  width: 6rem;
  height: 6rem;
  cursor: pointer;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 1.75rem;
}

.grid-cell:not(.disabled):hover {
  background: #C1DDFF;
}

.grid-cell:not(.disabled):first-child:hover {
  border-top-left-radius: 1rem;
}

.grid-cell:not(.disabled):nth-child(3):hover {
  border-top-right-radius: 1rem;
}

.grid-cell:not(.disabled):nth-last-child(3):hover {
  border-bottom-left-radius: 1rem;
}

.grid-cell:not(.disabled):last-child:hover {
  border-bottom-right-radius: 1rem;
}

.grid-cell:nth-child(-n + 3) {
  border-top: none;
}

.grid-cell:nth-child(3n + 1) {
  border-left: none;
}

.grid-cell:nth-child(3n + 3) {
  border-right: none;
}

.grid-cell:nth-last-of-type(-n + 3) {
  border-bottom: none;
}

.disabled {
  cursor: not-allowed;
}

.x::after {
  content: '❌';
}

.o::after {
  content: '⭕';
}

.game-over {
  border-radius: 1rem;
  display: none;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  box-shadow: rgb(0 0 0 / 50%) 0 0 0 9999px;
}

.game-over-text {
  position: absolute;
  top: -12rem;
  font-size:4rem;
  font-family: Dongle;
  font-weight: bold;
  text-align: center;
  padding-left: 6rem;
  transform: translateX(-30%);
  color: white;
}

button {
  appearance: button;
  background-color: #1899D6;
  border: solid...

JavaScript

const game = {
    xTurn: true,
    xState: [],
    oState: [],
    winningStates: [
        // Rij
        ['0', '1', '2'],
        ['3', '4', '5'],
        ['6', '7', '8'],

        // kolomen
        ['0', '3', '6'],
        ['1', '4', '7'],
        ['2', '5', '8'],

        // diagonaal
        ['0', '4', '8'],
        ['2', '4', '6']
    ]
}

let scores = {
    X: 0,
    O: 0
};

const updateScoreboard = () => {
    document.querySelector('.score-x-score').textContent = `X: ${scores.X}`;
    document.querySelector('.score-o-score').textContent = `O: ${scores.O}`;
};

document.addEventListener('click', event => {
    const target = event.target;
    const isCell = target.classList.contains('grid-cell');
    const isDisabled = target.classList.contains('disabled');

    if (isCell && !isDisabled) {
        const cellValue = target.dataset.value;

        game.xTurn === true ?
            game.xState.push(cellValue) :
            game.oState.push(cellValue)

        target.classList.add('disabled')
        target.classList.add(game.xTurn ? 'x' : 'o');

        game.xTurn = !game.xTurn;



        if (!document.querySelectorAll('.grid-cell:not(.disabled)').length) {
            document.querySelector('.game-over').classList.add('visible')
            document.querySelector('.game-over-text').textContent = 'Gelijk!'
        }

        game.winningStates.forEach(winningState => {
            const xWins = winningState.every(state => game.xState.includes(state))
            const oWins = winningState.every(state => game.oState.includes(state))

            if (xWins || oWins) {
                document.querySelectorAll('.grid-cell').forEach(cell => cell.classList.add('disabled'))
                document.querySelector('.game-over').classList.add('visible')
                document.querySelector('.game-over-text').textContent = xWins ?
                    '❌ wint!' :
                    '⭕ wint!'
                if (xWins) {
                    scores.X++;
         ...