JSFiddle - React, Tailwind, and code Playground

by Dutton

HTML

<div class="app">
  <div class="game-container">
    <img
      class="bg"
      src="https://i.imgur.com/5Cnwvxh.png"
      alt="Achtergrond"
    />

    <div class="sparkles"></div>

    <div class="timer-wrap">
      <span class="icon">⏱</span>
      <span id="timer">1:50</span>
    </div>

    <div class="attempts">
      Pogingen:
      <span id="attemptsCount">0</span>
    </div>

    <div class="hangman-area" id="hangmanArea">
      <svg viewBox="0 0 260 260" class="hangman" aria-label="Hangman">
        <line class="gallows" x1="30" y1="230" x2="120" y2="230"></line>
        <line class="gallows" x1="65" y1="230" x2="65" y2="35"></line>
        <line class="gallows" x1="65" y1="35" x2="165" y2="35"></line>
        <line class="gallows" x1="165" y1="35" x2="165" y2="60"></line>

        <g class="part head-part">
          <circle cx="165" cy="82" r="22"></circle>
        </g>

        <g class="part body-part">
          <line x1="165" y1="104" x2="165" y2="155"></line>
          <line x1="165" y1="118" x2="135" y2="138"></line>
          <line x1="165" y1="118" x2="195" y2="138"></line>
        </g>

        <g class="part legs-part">
          <line x1="165" y1="155" x2="140" y2="200"></line>
          <line x1="165" y1="155" x2="190" y2="200"></line>
        </g>
      </svg>
    </div>

    <div id="word"></div>
    <div id="keyboard"></div>

    <div class="lives-bar">
      <span class="lives-label">Levens:</span>
      <div class="lives-wrap" id="lives"></div>
    </div>

    <div class="overlay show" id="startPopup">
      <div class="popup">
        <p>
          <strong>GALGJE</strong><br>
          Raad het woord.. en win een code!<br>
          Je kunt het net zo vaak proberen totdat je het woord hebt geraden.<br>
          De code is te claimen t/m zondag Succes!
        </p>

        <button id="startBtn" type="button">
          Start
        </button>
      </div>
   ...

CSS

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  min-height: 100%;
  font-family: Arial, sans-serif;
}

body {
  background: #d9d9df;
  display: flex;
  justify-content: center;
}

.app {
  width: 688px;
  height: 700px;
  position: relative;
  overflow: hidden;
}

.game-container {
  position: relative;
  width: 688px;
  height: 700px;
  overflow: hidden;
}

.bg {
  position: absolute;
  inset: 0;
  width: 688px;
  height: 700px;
  object-fit: cover;
  display: block;
}

.sparkles {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 2;
  overflow: hidden;
}

.sparkles::before,
.sparkles::after {
  content: "";
  position: absolute;
  inset: -200%;
  background-image:
    radial-gradient(circle, rgba(255,255,255,0.9) 1px, transparent 1px),
    radial-gradient(circle, rgba(255,255,255,0.7) 1px, transparent 1px),
    radial-gradient(circle, rgba(255,255,255,0.6) 2px, transparent 2px);
  background-size:
    120px 120px,
    180px 180px,
    240px 240px;
  animation: sparkleMove 18s linear infinite;
  opacity: 0.35;
}

.sparkles::after {
  animation-duration: 28s;
  opacity: 0.2;
}

@keyframes sparkleMove {
  from {
    transform: translateY(0);
  }

  to {
    transform: translateY(120px);
  }
}

.timer-wrap {
  position: absolute;
  top: 125px;
  left: 62px;
  z-index: 3;
  display: flex;
  align-items: center;
  gap: 6px;
  color: #ffffff;
  font-size: 18px;
  font-weight: 700;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
}

.timer-danger {
  color: #ff6b8f !important;
  animation: timerBlink 0.8s infinite;
}

@keyframes timerBlink {
  0% {
    opacity: 1;
  }

  50% {
    opacity: 0.35;
  }

  100% {
    opacity: 1;
  }
}

.icon {
  font-size: 15px;
  line-height: 1;
}

.hangman-area {
  position: absolute;
  top: 160px;
  left: 72px;
  width: 185px;
  z-index: 3;
}

.hangman {
  width: 185px;
  height:...

JavaScript

document.addEventListener("DOMContentLoaded", () => {
  const wordRows = ["LENTE", "KRIEBELS"];
  const fullWord = wordRows.join("");

  let guessed = [];
  let mistakes = 0;
  let attempts = 0;

  const maxMistakes = 3;

  let timeLeft = 110;
  let timerInterval = null;

  let gameOver = false;
  let started = false;

  const wordDiv = document.getElementById("word");
  const keyboardDiv = document.getElementById("keyboard");
  const timerEl = document.getElementById("timer");
  const livesEl = document.getElementById("lives");

  const attemptsCount =
    document.getElementById("attemptsCount");

  const parts = [
    document.querySelector(".head-part"),
    document.querySelector(".body-part"),
    document.querySelector(".legs-part")
  ];

  const gameContainer =
    document.querySelector(".game-container");

  const startPopup =
    document.getElementById("startPopup");

  const losePopup =
    document.getElementById("losePopup");

  const endPopup =
    document.getElementById("endPopup");

  const startBtn =
    document.getElementById("startBtn");

  const tryAgainBtn =
    document.getElementById("tryAgainBtn");

  function updateTimer() {
    const min = Math.floor(timeLeft / 60);
    const sec = timeLeft % 60;

    timerEl.textContent =
      `${min}:${sec.toString().padStart(2, "0")}`;

    if (timeLeft <= 15) {
      timerEl.classList.add("timer-danger");
    } else {
      timerEl.classList.remove("timer-danger");
    }
  }

  function updateAttempts() {
    attemptsCount.textContent = attempts;
  }

  function updateLives() {
    livesEl.innerHTML = "";

    for (let i = 0; i < maxMistakes - mistakes; i++) {
      const heart = document.createElement("span");
      heart.textContent = "💗";
      livesEl.appendChild(heart);
    }
  }

  function renderWord() {
    wordDiv.innerHTML = "";

    wordRows.forEach((rowWord) => {
      const row =...