Add files via upload

This commit is contained in:
Zak Kermitz 2023-09-21 13:40:08 -04:00 committed by GitHub
parent 3421914748
commit 23d48ba15e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 18452 additions and 0 deletions

BIN
assets/CD.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

1
assets/share-icon.svg Normal file
View File

@ -0,0 +1 @@
<svg viewBox="0 0 64 64" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="M48 39.26a8.3 8.3 0 0 0-6.033 2.596L24.23 33.172c.061-.408.103-.821.103-1.246 0-.414-.04-.818-.098-1.215l17.711-8.589A8.302 8.302 0 0 0 48 24.741a8.333 8.333 0 1 0-8.333-8.333c0 .414.04.817.098 1.215l-17.711 8.589A8.3 8.3 0 0 0 16 23.593a8.333 8.333 0 0 0-8.333 8.333A8.332 8.332 0 0 0 16 40.259a8.3 8.3 0 0 0 6.033-2.596l17.737 8.684a8.378 8.378 0 0 0-.103 1.246c0 4.603 3.731 8.333 8.333 8.333s8.333-3.73 8.333-8.333A8.333 8.333 0 0 0 48 39.26z" fill="#ffffff" class="fill-000000"></path></svg>

After

Width:  |  Height:  |  Size: 586 B

5
assets/stat-logo.svg Normal file
View File

@ -0,0 +1,5 @@
<svg width="200" height="200" viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect y="100" width="50" height="100" fill="black"/>
<rect x="75" y="50" width="50" height="150" fill="black"/>
<rect x="150" width="50" height="200" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 272 B

16997
games.json Normal file

File diff suppressed because it is too large Load Diff

288
index.html Normal file
View File

