I'm learning web development with JavaScript, HTML, and CSS and have just created my first application, a Pig Game. The app works well, however, I think my CSS is a bit over complicated and that the media query I wrote for responsiveness could be greatly improved.
Game rules:
Player 1 starts first. Each player take turns to roll a single dice as
many times as they wish, adding all roll results to a running total.
However if the player rolls a 1, they lose their gained score. Press
the hold button to stop rolling the dice. First player to 100 points
wins!
HTML:
<body>
<!-- Game Title -->
<div class="container">
<div class="game-title">
<h1>PIG GAME</h1>
</div>
<div class="game-info-btns">
<button type="button" class="new-game">🔁 NEW GAME</button>
<button type="button" class="how-btn">?</button>
</div>
<!-- How to Play Modal -->
<div class="how-to-play hidden">
<p>
Player 1 starts first. Each player take turns to roll a single dice as
many times as they wish, adding all roll results to a running total.
However if the player rolls a 1, they lose their gained score. Press
the hold button to stop rolling the dice. First player to 100 points
wins!
</p>
<button class="close-how">Ok</button>
</div>
<div class="how-overlay hidden"></div>
<!-- Main Game Window -->
<div class="game-window">
<!-- Player 1 -->
<div class="p1-overlay overlay-active"></div>
<div class="player1">
<h1 class="player1-message">PLAYER 1</h1>
<div class="score-wrapper">
<span class="p1-score">0</span>
</div>
<div class="current">
<p>CURRENT</p>
<p><span class="p1-current-hold">0</span></p>
</div>
<div class="game-btn">
<button type="button" class="p1-roll">🎲 ROLL DICE</button>
<button type="button" class="p1-hold">📥 HOLD</button>
</div>
</div>
<div class="dice hidden">
<img src="images/dice1.png" id="diceImg" alt="dice" />
</div>
<!-- Player 2 -->
<div class="p2-overlay show-me"></div>
<div class="player2">
<h1 class="player2-message">PLAYER 2</h1>
<div class="score-wrapper">
<span class="p2-score">0</span>
</div>
<div class="current">
<p>CURRENT</p>
<p><span class="p2-current-hold">0</span></p>
</div>
<div class="game-btn">
<button type="button" class="p2-roll">🎲 ROLL DICE</button>
<button type="button" class="p2-hold">📥 HOLD</button>
</div>
</div>
</div>
</div>
<script src="script.js"></script>
CSS:
html {
height: 100vh;
background: linear-gradient(to top left, #174e86, #97e4f4);
background-attachment: fixed;
}
body {
margin: 0;
}
/* Game Info */
.container {
display: grid;
grid-template-columns: 1fr;
grid-template-rows: min-content min-content 1fr;
align-content: center;
justify-items: center;
}
.game-title {
grid-column: 1 / 2;
grid-row: 1 / 2;
position: relative;
text-align: center;
margin: 0;
}
.game-title h1 {
font-family: 'Montserrat', sans-serif;
color: #eee;
font-size: 2.5rem;
text-align: center;
}
.game-info-btns {
grid-column: 1/2;
grid-row: 2/3;
margin-left: 44rem;
margin-bottom: 1.5rem;
display: flex;
}
.how-btn {
margin-left: 1rem;
font-size: 1rem;
font-weight: 700;
border-color: #eee;
background-color: rgba(191, 184, 191, 0.68);
color: #eee;
cursor: pointer;
}
.new-game {
font-family: 'Montserrat', sans-serif;
width: 8rem;
padding: 0.5rem;
border-radius: 1.5rem;
border-color: #fff;
box-shadow: 0 0 0.7rem rgba(0, 0, 0, 0.5);
background: #eee;
cursor: pointer;
}
/* How to Play Modal */
.how-to-play {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
height: 10rem;
width: 28rem;
background-color: #eee;
padding: 2rem;
box-shadow: 0 0 0.7rem rgba(0, 0, 0, 0.5);
border-radius: 2rem;
border-color: #d3d3d3;
z-index: 4;
}
.how-to-play p {
font-family: 'Montserrat', sans-serif;
font-weight: 500;
color: #000000;
}
.how-to-play button {
font-family: 'Montserrat', sans-serif;
color: #000000;
float: right;
background-color: rgba(236, 232, 236, 0.68);
cursor: pointer;
}
.how-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(3px);
z-index: 3;
}
/* Main Game */
.game-window {
grid-row: 3 / 4;
position: relative;
align-self: auto;
width: 55rem;
border-radius: 0.5rem;
background-color: #e8749a;
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr;
align-items: center;
font-family: 'Montserrat', sans-serif;
color: #eee;
margin-bottom: 2rem;
}
.player1 {
grid-column: 1 / 2;
grid-row: 1 / 2;
display: flex;
flex-wrap: wrap;
flex-direction: column;
align-items: center;
margin: 3rem 0;
}
.score-wrapper {
font-size: 3rem;
}
.p1-overlay {
grid-column: 1 / 2;
grid-row: 1 / 2;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
border-top-left-radius: 0.5rem;
border-bottom-left-radius: 0.5rem;
z-index: 1;
}
.player2 {
grid-column: 2 / 3;
grid-row: 1 / 2;
display: flex;
flex-direction: column;
align-items: center;
margin: 3rem 0;
}
.p2-overlay {
grid-column: 2 / 3;
grid-row: 1 / 2;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
border-top-right-radius: 0.5rem;
border-bottom-right-radius: 0.5rem;
z-index: 1;
}
.current {
background-color: #d12e64;
text-align: center;
font-size: 1.5rem;
width: 15rem;
border-radius: 2rem;
margin-bottom: 2rem;
margin-top: 2rem;
}
.current span {
font-size: 2rem;
}
.game-btn {
display: flex;
flex-direction: column;
align-items: center;
}
.game-btn button {
font-family: 'Montserrat', sans-serif;
padding: 0.5rem;
width: 10rem;
border-radius: 1rem;
box-shadow: 0 0 0.7rem rgba(0, 0, 0, 0.5);
border-color: rgba(224, 218, 218, 0.5);
background-color: rgba(191, 184, 184, 0.5);
color: #eee;
cursor: pointer;
}
.p1-roll,
.p2-roll {
margin-bottom: 1rem;
}
.dice {
position: absolute;
top: 25%;
left: 50%;
transform: translate(-50%, -50%);
width: 5rem;
height: 5rem;
z-index: 2;
}
.dice img {
width: 100%;
height: 100%;
object-fit: cover;
}
/* Hidden Class for Reuse */
.hidden {
display: none;
}
.overlay-active {
visibility: hidden;
opacity: 0;
transition: all 0.4s ease-in;
}
.overlay-unactive {
visibility: visible;
opacity: 1;
}
/* Media Query */
@media (max-width: 971px) {
.container {
position: relative;
}
.game-info-btns {
margin-left: 57%;
}
.new-game {
width: fit-content;
font-size: 0.8rem;
padding: 0.4rem;
}
.game-window {
display: grid;
grid-template-columns: 1fr;
grid-template-rows: 1fr 1fr;
grid-gap: 2rem;
width: 75%;
margin: 3rem 2rem;
}
.player1 {
grid-column: 1 / 1;
grid-row: 1 / 2;
}
.p1-overlay {
grid-column: 1 / 1;
grid-row: 1 / 2;
border-top-right-radius: 0.5rem;
border-bottom-left-radius: 0rem;
}
.player2 {
grid-column: 1 / 1;
grid-row: 2 / 3;
}
.p2-overlay {
grid-column: 1 / 1;
grid-row: 2 / 3;
border-top-right-radius: 0rem;
border-bottom-left-radius: 0.5rem;
}
.dice {
position: absolute;
top: 51.5%;
left: 50%;
transform: translate(-50%, -50%);
width: 4rem;
height: 4rem;
z-index: 2;
}
}
@media (max-width: 770px) {
.container {
position: relative;
}
.game-info-btns {
margin-left: 52%;
}
.dice {
width: 3rem;
height: 3rem;
}
}
@media (max-width: 660px) {
.game-title h1 {
font-size: 2rem;
}
.game-window {
margin: 0rem;
}
.game-info-btns {
margin-left: auto;
margin-right: 1rem;
}
.how-to-play {
width: 70%;
}
}
@media (max-width: 500px) {
.game-title h1 {
font-size: 1.5rem;
}
.game-window {
margin: 0rem;
}
.player1 h1,
.player2 h1 {
font-size: 1.5rem;
}
.player1 span,
.player2 span {
font-size: 1.5rem;
}
.game-btn button {
font-size: 0.8rem;
width: 8rem;
}
.current {
width: 10rem;
font-size: 0.8rem;
}
.current span {
font-size: 1rem;
}
.game-info-btns {
margin-bottom: 1rem;
}
.new-game {
font-size: 0.5rem;
}
.how-btn {
height: 100%;
align-self: end;
font-size: 0.8rem;
}
.how-to-play {
height: fit-content;
}
}
Javascript:
'use strict';
// Set DOM elements
// How to Play elements
const howBtn = document.querySelector('.how-btn');
const howWindow = document.querySelector('.how-to-play');
const howOverlay = document.querySelector('.how-overlay');
const closeHowBtn = document.querySelector('.close-how');
const newGameBtn = document.querySelector('.new-game');
// Gameplay Elements
//Player One Elements
const playerOneMessage = document.querySelector('.player1-message');
const playerOneOverlay = document.querySelector('.p1-overlay');
const playerOneScore = document.querySelector('.p1-score');
const playerOneCurrent = document.querySelector('.p1-current-hold');
const playerOneRoll = document.querySelector('.p1-roll');
const playerOneHold = document.querySelector('.p1-hold');
// Player Two Elements
const playerTwoMessage = document.querySelector('.player2-message');
const playerTwoOverlay = document.querySelector('.p2-overlay');
const playerTwoScore = document.querySelector('.p2-score');
const playerTwoCurrent = document.querySelector('.p2-current-hold');
const playerTwoRoll = document.querySelector('.p2-roll');
const playerTwoHold = document.querySelector('.p2-hold');
// Dice Elements
const diceBox = document.querySelector('.dice');
const dice = document.getElementById('diceImg');
const diceArr = [
'images/dice1.png',
'images/dice2.png',
'images/dice3.png',
'images/dice4.png',
'images/dice5.png',
'images/dice6.png',
];
// State Variables
let diceRoll = 0;
let score;
let current = 0;
// How to Play Modal
howBtn.addEventListener('click', function () {
removeHidden(howWindow);
removeHidden(howOverlay);
});
howOverlay.addEventListener('click', function () {
addHidden(howOverlay);
addHidden(howWindow);
});
closeHowBtn.addEventListener('click', function () {
addHidden(howOverlay);
addHidden(howWindow);
});
// Gameplay Logic
// Player 1 Rolls
playerOneRoll.addEventListener('click', function () {
removeHidden(diceBox);
diceRoll = Math.trunc(Math.random() * 6) + 1;
changeDiceImg(`images/dice${diceRoll}.png`);
current += diceRoll;
playerOneCurrent.textContent = current;
if (diceRoll === 1) playerTwoTurn();
});
playerOneHold.addEventListener('click', function () {
score = Number(playerOneScore.textContent) + current;
playerOneScore.textContent = score;
console.log(score);
if (score >= 100) {
playerOneMessage.textContent = 'PLAYER 1 WINS! 🥇';
gameEnd();
} else playerTwoTurn();
});
// Player 2 Rolls
playerTwoRoll.addEventListener('click', function () {
diceRoll = Math.trunc(Math.random() * 6) + 1;
changeDiceImg(`images/dice${diceRoll}.png`);
current += diceRoll;
playerTwoCurrent.textContent = current;
if (diceRoll === 1) playerOneTurn();
});
playerTwoHold.addEventListener('click', function () {
score = Number(playerTwoScore.textContent) + current;
playerTwoScore.textContent = score;
if (score >= 100) {
playerTwoMessage.textContent = 'PLAYER 2 WINS! 🥇';
gameEnd();
} else playerOneTurn();
});
// Reset values for New Game
newGameBtn.addEventListener('click', function () {
playerOneCurrent.textContent = 0;
playerOneScore.textContent = 0;
playerTwoCurrent.textContent = 0;
playerTwoScore.textContent = 0;
playerOneRoll.disabled = false;
playerOneHold.disabled = false;
playerTwoRoll.disabled = false;
playerTwoHold.disabled = false;
playerOneMessage.textContent = 'PLAYER 1';
playerTwoMessage.textContent = 'PLAYER 2';
addHidden(diceBox);
playerOneTurn();
});
// Reusable functions
function addHidden(element) {
element.classList.add('hidden');
}
function removeHidden(element) {
element.classList.remove('hidden');
}
function changeDiceImg(source) {
dice.src = source;
}
function playerOneTurn() {
playerOneOverlay.classList.add('overlay-active');
playerOneOverlay.classList.remove('overlay-unactive');
playerTwoOverlay.classList.remove('overlay-active');
playerTwoOverlay.classList.add('overlay-unactive');
current = 0;
playerTwoCurrent.textContent = current;
}
function playerTwoTurn() {
playerTwoOverlay.classList.add('overlay-active');
playerTwoOverlay.classList.remove('overlay-unactive');
playerOneOverlay.classList.remove('overlay-active');
playerOneOverlay.classList.add('overlay-unactive');
current = 0;
playerOneCurrent.textContent = current;
}
function gameEnd() {
current = 0;
addHidden(diceBox);
playerOneRoll.disabled = true;
playerOneHold.disabled = true;
playerOneCurrent.textContent = current;
playerTwoRoll.disabled = true;
playerTwoHold.disabled = true;
playerTwoCurrent.textContent = current;
}
If anyone could review the overall quality of the code in all three files, that'd be greatly appreciated.