├── README.md ├── index.html ├── media ├── cactus.png ├── favicon.ico ├── replit.png ├── resp.png ├── ruby.png └── slide.wav ├── script.js └── style.css /README.md: -------------------------------------------------------------------------------- 1 | # Slidey 2 | 3 | Recreation of the classic picture sliding puzzle thingy. 4 | 5 | *** 6 | 7 | ## How To Play 8 | 9 | #### Setting up 10 | - Choose an image from the 4 provided or upload your own 11 | - Press play 12 | - The image will be shuffled 13 | - One random tile will be removed 14 | - The timer will begin 15 | 16 | #### Playing 17 | - Click on the tile that you wish to move into the unoccupied square 18 | - You may only move a tile directly adjacent to the unoccupied square 19 | 20 | #### Winning 21 | - Rearrange all the tiles so that they match the original image 22 | 23 | *** 24 | 25 | ## Features 26 | 27 | - 3 grid sizes: 3x3, 4x4, 5x5 28 | - Multiple images to use 29 | - Upload your own image 30 | - Sliding animation 31 | - Sliding sound 32 | - Ability to enable/disable the animations and sounds 33 | - Best score and time for each mode are remembered 34 | - Fully responsive 35 | - Fun! 36 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | Slidey 16 | 17 | 18 | 19 |
20 | 21 |
22 |

Slidey

23 |
Slide the tiles to correct the picture!
24 | 25 |

Moves: 0

Time: 0:00

Best Score: NA

Best Time: NA

35 | 36 |
37 |

Choose an image

38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 |
47 |
48 | 49 | 50 |
87 | 88 |
89 |
90 |

Mode

91 |
92 | 93 | 94 |
95 |
96 | 97 | 98 |
99 |
100 | 101 | 102 |
103 |
104 |
105 |

Preferences

106 |
107 | 108 | 109 |
110 |
111 | 112 | 113 |
114 |
115 | 116 | 117 |
118 |
119 |
120 | 121 | 122 | 123 |
124 |
125 |

Choose an image

126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 |
134 |
135 |

Mode

136 |
137 | 138 | 139 |
140 |
141 | 142 | 143 |
144 |
145 | 146 | 147 |
148 |
149 |
150 |

Preferences