@ -0,0 +1,288 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>COSTCODLE</title>
<link rel="icon" type="image/x-icon" href="./assets/CD.ico" />
<link rel="stylesheet" href="styles/game.css" />
<link rel="stylesheet" href="styles/overlay.css" />
<link rel="stylesheet" href="styles/global.css" />
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"
/>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=VT323&display=swap"
rel="stylesheet"
/>
<script
src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.4/jquery.min.js"
defer
></script>
<script src="scripts/main.js" defer></script>
<script src="scripts/searchBar.js" defer></script>
</head>
<body>
<div id="main-page">
<header id="site-header">
<div id="header-container">
<button id="info-button" data-overlay="info-overlay" type="button">
?
</button>
<h1 id="title" class="costco-red">
<center>COSTCO<span class="costco-blue">DLE</span></center>
</h1>
<button id="stat-button" data-overlay="stats-overlay" type="button">
<img
src="./assets/stat-logo.svg"
style="height: 100%; width: 100%"
/>
</button>
</div>
</header>
<main id="game-container">
<div id="info-card" class="animate__animated">
<div id="image-container"></div>
<div id="product-info"></div>
</div>
<div id="game-stats"></div>
<div id="guesses-container">
<div id="warning-toast" class="animate__animated hide">
<center>Invalid Guess ⚠️</center>
</div>
<div id="1" class="guess-container"></div>
<div id="2" class="guess-container"></div>
<div id="3" class="guess-container"></div>
<div id="4" class="guess-container"></div>
<div id="5" class="guess-container"></div>
<div id="6" class="guess-container"></div>
<div class="guess-container transparent-background">
<div id="input-container">
<div id="input-label">$</div>
<input
id="guess-input"
type="text"
pattern="^\$\d{1,3}(,\d{3})*(\.\d+)?$"
data-type="currency"
placeholder="Enter a guess..."
/>
</div>
<div id="button-container">
<button id="guess-button" class="active">SUBMIT</button>
</div>
</div>
</div>
<div id="info-overlay" class="overlay" style="display: none">
<div id="info-body" class="overlay-body">
<div class="info-section">
<h3><u>The game</u></h3>
<p>
Guess the <span class="costco-red">COSTCO</span
><span class="costco-blue">DLE</span> in 6 tries.
</p>
<br />
<ul style="list-style-type: square">
<li>Each guess must be a valid price.</li>
<li>
Incorrect guesses will help guide you to the target price.
</li>
</ul>
<br />
<p>If you guess within 5% of the target price, you win!</p>
<br />
<p>
A new <span class="costco-red">COSTCO</span
><span class="costco-blue">DLE</span> is available every day!
</p>
</div>
<div class="info-section">
<h3><u>Examples</u></h3>
<div class="guess-container transparent-background">
<div class="guess-value-container animate__flipInX">$10.00</div>
<div
class="guess-direction-container animate__flipInX guess-far"
>
&uarr;
</div>
</div>
<p>
First guess of $10.00 is low by more than 25% of the target
price.
</p>
<br />
<div class="guess-container transparent-background">
<div class="guess-value-container animate__flipInX">$18.00</div>
<div
class="guess-direction-container animate__flipInX guess-near"
>
&darr;
</div>
</div>
<p>
Next guess of $18.00 is too high but within 25% of the target
price.
</p>
<br />
<div class="guess-container transparent-background">
<div class="guess-value-container animate__flipInX">$16.00</div>
<div
class="guess-direction-container animate__flipInX guess-win"
>
&check;
</div>
</div>
<p>
The guess of $16.00 was within 5% of the target price! You win!
</p>
</div>
<div class="info-section">
<p>
If you would like to support my work, you can
<a
href="https://www.buymeacoffee.com/kerm"
target="_blank"
noreferrer="noopener noreferrer"
>buy me a coffee!<span style="font-size: 14px"></span></a
>
</p>
</div>
<div class="info-section">
<p>
Source code available on my
<a
href="https://github.com/KermWasTaken"
target="_blank"
noreferrer="noopener noreferrer"
>GitHub</a
>!
</p>
<p>
Suggestions? Find a bug?
<a href="mailto: costcodle@gmail.com">Let me know!</a>
</p>
</div>
<div class="info-section">
<p>
Special thanks to
<a
href="https://oec.world/en/tradle/"
target="_blank"
noreferrer="noopener noreferrer"
>
<span style="color: rgb(255, 166, 63)">TRAD</span>LE</a
>
and
<a
href="https://www.nytimes.com/games/wordle/index.html"
target="_blank"
noreferrer="noopener noreferrer"
>Wordle</a
>
for inspiring <span class="costco-red">COSTCO</span
><span class="costco-blue">DLE</span>!
</p>
<br />
<p>And thank you for checking out my game!</p>
</div>
<div style="margin-top: 8px">
<p>
Created by
<a
href="https://github.com/KermWasTaken"
target="_blank"
noreferrer="noopener noreferrer"
>@Kerm</a
>
</p>
</div>
</div>
</div>
<div id="stats-overlay" class="overlay" style="display: none">
<div id="stat-body" class="overlay-body">
<div id="stats-toast" class="animate__animated hide">
<center>Results copied to clipboard</center>
</div>
<p id="statistics-title">STATISTICS</p>
<div id="statistics">
<div class="statistic">
<div id="number-wins" class="stat"></div>
<div class="descriptor">Played</div>
</div>
<div class="statistic">
<div id="win-percent" class="stat"></div>
<div class="descriptor">Win %</div>
</div>
<div class="statistic">
<div id="current-streak" class="stat"></div>
<div class="descriptor">
<center>Current<br />Streak</center>
</div>
</div>
<div class="statistic">
<div id="max-streak" class="stat"></div>
<div class="descriptor">
<center>Max<br />Streak</center>
</div>
</div>
</div>
<p>GUESS DISTRIBUTION</p>
<div class="graph">
<div class="graph-row">
<div class="graph-label">1</div>
<div id="graph-1" class="graphElem"></div>
</div>
<div class="graph-row">
<div class="graph-label">2</div>
<div id="graph-2" class="graphElem"></div>
</div>
<div class="graph-row">
<div class="graph-label">3</div>
<div id="graph-3" class="graphElem"></div>
</div>
<div class="graph-row">
<div class="graph-label">4</div>
<div id="graph-4" class="graphElem"></div>
</div>
<div class="graph-row">
<div class="graph-label">5</div>
<div id="graph-5" class="graphElem"></div>
</div>
<div class="graph-row">
<div class="graph-label">6</div>
<div id="graph-6" class="graphElem"></div>
</div>
</div>
<div class="share-button-container">
<button id="share-button" class="share-button">
Share<img src="./assets/share-icon.svg" class="share-icon" />
</button>
</div>
</div>
</div>
</main>
<footer id="site-footer">
<div id="database-link">
<a
href="https://costcofdb.com/food-database"
target="_blank"
rel="noopener noreferrer"
>Costco Food Database</a
>
</div>
<div id="dev-name">&copy;Kerm</div>
</footer>
</div>
</body>
</html>