151 |
152 | 153 | 154 |
155 |
156 | 157 | 158 |
159 |
160 | 161 | 162 |
163 |
164 |
165 |
166 | 167 | 168 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /media/cactus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coding-Cactus/slidey/4c96674f3545179307e080608c5b97e0121748c8/media/cactus.png -------------------------------------------------------------------------------- /media/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coding-Cactus/slidey/4c96674f3545179307e080608c5b97e0121748c8/media/favicon.ico -------------------------------------------------------------------------------- /media/replit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coding-Cactus/slidey/4c96674f3545179307e080608c5b97e0121748c8/media/replit.png -------------------------------------------------------------------------------- /media/resp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coding-Cactus/slidey/4c96674f3545179307e080608c5b97e0121748c8/media/resp.png -------------------------------------------------------------------------------- /media/ruby.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coding-Cactus/slidey/4c96674f3545179307e080608c5b97e0121748c8/media/ruby.png -------------------------------------------------------------------------------- /media/slide.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Coding-Cactus/slidey/4c96674f3545179307e080608c5b97e0121748c8/media/slide.wav -------------------------------------------------------------------------------- /script.js: -------------------------------------------------------------------------------- 1 | window.onload = () => { 2 | let bestScore = localStorage.getItem("bestScore-x3"); 3 | bestScore = bestScore === null ? "NA" : bestScore; 4 | document.getElementById("bestScore").innerHTML = bestScore; 5 | 6 | let bestTime = localStorage.getItem("bestTime-x3"); 7 | bestTime = bestTime === null ? "NA" : displayTime(bestTime); 8 | document.getElementById("bestTime").innerHTML = bestTime; 9 | 10 | let soundOn = true; 11 | let animationOn = true; 12 | let backgroundOn = true; 13 | 14 | function soundToggle() { 15 | soundOn = !soundOn; 16 | document.getElementById("sound").checked = soundOn; 17 | document.getElementById("sound2").checked = soundOn; 18 | } 19 | 20 | function animationToggle() { 21 | animationOn = !animationOn; 22 | document.getElementById("animation").checked = animationOn; 23 | document.getElementById("animation2").checked = animationOn; 24 | } 25 | 26 | function backgroundToggle() { 27 | backgroundOn = !backgroundOn; 28 | document.getElementById("background-toggle").checked = backgroundOn; 29 | document.getElementById("background-toggle2").checked = backgroundOn; 30 | if (backgroundOn) { 31 | background(); 32 | } else { 33 | clearInterval(bgInterval); 34 | document.getElementById("background").innerHTML = ""; 35 | } 36 | 37 | } 38 | 39 | document.getElementById("sound").addEventListener("change", soundToggle); 40 | document.getElementById("sound2").addEventListener("change", soundToggle); 41 | document.getElementById("animation").addEventListener("change", animationToggle); 42 | document.getElementById("animation2").addEventListener("change", animationToggle); 43 | document.getElementById("background-toggle").addEventListener("change", backgroundToggle); 44 | document.getElementById("background-toggle2").addEventListener("change", backgroundToggle); 45 | 46 | 47 | function radioChange(e) { 48 | const id = e.target.id; 49 | if (id.includes("mobile-")) { 50 | document.getElementById(id.replace("mobile-", "")).checked = true; 51 | } else { 52 | document.getElementById("mobile-"+id).checked = true; 53 | } 54 | 55 | const mode = id.replace("mobile-", "").substr(1); 56 | document.getElementById("board").className = mode; 57 | 58 | let bestScore = localStorage.getItem("bestScore-"+mode); 59 | bestScore = bestScore === null ? "NA" : bestScore; 60 | document.getElementById("bestScore").innerHTML = bestScore; 61 | 62 | let bestTime = localStorage.getItem("bestTime-"+mode); 63 | bestTime = bestTime === null ? "NA" : displayTime(bestTime); 64 | document.getElementById("bestTime").innerHTML = bestTime; 65 | 66 | clearInterval(interval); 67 | resetBoard(); 68 | } 69 | 70 | document.getElementById("3x3").addEventListener("change", radioChange); 71 | document.getElementById("4x4").addEventListener("change", radioChange); 72 | document.getElementById("5x5").addEventListener("change", radioChange); 73 | document.getElementById("mobile-3x3").addEventListener("change", radioChange); 74 | document.getElementById("mobile-4x4").addEventListener("change", radioChange); 75 | document.getElementById("mobile-5x5").addEventListener("change", radioChange); 76 | 77 | 78 | function displayTime(d) { 79 | let s = String(d % 60); 80 | let m = String(Math.floor(d / 60)); 81 | 82 | if (s.length < 2) {s = "0" + s} 83 | 84 | return m + ":" + s; 85 | } 86 | 87 | function resetBoard() { 88 | const old_element = document.getElementById("board"); 89 | const new_element = old_element.cloneNode(true); 90 | old_element.parentNode.replaceChild(new_element, old_element); 91 | new_element.className = new_element.className.replace(" won", ""); 92 | 93 | let boardTiles = document.getElementById("board").children; 94 | for (let row = 0; row < boardTiles.length; row++) { 95 | for (let col = 0; col < boardTiles[row].children.length; col++) { 96 | boardTiles[row].children[col].className = "row" + (row+1) + " col" + (col+1) + " tile"; 97 | } 98 | } 99 | 100 | document.getElementById("moves").innerHTML = "0"; 101 | document.getElementById("time").innerHTML = "0:00"; 102 | document.getElementById("start").innerHTML = "Play"; 103 | } 104 | 105 | function shuffleBoard(blank, mode) { 106 | let tiles = []; 107 | for (let row = 1; row <= mode; row++) { 108 | for (let col = 1; col <= mode; col++) { 109 | tiles.push("row" + row + " col" + col); 110 | } 111 | } 112 | tiles[blank] = "blank"; 113 | 114 | for (let i = 0; i < 10000; i++) { 115 | let moved = false; 116 | while (!moved) { 117 | let num = Math.floor(Math.random() * 4); 118 | if (num === 0) { 119 | const moveIndex = tiles.indexOf("blank") - mode; 120 | if (moveIndex > -1) { 121 | const tmp = tiles[moveIndex]; 122 | tiles[moveIndex] = "blank"; 123 | tiles[moveIndex+mode] = tmp; 124 | moved = true; 125 | } 126 | } else if (num === 1) { 127 | const moveIndex = tiles.indexOf("blank") + 1; 128 | if (moveIndex % mode !== 0 && moveIndex < mode**2) { 129 | const tmp = tiles[moveIndex]; 130 | tiles[moveIndex] = "blank"; 131 | tiles[moveIndex-1] = tmp; 132 | moved = true; 133 | } 134 | } else if (num === 2) { 135 | const moveIndex = tiles.indexOf("blank") + mode; 136 | if (moveIndex < mode**2) { 137 | const tmp = tiles[moveIndex]; 138 | tiles[moveIndex] = "blank"; 139 | tiles[moveIndex-mode] = tmp; 140 | moved = true; 141 | } 142 | } else if (num === 3) { 143 | const moveIndex = tiles.indexOf("blank") - 1; 144 | if ((moveIndex + 1) % mode !== 0 && moveIndex > 0) { 145 | const tmp = tiles[moveIndex]; 146 | tiles[moveIndex] = "blank"; 147 | tiles[moveIndex+1] = tmp; 148 | moved = true; 149 | } 150 | } 151 | } 152 | } 153 | 154 | for (let row = 0; row < mode; row++) { 155 | for (let col=0; col < mode; col++) { 156 | document.getElementById("board").children[row].children[col].className = tiles[row*mode+col] + " tile"; 157 | } 158 | } 159 | } 160 | 161 | function checkWin(blank, moves, timeStart, interval, mode) { 162 | let correctTiles = []; 163 | for (let row = 1; row <= mode; row++) { 164 | for (let col = 1; col <= mode; col++) { 165 | correctTiles.push("row" + row + " col" + col); 166 | } 167 | } 168 | correctTiles[blank] = "blank"; 169 | 170 | const boardTiles = document.getElementById("board").children; 171 | for (let row = 0; row < mode; row++) { 172 | for (let col = 0; col < mode; col++) { 173 | if (boardTiles[row].children[col].className.replace(" tile", "") !== correctTiles[row*mode+col]) { 174 | return; 175 | } 176 | } 177 | } 178 | 179 | clearInterval(interval); 180 | document.getElementById("board").className += " won"; 181 | 182 | if (localStorage.getItem("bestScore-x"+mode) === null || moves < Number(localStorage.getItem("bestScore-x"+mode))) { 183 | localStorage.setItem("bestScore-x"+mode, moves); 184 | document.getElementById("bestScore").innerHTML = moves; 185 | } 186 | 187 | let time = Math.floor((new Date() - timeStart)/1000); 188 | if (localStorage.getItem("bestTime-x"+mode) === null || time < Number(localStorage.getItem("bestTime-x"+mode))) { 189 | localStorage.setItem("bestTime-x"+mode, time); 190 | document.getElementById("bestTime").innerHTML = displayTime(time); 191 | } 192 | } 193 | 194 | let interval = 0; 195 | document.getElementById("start").addEventListener("click", () => { 196 | const old_element = document.getElementById("board"); 197 | const new_element = old_element.cloneNode(true); 198 | old_element.parentNode.replaceChild(new_element, old_element); 199 | 200 | new_element.className = new_element.className.replace(" won", ""); 201 | const mode = Number(new_element.className.substr(1)); 202 | let moves = 0; 203 | document.getElementById("moves").innerHTML = moves; 204 | let timeStart = new Date().getTime(); 205 | document.getElementById("time").innerHTML = "0:00"; 206 | 207 | const blank = Math.floor(Math.random() * mode**2); 208 | document.getElementById("start").innerHTML = "Restart"; 209 | shuffleBoard(blank, mode); 210 | 211 | clearInterval(interval); 212 | interval = setInterval(() => { 213 | document.getElementById("time").innerHTML = displayTime(Math.floor((new Date().getTime() - timeStart)/1000)); 214 | }, 250); 215 | 216 | const sound = new Howl({ 217 | src: ['media/slide.wav'] 218 | }); 219 | 220 | document.querySelectorAll(".tile").forEach( tile => { 221 | tile.addEventListener("click", (e) => { 222 | const row = Number(tile.parentElement.id.replace("row", "")); 223 | let col; 224 | for (let c = 0; c < mode; c++) { 225 | if (document.getElementById("row"+row).children[c] == e.target) { 226 | col = c; 227 | break; 228 | } 229 | } 230 | 231 | if (row < mode) { 232 | if (document.getElementById("row"+String(row + 1)).children[col].className.includes("blank")) { 233 | if (document.getElementById("sound").checked) { 234 | sound.play(); 235 | } 236 | let time = 0; 237 | if (document.getElementById("animation").checked) { 238 | time = 150; 239 | tile.className += " down"; 240 | } 241 | setTimeout(() => { 242 | document.getElementById("row"+String(row + 1)).children[col].className = tile.className.replace(" down", ""); 243 | tile.className = "blank tile"; 244 | moves++; 245 | document.getElementById("moves").innerHTML = moves; 246 | checkWin(blank, moves, timeStart, interval, mode); 247 | return; 248 | }, time); 249 | } 250 | } if (row > 1) { 251 | if (document.getElementById("row"+String(row - 1)).children[col].className.includes("blank")) { 252 | if (document.getElementById("sound").checked) { 253 | sound.play(); 254 | } 255 | let time = 0; 256 | if (document.getElementById("animation").checked) { 257 | time = 150; 258 | tile.className += " up"; 259 | } 260 | setTimeout(() => { 261 | document.getElementById("row"+String(row - 1)).children[col].className = tile.className.replace(" up", ""); 262 | tile.className = "blank tile"; 263 | moves++; 264 | document.getElementById("moves").innerHTML = moves; 265 | checkWin(blank, moves, timeStart, interval, mode); 266 | return; 267 | }, time); 268 | } 269 | } if (col < mode-1) { 270 | if (document.getElementById("row"+String(row)).children[col+1].className.includes("blank")) { 271 | if (document.getElementById("sound").checked) { 272 | sound.play(); 273 | } 274 | let time = 0; 275 | if (document.getElementById("animation").checked) { 276 | time = 150; 277 | tile.className += " right"; 278 | } 279 | setTimeout(() => { 280 | document.getElementById("row"+String(row)).children[col+1].className = tile.className.replace(" right", ""); 281 | tile.className = "blank tile"; 282 | moves++; 283 | document.getElementById("moves").innerHTML = moves; 284 | checkWin(blank, moves, timeStart, interval, mode); 285 | return; 286 | }, time); 287 | } 288 | } if (col > 0) { 289 | if (document.getElementById("row"+String(row)).children[col-1].className.includes("blank")) { 290 | if (document.getElementById("sound").checked) { 291 | sound.play(); 292 | } 293 | let time = 0; 294 | if (document.getElementById("animation").checked) { 295 | time = 150; 296 | tile.className += " left"; 297 | } 298 | setTimeout(() => { 299 | document.getElementById("row"+String(row)).children[col-1].className = tile.className.replace(" left", ""); 300 | tile.className = "blank tile"; 301 | moves++; 302 | document.getElementById("moves").innerHTML = moves; 303 | checkWin(blank, moves, timeStart, interval, mode); 304 | return; 305 | }, time); 306 | } 307 | } 308 | }); 309 | }); 310 | }); 311 | 312 | document.querySelectorAll("#images button").forEach(button => { 313 | button.addEventListener("click", () =>{ 314 | document.querySelectorAll(".tile").forEach(tile => { 315 | tile.style.backgroundImage = "url("+button.getAttribute("data-src")+")"; 316 | }); 317 | clearInterval(interval); 318 | resetBoard(); 319 | }); 320 | }); 321 | document.querySelectorAll("#mobile-images button").forEach(button => { 322 | button.addEventListener("click", () =>{ 323 | document.querySelectorAll(".tile").forEach(tile => { 324 | tile.style.backgroundImage = "url("+button.getAttribute("data-src")+")"; 325 | }); 326 | clearInterval(interval); 327 | resetBoard(); 328 | }); 329 | }); 330 | 331 | document.getElementById("img-input").onchange = function (e) { 332 | const files = e.target.files; 333 | var fr = new FileReader(); 334 | fr.onload = function () { 335 | document.querySelectorAll(".tile").forEach(tile => { 336 | tile.style.backgroundImage = "url("+fr.result+")"; 337 | }); 338 | } 339 | fr.readAsDataURL(files[0]); 340 | clearInterval(interval); 341 | resetBoard(); 342 | e.preventDefault(); 343 | } 344 | document.getElementById("mobile-img-input").onchange = function (e) { 345 | const files = e.target.files; 346 | var fr = new FileReader(); 347 | fr.onload = function () { 348 | document.querySelectorAll(".tile").forEach(tile => { 349 | tile.style.backgroundImage = "url("+fr.result+")"; 350 | }); 351 | } 352 | fr.readAsDataURL(files[0]); 353 | clearInterval(interval); 354 | resetBoard(); 355 | e.preventDefault(); 356 | } 357 | 358 | 359 | 360 | let bgInterval; 361 | function background() { 362 | if (backgroundOn) { 363 | document.getElementById("background").innerHTML = ""; 364 | clearInterval(bgInterval); 365 | 366 | const cols = Math.ceil(window.innerWidth / 175); 367 | const rows = Math.ceil(window.innerHeight / 175); 368 | const randBlank = Math.floor(Math.random() * (cols*rows)); 369 | 370 | const colours = ["#cc0000bb", "#00cc00bb", "#0000ccbb", "#cccc00bb", "#cc00ccbb", "#00ccccbb"] 371 | for (let r = 1; r <= rows; r++) { 372 | let row = document.createElement("div"); 373 | row.id = "bg-row"+r; 374 | row.className = "bg-row" 375 | document.getElementById("background").appendChild(row); 376 | for (let c = 1; c <= cols; c++) { 377 | let col = document.createElement("div"); 378 | document.getElementById("bg-row"+r).appendChild(col); 379 | } 380 | } 381 | 382 | for (let r = 1; r <= rows; r++) { 383 | for (let c = 1; c <= cols; c++) { 384 | col = document.getElementById("bg-row"+r).children[c-1]; 385 | if ((r-1)*cols + c !== randBlank) { 386 | col.className = "bg-tile bg-col" + c; 387 | colour1 = colours[Math.floor(Math.random() * colours.length)]; 388 | colour2 = colours[Math.floor(Math.random() * colours.length)]; 389 | while (colour1 == colour2) { 390 | colour1 = colours[Math.floor(Math.random() * colours.length)]; 391 | colour2 = colours[Math.floor(Math.random() * colours.length)]; 392 | } 393 | col.style.background = "linear-gradient("+Math.floor(Math.random() * 360)+"deg,"+colour1+","+colour2+")"; 394 | } else { 395 | col.className = "bg-tile bg-blank bg-col"+c; 396 | } 397 | } 398 | } 399 | 400 | let last_direction; 401 | bgInterval = setInterval(() => { 402 | const blank = document.querySelector(".bg-blank"); 403 | const blankRow = Number(blank.parentElement.id.replace("bg-row", "")); 404 | const blankCol = Number(blank.className.replace("bg-tile bg-blank bg-col", "")); 405 | let moved = false; 406 | while (!moved) { 407 | let direction = Math.floor(Math.random() * 4); 408 | while (direction === 3-last_direction) { 409 | direction = Math.floor(Math.random() * 4); 410 | } 411 | if (direction === 0 && blankRow < rows) { 412 | moved = true; 413 | let tile = document.getElementById("bg-row"+String(blankRow + 1)).children[blankCol-1]; 414 | tile.className += " up"; 415 | setTimeout(() => { 416 | blank.style.background = tile.style.background; 417 | blank.className = "bg-tile bg-col"+blankCol; 418 | tile.className = "bg-tile bg-blank bg-col"+blankCol; 419 | tile.background = ""; 420 | last_direction = direction; 421 | }, 500); 422 | } else if (direction === 1 && blankCol < cols) { 423 | moved = true; 424 | let tile = document.getElementById("bg-row"+blankRow).children[blankCol]; 425 | tile.className += " left"; 426 | setTimeout(() => { 427 | blank.style.background = tile.style.background; 428 | blank.className = "bg-tile bg-col"+blankCol; 429 | tile.className = "bg-tile bg-blank bg-col"+(blankCol+1); 430 | tile.background = ""; 431 | last_direction = direction; 432 | }, 500); 433 | } else if (direction === 2 && blankCol > 1) { 434 | moved = true; 435 | let tile = document.getElementById("bg-row"+blankRow).children[blankCol-2]; 436 | tile.className += " right"; 437 | setTimeout(() => { 438 | blank.style.background = tile.style.background; 439 | blank.className = "bg-tile bg-col"+blankCol; 440 | tile.className = "bg-tile bg-blank bg-col"+(blankCol-1); 441 | tile.background = ""; 442 | last_direction = direction; 443 | }, 500); 444 | } else if (direction === 3 && blankRow > 1) { 445 | moved = true; 446 | let tile = document.getElementById("bg-row"+String(blankRow - 1)).children[blankCol-1]; 447 | tile.className += " down"; 448 | setTimeout(() => { 449 | blank.style.background = tile.style.background; 450 | blank.className = "bg-tile bg-col"+blankCol; 451 | tile.className = "bg-tile bg-blank bg-col"+blankCol; 452 | tile.background = ""; 453 | last_direction = direction; 454 | }, 500); 455 | } 456 | } 457 | }, 1000); 458 | } 459 | } 460 | background(); 461 | window.addEventListener("resize", background); 462 | } -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | @import url("https://fonts.googleapis.com/css2?family=KoHo&display=swap"); 2 | 3 | * { 4 | margin: 0; 5 | padding: 0; 6 | box-sizing: border-box; 7 | font-family: "KoHo", sans-serif; 8 | } 9 | 10 | h1 { font-size: 5vh; } 11 | h5 { font-size: 2vh; } 12 | h1, h5 { text-align: center; } 13 | 14 | h1, h5, #scores, #mobile-options h4, #mobile-options label { 15 | color: #fff; 16 | text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000; 17 | } 18 | #mobile-options label { text-shadow: -0.5px -0.5px 0 #000, 0.5px -0.5px 0 #000, -0.5px 0.5px 0 #000, 0.5px 0.5px 0 #000; } 19 | #mobile-options .img-label { font-size: 18px; } 20 | 21 | main { 22 | width: max-content; 23 | margin: auto; 24 | --size: 65vh; 25 | padding-top: 20px; 26 | } 27 | 28 | #board { 29 | --padding: 5px; 30 | --spacing: 2.5px; 31 | vertical-align: middle; 32 | height: var(--size); 33 | width: var(--size); 34 | padding: var(--padding); 35 | margin: 5px auto; 36 | background-color: rgba(200, 200, 200, 0.85); 37 | border-radius: var(--padding); 38 | display: inline-block; 39 | } 40 | 41 | .x3 { --mode: 3; } 42 | .x4 { --mode: 4; } 43 | .x5 { --mode: 5; } 44 | 45 | .row { 46 | width: 100%; 47 | height: calc(100% / var(--mode) - var(--spacing) * (1 - 1 / var(--mode))); 48 | } 49 | 50 | .tile { 51 | height: 100%; 52 | width: calc(100% / var(--mode) - var(--spacing) * (1 - 1 / var(--mode))); 53 | display: inline-block; 54 | background-color: white; 55 | background-image: url(/media/replit.png); 56 | background-size: calc(var(--size) - 2 * var(--padding)); 57 | background-repeat: no-repeat; 58 | transition: transform 150ms; 59 | } 60 | 61 | .row:not(:first-child) { margin-top: var(--spacing); } 62 | .row .tile:not(:first-child) { margin-left: var(--spacing); } 63 | 64 | .row .col1 { background-position-x: 0; } 65 | .row .col2 { background-position-x: calc((var(--size) / var(--mode) - var(--spacing)) * -1) } 66 | .row .col3 { background-position-x: calc((var(--size) / (var(--mode) / 2) - 2 * var(--spacing)) * -1); } 67 | .row .col4 { background-position-x: calc((var(--size) / (var(--mode) / 3) - 3 * var(--spacing)) * -1); } 68 | .row .col5 { background-position-x: calc((var(--size) / (var(--mode) / 4) - 4 * var(--spacing)) * -1); } 69 | 70 | .row1.tile { background-position-y: 0; } 71 | .row2.tile { background-position-y: calc((var(--size) / var(--mode) - var(--spacing)) * -1); } 72 | .row3.tile { background-position-y: calc((var(--size) / (var(--mode) / 2) - 2 * var(--spacing)) * -1); } 73 | .row4.tile { background-position-y: calc((var(--size) / (var(--mode) / 3) - 3 * var(--spacing)) * -1); } 74 | .row5.tile { background-position-y: calc((var(--size) / (var(--mode) / 4) - 4 * var(--spacing)) * -1); } 75 | 76 | .tile.right { transform: translateX(calc(var(--size) / var(--mode) - var(--spacing))); } 77 | .tile.left { transform: translateX(calc(var(--size) / var(--mode) * -1 + var(--spacing))); } 78 | .tile.up { transform: translateY(calc(var(--size) / var(--mode) * -1 + var(--spacing))); } 79 | .tile.down { transform: translateY(calc(var(--size) / var(--mode) - var(--spacing))); } 80 | .tile.right, .tile.left, .tile.down, .tile.up { pointer-events: none; } 81 | 82 | .x3 .row .col4, .x3 .row .col5, .x3 #row4, .x3 #row5, .x4 .row .col5, .x4 #row5 { display: none !important; } 83 | 84 | .blank { 85 | background-image: none !important; 86 | background-color: transparent !important; 87 | } 88 | 89 | .won { 90 | pointer-events: none; 91 | background-color: rgba(0, 225, 0, 0.85) !important; 92 | } 93 | 94 | #image-select, #settings { 95 | --padding: 10px; 96 | font-size: calc(var(--size) / 35); 97 | display: inline-block; 98 | width: calc((var(--size) / 3 + (var(--padding)) * 2) / 1.25); 99 | height: var(--size); 100 | vertical-align: middle; 101 | background-color: rgba(200, 200, 200, 0.85); 102 | padding: var(--padding); 103 | border-radius: calc(var(--padding) / 2); 104 | position: relative; 105 | } 106 | 107 | #images { 108 | margin: auto; 109 | width: max-content; 110 | } 111 | 112 | #images > button, #images label { 113 | --width: calc((var(--size) / 3.6 - (var(--padding) * 2)) / 1.25); 114 | } 115 | 116 | #images > button, #mobile-images > button { 117 | width: var(--width); 118 | height: var(--width); 119 | cursor: pointer; 120 | display: block; 121 | margin: 10px auto; 122 | border: 5px solid #16aaec; 123 | border-radius: 22.5%; 124 | transition: border-color 0.3s; 125 | } 126 | 127 | #images > button:hover, #mobile-images > button:hover { 128 | border-color: #45cc01; 129 | } 130 | 131 | #images img, #mobile-images img { 132 | width: 100%; 133 | height: 100%; 134 | object-fit: cover; 135 | border-radius: 22.5%; 136 | } 137 | 138 | label { 139 | cursor: pointer; 140 | vertical-align: middle; 141 | user-select: none; 142 | } 143 | 144 | .img-label { 145 | position: absolute; 146 | bottom: 10px; 147 | display: block; 148 | width: var(--width); 149 | text-align: center; 150 | background-color: #16aaec; 151 | color: #fff; 152 | padding: 5px; 153 | border-radius: 5px; 154 | font-weight: 600; 155 | font-size: calc(var(--size) / 40); 156 | transition: background-color 0.3s; 157 | } 158 | 159 | .img-label:hover { 160 | background-color: #45cc01; 161 | } 162 | 163 | #img-input, #mobile-img-input { 164 | display: none; 165 | } 166 | 167 | #preferences { 168 | margin-top: 20px; 169 | } 170 | 171 | #preferences h4, #mode h4 { 172 | margin-bottom: 10px; 173 | } 174 | 175 | input, select, button, textarea { 176 | -webkit-appearance: none; 177 | } 178 | 179 | input[type="checkbox"], input[type="radio"] { 180 | display: inline-block; 181 | position: relative; 182 | width: calc(var(--size) / 35); 183 | height: calc(var(--size) / 35); 184 | font-size: calc(var(--size) / 35); 185 | margin: 0; 186 | margin-right: 3px; 187 | vertical-align: middle; 188 | border-radius: 25%; 189 | background-color: #ddd; 190 | cursor: pointer; 191 | } 192 | 193 | input[type="checkbox"]:checked, input[type="radio"]:checked { 194 | background-color: #16aaec; 195 | } 196 | 197 | input[type="checkbox"]:checked::before, input[type="radio"]:checked::before { 198 | position: absolute; 199 | display: block; 200 | content: "✔"; 201 | color: #fff; 202 | top: 45%; 203 | left: 50%; 204 | transform: translateX(-50%) translateY(-50%); 205 | } 206 | 207 | input[type="radio"]:checked::before { 208 | content: "•"; 209 | font-size: 150%; 210 | top: 36%; 211 | } 212 | 213 | input[type="radio"] { 214 | border-radius: 50%; 215 | } 216 | 217 | #start { 218 | display: block; 219 | width: var(--size); 220 | font-size: calc(var(--size) / 12.5); 221 | padding-bottom: 5px; 222 | background-color: #16aaec; 223 | color: #fff; 224 | border-radius: 30px; 225 | cursor: pointer; 226 | font-weight: 600; 227 | transition: background-color 0.3s; 228 | border: none; 229 | margin: auto; 230 | } 231 | 232 | #start:hover { 233 | background-color: #45cc01; 234 | } 235 | 236 | #scores { 237 | width: var(--size); 238 | margin: auto; 239 | margin-top: 20px; 240 | } 241 | 242 | #current, #best { 243 | width: 50%; 244 | display: inline-block; 245 | padding: 10px; 246 | } 247 | 248 | #current { border-right: 2.5px solid #16aaec; } 249 | #best { border-left: 2.5px solid #16aaec; } 250 | 251 | #current p { 252 | text-align: right; 253 | } 254 | 255 | #best p { 256 | text-align: left; 257 | } 258 | 259 | #mobile-options { display: none; } 260 | 261 | @media screen and (max-width: 1000px) { 262 | main { --size: 60vh; } 263 | .img-label { font-size: calc(var(--size) / 45)} 264 | } 265 | 266 | @media screen and (max-width: 600px) { 267 | h1 { font-size: 30px; } 268 | h5 { font-size: 10px; } 269 | 270 | #mobile-options { display: block; } 271 | #image-select, #settings { display: none; } 272 | 273 | main { 274 | --size: 95vw; 275 | padding-bottom: calc((100vh - var(--size)) / 2 - 75px); 276 | } 277 | 278 | #scores { width: 100%; } 279 | 280 | #board { 281 | display: block; 282 | margin-left: auto; 283 | margin-right: auto; 284 | } 285 | 286 | #mobile-images > button { 287 | width: 22.5vw; 288 | height: 22.5vw; 289 | display: inline-block; 290 | margin-right: 1vw; 291 | } 292 | 293 | #mobile-images { margin: auto; } 294 | 295 | .img-label { 296 | display: block; 297 | position: static; 298 | width: var(--size); 299 | bottom: 0; 300 | } 301 | 302 | #mobile-options { 303 | font-size: 20px; 304 | } 305 | 306 | input[type="checkbox"], input[type="radio"] { 307 | width: 15px; 308 | height: 15px; 309 | } 310 | } 311 | 312 | 313 | 314 | #background { 315 | position: fixed; 316 | height: 100vh; 317 | width: 100vw; 318 | z-index: -100; 319 | overflow: hidden; 320 | } 321 | 322 | .bg-row { 323 | height: 175px; 324 | width: 100vw; 325 | white-space: nowrap; 326 | } 327 | 328 | .bg-tile { 329 | width: 175px; 330 | height: 100%; 331 | display: inline-block; 332 | transition: transform 500ms; 333 | } 334 | 335 | .bg-blank { 336 | background: none !important; 337 | } 338 | 339 | .bg-tile.up { transform: translateY(-175px); } 340 | .bg-tile.down { transform: translateY(175px); } 341 | .bg-tile.left { transform: translateX(-175px); } 342 | .bg-tile.right { transform: translateX(175px); } --------------------------------------------------------------------------------