476
scripts/main.js Normal file
View File

@ -0,0 +1,476 @@
/*
Declaration of global variables
*/
//Product info variables
let productName;
let productPrice;
let productImage;
//Timeout IDs
let shakeTimeout;
let toastTimeout;
let warningTimeout;
/*
Global variable constants
*/
//The day Costcodle was launched. Used to find game number each day
const costcodleStartDate = new Date("09/21/2023");
const gameNumber = getGameNumber();
//Elements with event listeners to play the game
const input = document.getElementById("guess-input");
const buttonInput = document.getElementById("guess-button");
const infoButton = document.getElementById("info-button");
infoButton.addEventListener("click", switchState);
const statButton = document.getElementById("stat-button");
statButton.addEventListener("click", switchState);
const shareButton = document.getElementById("share-button");
shareButton.addEventListener("click", copyStats);
//User stats object
localStorage.removeItem("stats");
const userStats = JSON.parse(localStorage.getItem("stats")) || {
numGames: 0,
numWins: 0,
winsInNum: [0, 0, 0, 0, 0, 0],
currentStreak: 0,
maxStreak: 0,
};
//User game state
localStorage.removeItem("state");
const gameState = JSON.parse(localStorage.getItem("state")) || {
gameNumber: -1,
guesses: [],
hasWon: false,
};
/*
Starts playing the game. Called at beginning of execution
*/
playGame();
function playGame() {
fetchGameData(getGameNumber());
}
/*
Acquiring Game Data
*/
//Fetches the current day's game data from the json and starts game
function fetchGameData(gameNumber) {
fetch("./games.json")
.then((response) => response.json())
.then((json) => {
productName = json[`game-${gameNumber}`].name;
productPrice = json[`game-${gameNumber}`].price;
productPrice = Number(productPrice.slice(1, productPrice.length));
productImage = json[`game-${gameNumber}`].image;
initializeGame();
});
}
/*
Used to initialize the game board using the current game state
*/
function initializeGame() {
//Reset game state and track new game if user last played on a previous day
if (gameState.gameNumber !== gameNumber) {
gameState.gameNumber = gameNumber;
gameState.guesses = [];
gameState.hasWon = false;
userStats.numGames++;
localStorage.setItem("stats", JSON.stringify(userStats));
localStorage.setItem("state", JSON.stringify(gameState));
}
displayProductCard();
updateGameBoard();
if (gameState.guesses.length < 6 && !gameState.hasWon) {
addEventListeners();
} else {
buttonInput.setAttribute("disabled", "");
buttonInput.classList.remove("active");
input.setAttribute("disabled", "");
input.setAttribute("placeholder", "Game Over!");
}
}
function displayProductCard() {
//First, update the image container with the new product image
const imageContainer = document.getElementById("image-container");
//Create a new image element to dynamically store game image
const productImageElement = document.createElement("img");
productImageElement.src = productImage;
productImageElement.setAttribute("id", "product-image");
//Add created image to the image container
imageContainer.appendChild(productImageElement);
//Select product info element and update the html to display product name
const productInfo = document.getElementById("product-info");
productInfo.innerHTML = `<center>${productName}</center>`;
}
function updateGameBoard() {
updateGuessStat();
gameState.guesses.forEach((guess, index) => displayGuess(guess, index + 1));
}
function updateGuessStat() {
const guessStats = document.getElementById("game-stats");
if (gameState.hasWon) {
guessStats.innerHTML = `<center>You win! Congratulations!🎉</center>`;
guessStats.innerHTML += `<center>The price was $${productPrice}</center>`;
return;
}
if (gameState.guesses.length === 6) {
guessStats.innerHTML = `<center>Better luck next time!</center>`;
guessStats.innerHTML += `<center>The price was $${productPrice}</center>`;
} else {
guessStats.innerHTML = `Guess: ${gameState.guesses.length + 1}/6`;
}
}
/*
Event Listeners
*/
//Text input event listener to submit guess when user presses "Enter"
function inputEventListener(event) {
if (event.key === "Enter") {
handleInput();
}
}
//Button event listener to submit guess when user presses guess button
function buttonEventListener() {
handleInput();
}
function handleInput() {
const strippedString = input.value.replaceAll(",", "");
const guess = Number(strippedString).toFixed(2);
console.log(guess);
if (isNaN(guess) || !strippedString) {
displayWarning();
return;
}
checkGuess(guess);
input.value = "";
function displayWarning() {
clearTimeout(warningTimeout);
const warningElem = document.getElementById("warning-toast");
warningElem.classList.remove("hide");
warningElem.classList.add("animate__flipInX");
warningTimeout = setTimeout(() => {
warningElem.classList.remove("animate__flipInX");
warningElem.classList.add("animate__flipOutX");
setTimeout(() => {
warningElem.classList.remove("animate__flipOutX");
warningElem.classList.add("hide");
}, 1000);
}, 2000);
}
}
function copyStats() {
let output = `Costcodle #${gameNumber}`;
if (!gameState.hasWon) {
output += ` X/6\n`;
} else {
output += ` ${gameState.guesses.length}/6\n`;
}
gameState.guesses.forEach((guess) => {
switch (guess.direction) {
case "&uarr;":
output += `⬆️`;
break;
case "&darr;":
output += `⬇️`;
break;
case "&check;":
output += ``;
break;
}
switch (guess.closeness) {
case "guess-far":
output += `🟥`;
break;
case "guess-near":
output += `🟨`;
break;
}
output += `\n`;
});
output += `https://www.costcodle.com`;
navigator.clipboard.writeText(output);
displayToast();
function displayToast() {
clearTimeout(toastTimeout);
const toastElem = document.getElementById("stats-toast");
toastElem.classList.remove("hide");
toastElem.classList.add("animate__flipInX");
toastTimeout = setTimeout(() => {
toastElem.classList.remove("animate__flipInX");
toastElem.classList.add("animate__flipOutX");
setTimeout(() => {
toastElem.classList.remove("animate__flipOutX");
toastElem.classList.add("hide");
}, 1000);
}, 3000);
}
}
function addEventListeners() {
input.addEventListener("keydown", inputEventListener);
buttonInput.addEventListener("click", buttonEventListener);
input.addEventListener("focus", () => {
input.setAttribute("placeholder", "0.00");
});
input.addEventListener("blur", () => {
input.setAttribute("placeholder", "Enter a guess...");
});
}
function removeEventListeners() {
buttonInput.setAttribute("disabled", "");
buttonInput.classList.remove("active");
input.setAttribute("disabled", "");
input.setAttribute("placeholder", "Game Over!");
input.removeEventListener("keydown", inputEventListener);
buttonInput.removeEventListener("click", buttonEventListener);
}
/*
Handles the logic of Costocodle
Creates a guess object based on user guess and checks win condition
*/
function checkGuess(guess) {
const guessObj = { guess, closeness: "", direction: "" };
const percentAway = calculatePercent(guess);
if (Math.abs(percentAway) <= 5) {
guessObj.closeness = "guess-win";
gameState.hasWon = true;
} else {
shakeBox();
if (Math.abs(percentAway) <= 25) {
guessObj.closeness = "guess-near";
} else {
guessObj.closeness = "guess-far";
}
}
if (gameState.hasWon) {
guessObj.direction = "&check;";
} else if (percentAway < 0) {
guessObj.direction = "&uarr;";
} else {
guessObj.direction = "&darr;";
}
gameState.guesses.push(guessObj);
localStorage.setItem("state", JSON.stringify(gameState));
displayGuess(guessObj);
if (gameState.hasWon) {
gameWon();
} else if (gameState.guesses.length === 6) {
gameLost();
}
}
/*
Displays guess object from either game state or a new guess
*/
function displayGuess(guess, index = gameState.guesses.length) {
const guessContainer = document.getElementById(index);
const guessValueContainer = document.createElement("div");
const infoContainer = document.createElement("div");
guessValueContainer.classList.add(
"guess-value-container",
"animate__flipInX"
);
infoContainer.classList.add("guess-direction-container", "animate__flipInX");
guessValueContainer.innerHTML = `$${guess.guess}`;
infoContainer.classList.add(guess.closeness);
infoContainer.innerHTML = guess.direction;
guessContainer.classList.add("animate__flipOutX");
setTimeout(() => {
guessContainer.classList.add("transparent-background");
guessContainer.appendChild(guessValueContainer);
guessContainer.appendChild(infoContainer);
}, 500);
updateGuessStat();
}
/*
Helper function to compute guess accuracy
*/
function calculatePercent(guess) {
return ((guess * 100) / (productPrice * 100)) * 100 - 100;
}
/*
End state function to handle win/loss conditions
*/
function gameWon() {
userStats.numWins++;
userStats.currentStreak++;
userStats.winsInNum[gameState.guesses.length - 1]++;
if (userStats.currentStreak > userStats.maxStreak) {
userStats.maxStreak = userStats.currentStreak;
}
gameState.hasWon = true;
localStorage.setItem("state", JSON.stringify(gameState));
localStorage.setItem("stats", JSON.stringify(userStats));
removeEventListeners();
}
function gameLost() {
userStats.currentStreak = 0;
localStorage.setItem("stats", JSON.stringify(userStats));
removeEventListeners();
}
/*
DOM manipulation functions for overlays and animations
*/
function switchState(event) {
const overlayBtnClicked = event.currentTarget.dataset.overlay;
const overlayElem = document.getElementById(overlayBtnClicked);
const title = document.getElementById("title");
if (overlayElem.style.display === "flex") {
title.innerHTML = `COSTCO<span class="costco-blue">DLE</span>`;
overlayElem.style.display = "none";
return;
}
if (overlayBtnClicked === "info-overlay") {
document.getElementById("stats-overlay").style.display = "none";
renderInfo();
} else {
document.getElementById("info-overlay").style.display = "none";
renderStats();
}
function renderInfo() {
title.innerHTML = `HOW TO <span class="costco-blue">PLAY</span>`;
overlayElem.style.display = "flex";
}
function renderStats() {
title.innerHTML = `GAME <span class="costco-blue">STATS</span>`;
renderStatistics();
graphDistribution();
overlayElem.style.display = "flex";
function renderStatistics() {
const numWinsElem = document.getElementById("number-wins");
numWinsElem.innerHTML = `${userStats.numGames}`;
const winPercentElem = document.getElementById("win-percent");
if (userStats.numGames === 0) {
winPercentElem.innerHTML = `0`;
} else {
winPercentElem.innerHTML = `${Math.round(
(userStats.numWins / userStats.numGames) * 100
)}`;
}
const currentStreakElem = document.getElementById("current-streak");
currentStreakElem.innerHTML = `${userStats.currentStreak}`;
const maxStreakElem = document.getElementById("max-streak");
maxStreakElem.innerHTML = `${userStats.maxStreak}`;
}
function graphDistribution() {
console.log("here");
userStats.winsInNum.forEach((value, index) => {
const graphElem = document.getElementById(`graph-${index + 1}`);
if (userStats.numWins === 0) {
graphElem.style = `width: 5%`;
} else {
graphElem.style = `width: ${
Math.floor((value / userStats.numWins) * 0.95 * 100) + 5
}%`;
}
graphElem.innerHTML = `${value}`;
});
}
}
}
function shakeBox() {
clearTimeout(shakeTimeout);
const infoCard = document.getElementById("info-card");
if (infoCard.classList.contains("animate__headShake")) {
infoCard.classList.remove("animate__headShake");
}
shakeTimeout = setTimeout(
() => infoCard.classList.add("animate__headShake"),
100
);
}
/*
Finds current game number based off of Costcodle start date
*/
function getGameNumber() {
const currDate = new Date();
let timeDifference = currDate.getTime() - costcodleStartDate.getTime();
let dayDifference = timeDifference / (1000 * 3600 * 24);
return Math.ceil(dayDifference);
}

89
scripts/searchBar.js Normal file
View File

@ -0,0 +1,89 @@
/*
Copyright (c) 2023 by Wade Williams (https://codepen.io/559wade/pen/LRzEjj)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
$("input[data-type='currency']").on({
keyup: function () {
formatCurrency($(this));
},
blur: function () {
formatCurrency($(this), "blur");
},
});
function formatNumber(n) {
// format number 1000000 to 1,234,567
return n.replace(/\D/g, "").replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
function formatCurrency(input, blur) {
// appends $ to value, validates decimal side
// and puts cursor back in right position.
// get input value
var input_val = input.val();
// don't validate empty input
if (input_val === "") {
return;
}
// original length
var original_len = input_val.length;
// initial caret position
var caret_pos = input.prop("selectionStart");
// check for decimal
if (input_val.indexOf(".") >= 0) {
// get position of first decimal
// this prevents multiple decimals from
// being entered
var decimal_pos = input_val.indexOf(".");
// split number by decimal point
var left_side = input_val.substring(0, decimal_pos);
var right_side = input_val.substring(decimal_pos);
// add commas to left side of number
left_side = formatNumber(left_side);
// validate right side
right_side = formatNumber(right_side);
// On blur make sure 2 numbers after decimal
if (blur === "blur") {
right_side += "00";
}
// Limit decimal to only 2 digits
right_side = right_side.substring(0, 2);
// join number by .
input_val = left_side + "." + right_side;
} else {
// no decimal entered
// add commas to number
// remove all non-digits
input_val = formatNumber(input_val);
// final formatting
if (blur === "blur") {
input_val += ".00";
}
}
// send updated string to input
input.val(input_val);
// put caret back in the right position
var updated_len = input_val.length;
caret_pos = updated_len - original_len + caret_pos;
input[0].setSelectionRange(caret_pos, caret_pos);
}

209
styles/game.css Normal file
View File

@ -0,0 +1,209 @@
#game-container {
position: relative;
display: flex;
align-items: center;
flex-direction: column;
flex-grow: 1;
justify-content: start;
background-color: gainsboro;
}
#info-card {
display: flex;
overflow: hidden;
align-items: center;
flex-direction: column;
margin-top: 36px;
margin-bottom: 24px;
padding: 12px;
border-radius: 8px;
background-color: rgb(173, 173, 173);
box-shadow: 0 3px 10px;
}
#image-container {
display: flex;
overflow: hidden;
justify-content: center;
width: 300px;
height: 300px;
margin-bottom: 10px;
border-radius: 8px;
background-color: white;
}
#product-image {
height: 100%;
}
#product-info {
font-size: 24px;
width: 300px;
}
#game-stats {
margin-bottom: 12px;
}
#guesses-container {
display: flex;
flex-direction: column;
justify-content: space-between;
position: relative;
width: 324px;
height: 275px;
padding: 3px;
border-radius: 8px;
background-color: transparent;
}
.guess-container {
display: flex;
overflow: hidden;
flex: 1;
flex-direction: row;
margin-bottom: 5px;
border-radius: 8px;
background-color: gray;
box-shadow: inset 0 0 5px;
}
.guess-value-container {
font-size: 28px;
display: flex;
overflow: hidden;
align-items: center;
flex: 2;
justify-content: center;
border-radius: 8px;
background-color: gray;
box-shadow: inset 0 0 5px;
}
#input-container {
font-size: 28px;
display: flex;
overflow: hidden;
align-items: center;
flex: 2;
justify-content: center;
border: 1px solid black;
border-radius: 8px;
background-color: gray;
}
.guess-value-container,
.guess-direction-container {
animation: flipInX;
animation-duration: 1s;
}
.guess-direction-container,
#button-container {
font-size: 28px;
font-weight: bold;
display: flex;
overflow: hidden;
align-items: center;
flex: 1;
justify-content: center;
margin-left: 5px;
color: rgba(0, 0, 0, 0.5);
border-radius: 8px;
box-shadow: inset 0 0 5px;
}
.guess-win {
background-color: rgb(1, 154, 1);
}
.guess-near {
background-color: rgb(255, 196, 37);
}
.guess-far {
background-color: rgb(255, 37, 37);
}
#warning-toast {
position: absolute;
z-index: 1;
top: 50px;
left: 52px;
font-size: 24px;
width: 220px;
padding: 18px;
color: white;
border-radius: 8px;
background-color: rgb(0, 0, 0);
}
#input-label {
display: flex;
align-items: center;
flex: 1;
justify-content: center;
height: 40px;
border-right: 2px solid black;
background-color: rgb(173, 173, 173);
}
#guess-input {
font-family: "VT323", monospace;
font-size: 24px;
width: 180px;
height: 40px;
padding-left: 6px;
color: gainsboro;
border: none;
border-radius: 0 8px 8px 0;
background-color: rgb(94, 94, 94);
}
#guess-input::placeholder {
font-size: 24px;
color: gainsboro;
}
#guess-button {
font-family: "VT323", monospace;
font-size: 24px;
width: 100%;
height: 100%;
color: rgb(227, 42, 54);
border: none;
background-color: rgb(173, 173, 173);
}
#guess-button.active:hover {
cursor: pointer;
background-color: rgba(0, 96, 169, 0.5);
}

236
styles/global.css Normal file
View File

@ -0,0 +1,236 @@
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
*:focus {
outline: none;
}
body {
font-family: "VT323", monospace;
font-size: 30px;
background-color: gainsboro;
}
body::-webkit-scrollbar {
display: none;
}
a {
color: black;
}
h3 {
font-weight: normal;
margin-bottom: 6px;
}
ul {
margin-left: 1rem;
}
img {
-webkit-user-drag: none;
-khtml-user-drag: none;
-moz-user-drag: none;
-o-user-drag: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-o-user-select: none;
user-select: none;
}
.costco-red {
color: rgb(227, 42, 54);
}
.costco-blue {
color: rgb(0, 96, 169);
}
.hide {
display: none;
}
.transparent-background {
background-color: transparent;
box-shadow: none;
}
#main-page {
display: flex;
flex-direction: column;
width: 100wh;
height: 100vh;
}
#site-header {
display: flex;
align-items: center;
justify-content: center;
background-color: gainsboro;
}
#header-container {
display: flex;
align-items: center;
justify-content: space-between;
width: 344px;
border-bottom: 2px solid gray;
}
#title {
font-size: 60px;
}
#info-button {
font-family: "VT323", monospace;
font-size: 55px;
width: 30px;
height: 60px;
cursor: pointer;
border: none;
background: none;
}
#stat-button {
font-size: 30px;
width: 30px;
height: 60px;
cursor: pointer;
border: none;
background: none;
}
#site-footer {
display: flex;
align-items: center;
justify-content: space-between;
border-top: 2px solid gray;
background-color: gainsboro;
}
#database-link {
margin-left: 12px;
}
#dev-name {
margin-right: 12px;
}
::-webkit-scrollbar {
width: 4px;
}
::-webkit-scrollbar-track {
background-color: transparent;
}
::-webkit-scrollbar-thumb {
background-color: rgba(0, 0, 0, 0.5);
}
::-webkit-scrollbar-thumb:hover {
background-color: #a8bbbf;
}
@media screen and (max-height: 900px) {
#image-container {
width: 275px;
height: 275px;
}
#product-info {
width: 275px;
}
}
@media screen and (max-height: 850px) {
#image-container {
width: 250px;
height: 250px;
}
#product-info {
width: 250px;
}
}
@media screen and (max-height: 825px) {
#image-container {
width: 225px;
height: 225px;
}
#product-info {
width: 225px;
}
#site-footer {
font-size: 24px;
}
#game-stats {
font-size: 24px;
}
}
@media screen and (max-height: 785px) {
#image-container {
width: 200px;
height: 200px;
}
#product-info {
width: 200px;
}
}
@media screen and (max-height: 760px) {
#image-container {
height: 175px;
}
#product-image {
height: 175;
}
#product-info {
font-size: 20px;
}
}
@media screen and (max-height: 725px) {
#image-container {
height: 150px;
}
}
@media screen and (max-height: 700px) {
#image-container {
height: 125px;
}
}
@media screen and (max-height: 675px) {
#info-card {
margin-top: 20px;
}
}

151
styles/overlay.css Normal file
View File

@ -0,0 +1,151 @@
.overlay {
position: absolute;
z-index: 1;
top: 0;
left: 0;
display: flex;
align-items: start;
justify-content: center;
width: 100%;
height: 100%;
padding-top: 10px;
padding-bottom: 10px;
background-color: gainsboro;
}
.overlay-body {
overflow-y: auto;
width: 350px;
height: 100%;
padding: 0 6px;
}
#info-body {
font-size: 20px;
}
.info-section {
margin-top: 8px;
padding-bottom: 8px;
border-bottom: 2px solid gray;
}
#stat-body {
position: relative;
display: flex;
align-items: center;
flex-direction: column;
background-color: gainsboro;
}
#stats-toast {
position: absolute;
top: 16px;
width: 300px;
padding: 18px;
font-size: 24px;
color: white;
border-radius: 8px;
background-color: rgb(0, 0, 0);
}
#statistics-title {
margin-bottom: 24px;
}
#statistics {
display: flex;
justify-content: space-evenly;
width: 100%;
margin-bottom: 32px;
}
.statistic {
display: flex;
align-items: center;
flex-direction: column;
}
.stat {
font-size: 66px;
}
.descriptor {
font-size: 22px;
}
.graph {
display: flex;
flex-direction: column;
width: 100%;
margin-top: 16px;
margin-bottom: 16px;
}
.graph-label {
margin-right: 6px;
}
.graphElem {
font-size: 24px;
display: flex;
align-items: center;
justify-content: end;
padding-right: 4px;
color: white;
background-color: rgb(0, 96, 169);
}
.graph-row {
display: flex;
align-items: center;
height: 26px;
margin-bottom: 15px;
}
.share-button-container {
display: flex;
justify-content: center;
width: 100%;
}
.share-button {
font-family: "VT323", monospace;
font-size: 36px;
display: flex;
align-items: center;
justify-content: center;
padding: 8px 64px;
color: white;
border: none;
border-radius: 32px;
background-color: rgb(227, 42, 54);
}
.share-button:hover {
background-color: rgba(227, 42, 54, 0.8);
}
.share-icon {
height: 32px;
margin-left: 16px;
}