├── .DS_Store
├── assets
├── .DS_Store
├── images
│ ├── SVG
│ │ ├── icon-o-default.svg
│ │ ├── icon-o-outline.svg
│ │ ├── icon-o-win.svg
│ │ ├── icon-o.svg
│ │ ├── icon-restart.svg
│ │ ├── icon-x-default.svg
│ │ ├── icon-x-outline.svg
│ │ ├── icon-x-win.svg
│ │ ├── icon-x.svg
│ │ ├── logo.svg
│ │ └── test.txt
│ ├── favicon-32x32.png
│ ├── loading.gif
│ └── test.txt
├── scripts
│ ├── .DS_Store
│ └── app.js
└── styles
│ ├── style.css
│ └── test.txt
├── designs
├── accessability-score.PNG
├── css-validation.png
├── desktop-game-multiplayer-player1-win.png
├── desktop-game-multiplayer-player2-win.png
├── desktop-game-multiplayer.png
├── desktop-game-solo-player-loss.png
├── desktop-game-solo-player-win.png
├── desktop-game-solo.png
├── desktop-game-start.png
├── desktop-new-game-menu.png
├── desktop-restart-game.png
├── desktop-round-tied.png
├── html-validation.png
├── js-validation.png
├── main-screenshot.png
├── page-speed-desktop.png
├── page-speed-mobile.png
└── test.txt
├── index.html
├── package-lock.json
├── package.json
└── sass
├── _base.scss
├── _components.scss
├── main.scss
└── test.txt+
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OracleBrain/Tic-Tac-Toe-Game/5e647f0af15f0f2ebb0a33b7b4e5e367c560e960/.DS_Store
--------------------------------------------------------------------------------
/assets/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OracleBrain/Tic-Tac-Toe-Game/5e647f0af15f0f2ebb0a33b7b4e5e367c560e960/assets/.DS_Store
--------------------------------------------------------------------------------
/assets/images/SVG/icon-o-default.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/images/SVG/icon-o-outline.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/images/SVG/icon-o-win.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/images/SVG/icon-o.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/images/SVG/icon-restart.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/images/SVG/icon-x-default.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/images/SVG/icon-x-outline.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/images/SVG/icon-x-win.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/images/SVG/icon-x.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/images/SVG/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/images/SVG/test.txt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/assets/images/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OracleBrain/Tic-Tac-Toe-Game/5e647f0af15f0f2ebb0a33b7b4e5e367c560e960/assets/images/favicon-32x32.png
--------------------------------------------------------------------------------
/assets/images/loading.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OracleBrain/Tic-Tac-Toe-Game/5e647f0af15f0f2ebb0a33b7b4e5e367c560e960/assets/images/loading.gif
--------------------------------------------------------------------------------
/assets/images/test.txt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/assets/scripts/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OracleBrain/Tic-Tac-Toe-Game/5e647f0af15f0f2ebb0a33b7b4e5e367c560e960/assets/scripts/.DS_Store
--------------------------------------------------------------------------------
/assets/scripts/app.js:
--------------------------------------------------------------------------------
1 | const X_CLASS = 'x';
2 | const O_CLASS = 'o';
3 | const WINNING_COMBINATIONS = [
4 | [0, 1, 2],
5 | [3, 4, 5],
6 | [6, 7, 8],
7 | [0, 3, 6],
8 | [1, 4, 7],
9 | [2, 5, 8],
10 | [0, 4, 8],
11 | [2, 4, 6],
12 | ];
13 |
14 | let currentPlayerMark = O_CLASS;
15 | let isVsPlayer = false;
16 | let oTurn = false;
17 |
18 | let xWin = 0;
19 | let oWin = 0;
20 | let tie = 0;
21 |
22 | let winningArry;
23 | let currentClass;
24 |
25 | // CASHING DOM ELEMENTS
26 | const vsCpuBtn = document.getElementById('vs-cpu');
27 | const vsPlayerBtn = document.getElementById('vs-player');
28 | const restartBtn = document.getElementById('restart-btn');
29 |
30 | const gameStartEl = document.getElementById('game-start');
31 | const gamePlayEl = document.getElementById('gameplay');
32 | const gameMarksEl = document.querySelectorAll('#game-start-marks div');
33 | const gameBoardEl = document.getElementById('gameplay-board');
34 |
35 | const modalEl = document.getElementById('modal');
36 | const backdropEl = document.getElementById('backdrop');
37 |
38 | const cells = document.querySelectorAll('.gameplay__card');
39 | const opponentMessage = document.getElementById('opponent-message');
40 |
41 | function setGameModeHandler() {
42 | const btnClickedId = this.id;
43 |
44 | if (btnClickedId === 'vs-player') isVsPlayer = true;
45 |
46 | changeDomLayout(gameStartEl, 'd-block', 'd-none');
47 | changeDomLayout(gamePlayEl, 'd-none', 'd-grid');
48 | startGame();
49 | }
50 |
51 | function changeDomLayout(domEl, display1, display2) {
52 | domEl.classList.remove(display1);
53 | domEl.classList.add(display2);
54 | }
55 |
56 | function startGame() {
57 | setBoardHoverClass();
58 | setScoreBoard();
59 | setTurn();
60 |
61 | if (!isVsPlayer) playVsCpu();
62 | else playVsPlayer();
63 | }
64 |
65 | function setBoardHoverClass() {
66 | if (oTurn) {
67 | gameBoardEl.classList.remove(X_CLASS);
68 | gameBoardEl.classList.add(O_CLASS);
69 | } else {
70 | gameBoardEl.classList.remove(O_CLASS);
71 | gameBoardEl.classList.add(X_CLASS);
72 | }
73 | }
74 |
75 | function setScoreBoard() {
76 | const xWinEl = document.getElementById('x-win');
77 | const tieEl = document.getElementById('tie');
78 | const oWinEl = document.getElementById('o-win');
79 |
80 | xWinEl.innerHTML = `${
81 | isVsPlayer
82 | ? 'X (P1)'
83 | : currentPlayerMark === O_CLASS
84 | ? 'X (CPU)'
85 | : 'X (You)'
86 | } ${xWin}`;
87 | tieEl.innerHTML = `Ties ${tie}`;
88 | oWinEl.innerHTML = `${
89 | isVsPlayer
90 | ? 'O (P2)'
91 | : currentPlayerMark === O_CLASS
92 | ? 'O (You)'
93 | : 'O (CPU)'
94 | } ${oWin}`;
95 | }
96 |
97 | function setTurn() {
98 | const turnEl = document.getElementById('gameplay-turn');
99 |
100 | turnEl.innerHTML = ` Turn`;
107 | }
108 |
109 | function playVsCpu() {
110 | if (currentPlayerMark === O_CLASS) getCpuChoice();
111 | // CPU starts first
112 | else getPlayerChoice(); // Player starts first
113 | }
114 |
115 | function playVsPlayer() {
116 | getPlayerChoice();
117 | }
118 |
119 | function getEmptyCells() {
120 | const cellsArray = Array.from(cells);
121 |
122 | return cellsArray.filter(
123 | cell => !cell.classList.contains('x') && !cell.classList.contains('o')
124 | );
125 | }
126 |
127 | function setCpuBestMove() {
128 | const emptyCells = getEmptyCells();
129 | return emptyCells[Math.floor(Math.random() * emptyCells.length)];
130 | }
131 |
132 | async function getCpuChoice() {
133 | currentClass = oTurn ? O_CLASS : X_CLASS;
134 |
135 | gameBoardEl.classList.remove(O_CLASS);
136 | gameBoardEl.classList.remove(X_CLASS);
137 |
138 | cells.forEach(cell => cell.removeEventListener('click', playHandler));
139 |
140 | changeDomLayout(opponentMessage, 'd-none', 'd-block');
141 |
142 | await new Promise(resolve => {
143 | setTimeout(() => {
144 | placeMark(setCpuBestMove(), currentClass);
145 | changeDomLayout(opponentMessage, 'd-block', 'd-none');
146 | setGameLogic();
147 | resolve('resolved');
148 | }, 2000);
149 | });
150 |
151 | getPlayerChoice();
152 | }
153 |
154 | function getPlayerChoice() {
155 | cells.forEach(cell => {
156 | if (!cell.classList.contains('x') && !cell.classList.contains('o')) {
157 | cell.addEventListener('click', playHandler, { once: true });
158 | }
159 | });
160 | }
161 |
162 | function placeMark(cell, mark) {
163 | cell.classList.add(mark);
164 | }
165 |
166 | function setGameLogic() {
167 | if (checkWin(currentClass)) {
168 | endGame(false);
169 | } else if (isDraw()) {
170 | endGame(true);
171 | } else {
172 | swapTurns();
173 | setBoardHoverClass();
174 | }
175 | }
176 |
177 | function playHandler(event) {
178 | const cell = event.target;
179 | currentClass = oTurn ? O_CLASS : X_CLASS;
180 |
181 | placeMark(cell, currentClass);
182 | setGameLogic();
183 |
184 | if (!isVsPlayer && !checkWin(currentClass) && !isDraw()) getCpuChoice();
185 | }
186 |
187 | function checkWin(currentClass) {
188 | return WINNING_COMBINATIONS.some(combination => {
189 | return combination.every((element, index, array) => {
190 | let condition = cells[element].classList.contains(currentClass);
191 | if (condition) winningArry = array;
192 | return condition;
193 | });
194 | });
195 | }
196 |
197 | function isDraw() {
198 | return [...cells].every(cell => {
199 | return cell.classList.contains(X_CLASS) || cell.classList.contains(O_CLASS);
200 | });
201 | }
202 |
203 | function configureModalButtons() {
204 | const nextRoundBtn = document.getElementById('next-round');
205 | const quitBtn = document.getElementById('quit');
206 |
207 | nextRoundBtn.addEventListener('click', setNextRound);
208 | quitBtn.addEventListener('click', () => {
209 | location.reload();
210 | });
211 | }
212 |
213 | function endGame(draw) {
214 | const tieInnerEl = document.getElementById('tie-inner');
215 |
216 | if (draw) {
217 | tie++;
218 | tieInnerEl.innerText = tie;
219 |
220 | changeDomLayout(backdropEl, 'd-none', 'd-block');
221 | changeDomLayout(modalEl, 'd-none', 'd-block');
222 |
223 | modalEl.innerHTML = `
224 |
Round Tied
225 |
226 |
227 |
228 |
229 |
230 | `;
231 | } else {
232 | setWinner(oTurn);
233 | }
234 |
235 | configureModalButtons();
236 | }
237 |
238 | function swapTurns() {
239 | oTurn = !oTurn;
240 | setTurn();
241 | }
242 |
243 | function setWinner() {
244 | const xWinInnerEl = document.getElementById('x-win-inner');
245 | const oWinInnerEl = document.getElementById('o-win-inner');
246 |
247 | if (oTurn) oWin++;
248 | else xWin++;
249 |
250 | xWinInnerEl.innerText = xWin;
251 | oWinInnerEl.innerText = oWin;
252 |
253 | winningArry.forEach(index => {
254 | cells[index].classList.add('win');
255 | });
256 |
257 | setTimeout(() => {
258 | changeDomLayout(backdropEl, 'd-none', 'd-block');
259 | changeDomLayout(modalEl, 'd-none', 'd-block');
260 | }, 500);
261 |
262 | modalEl.innerHTML = `${
263 | isVsPlayer
264 | ? oTurn
265 | ? 'Player 2 Win'
266 | : 'Player 1 win'
267 | : oTurn && currentPlayerMark === 'o'
268 | ? 'You won'
269 | : !oTurn && currentPlayerMark === 'x'
270 | ? 'You won'
271 | : 'oh No, you lost...'
272 | }
273 |
274 |
279 |
takes the round
282 |
283 |
284 |
285 |
286 |
287 |
`;
288 | }
289 |
290 | function setNextRound() {
291 | oTurn = false;
292 |
293 | changeDomLayout(modalEl, 'd-block', 'd-none');
294 | changeDomLayout(backdropEl, 'd-block', 'd-none');
295 |
296 | cells.forEach(cell => {
297 | cell.classList.remove(X_CLASS);
298 | cell.classList.remove(O_CLASS);
299 | cell.classList.remove('win');
300 | cell.removeEventListener('click', playHandler);
301 | });
302 |
303 | startGame();
304 | }
305 |
306 | function restartHandler() {
307 | modalEl.innerHTML = `Restart Game
308 |
309 |
310 |
313 |
316 |
`;
317 |
318 | const restartBtn = document.getElementById('btn-restart');
319 | const cancelBtn = document.getElementById('btn-cancel');
320 |
321 | changeDomLayout(modalEl, 'd-none', 'd-block');
322 |
323 | restartBtn.addEventListener('click', setNextRound);
324 | cancelBtn.addEventListener('click', () => {
325 | changeDomLayout(modalEl, 'd-block', 'd-none');
326 | });
327 | }
328 |
329 | function getUserChoiceHandler() {
330 | currentPlayerMark = this.id;
331 |
332 | this.classList.add('selected');
333 |
334 | if (this.nextElementSibling)
335 | this.nextElementSibling.classList.remove('selected');
336 | else this.previousElementSibling.classList.remove('selected');
337 | }
338 |
339 | gameMarksEl.forEach(mark => {
340 | mark.addEventListener('click', getUserChoiceHandler);
341 | });
342 |
343 | restartBtn.addEventListener('click', restartHandler);
344 | vsCpuBtn.addEventListener('click', setGameModeHandler);
345 | vsPlayerBtn.addEventListener('click', setGameModeHandler);
346 |
--------------------------------------------------------------------------------
/assets/styles/style.css:
--------------------------------------------------------------------------------
1 | *,
2 | *::before,
3 | *::after {
4 | margin: 0;
5 | padding: 0;
6 | box-sizing: inherit; }
7 |
8 | html {
9 | font-size: 62.5%;
10 | box-sizing: border-box; }
11 | @media only screen and (max-width: 37.5em) {
12 | html {
13 | font-size: 56.25%; } }
14 | @media only screen and (max-width: 18.75em) {
15 | html {
16 | font-size: 35%; } }
17 |
18 | body {
19 | font-family: 'Outfit', sans-serif;
20 | font-size: 1.4rem;
21 | font-weight: 500;
22 | line-height: 1.8rem;
23 | color: #a8bfc9;
24 | background-color: #1a2a33;
25 | min-height: 100vh;
26 | display: flex;
27 | align-items: center;
28 | justify-content: center;
29 | overflow-x: hidden;
30 | position: relative; }
31 |
32 | .container {
33 | max-width: 90%; }
34 |
35 | .heading-lg {
36 | font-weight: 700;
37 | text-transform: uppercase;
38 | font-size: 4rem;
39 | line-height: 5rem; }
40 | .heading-lg--blue {
41 | color: #31c3bd; }
42 | .heading-lg--yellow {
43 | color: #f2b137; }
44 | @media only screen and (max-width: 37.5em) {
45 | .heading-lg {
46 | font-size: 2.4rem; } }
47 |
48 | .heading-xs {
49 | font-weight: 700;
50 | text-transform: uppercase;
51 | font-size: 1.6rem;
52 | line-height: 2rem; }
53 |
54 | .d-none {
55 | display: none !important; }
56 |
57 | .d-grid {
58 | display: grid !important; }
59 |
60 | .d-block {
61 | display: block !important; }
62 |
63 | @keyframes fadeIn {
64 | 0% {
65 | opacity: 0; }
66 | 100% {
67 | opacity: 1; } }
68 |
69 | .logo__icon {
70 | width: 7.2rem;
71 | height: 3.2rem; }
72 |
73 | .game-start {
74 | text-align: center;
75 | max-width: 90%;
76 | animation: fadeIn 1s ease-in-out; }
77 | .game-start__choices {
78 | margin: 4rem 0;
79 | background-color: #1f3641;
80 | padding: 2.4rem;
81 | border-radius: 1.5rem;
82 | box-shadow: inset 0 -0.8rem 0 #111c22; }
83 | .game-start__marks {
84 | margin: 2.4rem 0 1.7rem 0;
85 | padding: 0.9rem 0.8rem;
86 | background-color: #1a2a33;
87 | border-radius: 1rem;
88 | display: flex;
89 | height: 7rem;
90 | align-items: center;
91 | justify-content: space-between; }
92 | .game-start__x-mark, .game-start__o-mark {
93 | position: relative;
94 | width: 100%;
95 | height: 100%;
96 | border-radius: inherit;
97 | transition: all 0.4s ease;
98 | display: flex;
99 | align-items: center;
100 | justify-content: center;
101 | flex-basis: 50%; }
102 | .game-start__x-mark:hover, .game-start__o-mark:hover {
103 | background-color: #1d303a;
104 | cursor: pointer; }
105 | .game-start__x-mark.selected, .game-start__o-mark.selected {
106 | background-color: #a8bfc9; }
107 | .game-start__x-mark.selected > svg, .game-start__o-mark.selected > svg {
108 | fill: #1a2a33; }
109 | .game-start__x-icon, .game-start__o-icon {
110 | width: 3.2rem;
111 | height: 3.2rem;
112 | fill: #a8bfc9; }
113 | .game-start__footer {
114 | text-transform: uppercase;
115 | opacity: 0.5; }
116 |
117 | .btn {
118 | display: block;
119 | width: 100%;
120 | font-size: 2rem;
121 | font-weight: 700;
122 | color: #1a2a33;
123 | text-transform: uppercase;
124 | line-height: 2.5rem;
125 | text-align: center;
126 | padding: 2.5rem 12rem;
127 | border: none;
128 | border-radius: 1.5rem;
129 | transition: all 0.4s ease; }
130 | .btn:hover {
131 | cursor: pointer; }
132 | .btn--small {
133 | width: auto;
134 | display: inline-block;
135 | padding: 1.5rem 1.8rem; }
136 | .btn--small:last-child {
137 | margin-left: 1rem; }
138 | .btn:not(:last-child) {
139 | margin-bottom: 1.5rem; }
140 | .btn--yellow {
141 | background-color: #f2b137;
142 | box-shadow: inset 0 -0.8rem 0 #b77c0c; }
143 | .btn--yellow-small {
144 | background-color: #f2b137;
145 | box-shadow: inset 0 -0.4rem 0 #b77c0c; }
146 | .btn--yellow-small:hover {
147 | background-color: #ffc860; }
148 | .btn--yellow:hover {
149 | background-color: #ffc860; }
150 | .btn--blue {
151 | background-color: #31c3bd;
152 | box-shadow: inset 0 -0.8rem 0 #1d716e; }
153 | .btn--blue:hover {
154 | background-color: #65e9e4; }
155 | .btn--silver {
156 | background-color: #a8bfc9;
157 | box-shadow: inset 0 -0.8rem 0 #6991a2; }
158 | .btn--silver-small {
159 | background-color: #a8bfc9;
160 | box-shadow: inset 0 -0.4rem 0 #6991a2; }
161 | .btn--silver-small:hover {
162 | background-color: #dbe8ed; }
163 | .btn--silver:hover {
164 | background-color: #dbe8ed; }
165 |
166 | .gameplay {
167 | display: grid;
168 | margin: 5rem 0;
169 | grid-template-columns: repeat(3, 1fr);
170 | grid-gap: 2rem;
171 | align-items: center;
172 | text-align: center;
173 | animation: fadeIn 0.4s ease-in-out;
174 | display: none; }
175 | .gameplay__turn {
176 | width: 100%;
177 | background-color: #1f3641;
178 | padding: 2rem 3rem;
179 | border-radius: 1rem;
180 | box-shadow: inset 0 -0.4rem 0 #0f191e;
181 | display: flex;
182 | align-items: center; }
183 | @media only screen and (max-width: 37.5em) {
184 | .gameplay__turn {
185 | font-size: 1.4rem;
186 | padding: 2rem 1.5rem; } }
187 | .gameplay__turn-icon {
188 | width: 2rem;
189 | height: 2rem;
190 | fill: #a8bfc9; }
191 | .gameplay__restart {
192 | width: 50%;
193 | background-color: #a8bfc9;
194 | padding: 1.6rem;
195 | border-radius: 1rem;
196 | box-shadow: inset 0 -0.4rem 0 #6991a2;
197 | transition: all 0.4s ease;
198 | justify-self: flex-end; }
199 | .gameplay__restart:hover {
200 | cursor: pointer;
201 | background-color: #dbe8ed; }
202 | .gameplay__restart-icon {
203 | width: 2rem;
204 | height: 2rem; }
205 | .gameplay__board {
206 | grid-column: 1 / -1;
207 | display: grid;
208 | grid-template-columns: repeat(3, 14rem);
209 | grid-template-rows: repeat(3, 14rem);
210 | grid-gap: 2rem; }
211 | @media only screen and (max-width: 37.5em) {
212 | .gameplay__board {
213 | grid-template-columns: repeat(3, minmax(9.6rem, 1fr));
214 | grid-template-rows: repeat(3, minmax(9.6rem, 1fr)); } }
215 | .gameplay__card {
216 | padding: 3.8rem;
217 | background-color: #1f3641;
218 | box-shadow: inset 0 -0.8rem 0 #0f191e;
219 | border-radius: 1.5rem; }
220 | @media only screen and (max-width: 37.5em) {
221 | .gameplay__card {
222 | padding: 2.8rem; } }
223 | .gameplay__card:hover {
224 | cursor: pointer; }
225 | .gameplay__card.x, .gameplay__card.o {
226 | cursor: not-allowed;
227 | box-shadow: inset 0 -0.5rem 0 #0f191e; }
228 | .gameplay__card.x::before {
229 | content: url("../images/SVG/icon-x.svg"); }
230 | .gameplay__card.x.win {
231 | background-color: #31c3bd; }
232 | .gameplay__card.x.win::before {
233 | content: url("../images/SVG/icon-x-win.svg"); }
234 | .gameplay__card.o.win {
235 | background-color: #f2b137; }
236 | .gameplay__card.o.win::before {
237 | content: url("../images/SVG/icon-o-win.svg"); }
238 | .gameplay__card.o::before {
239 | content: url("../images/SVG/icon-o.svg"); }
240 | .gameplay__board.x .gameplay__card:not(.x):not(.o):hover::before {
241 | content: url("../images/SVG/icon-x-outline.svg"); }
242 | .gameplay__board.o .gameplay__card:not(.x):not(.o):hover::before {
243 | content: url("../images/SVG/icon-o-outline.svg"); }
244 | .gameplay__win, .gameplay__tie, .gameplay__loss {
245 | color: #1a2a33;
246 | text-transform: uppercase;
247 | padding: 1.3rem 4rem;
248 | border-radius: 1.5rem; }
249 | @media only screen and (max-width: 37.5em) {
250 | .gameplay__win, .gameplay__tie, .gameplay__loss {
251 | padding: 1rem 2.3rem; } }
252 | .gameplay__win {
253 | background-color: #31c3bd; }
254 | .gameplay__tie {
255 | background-color: #a8bfc9; }
256 | .gameplay__loss {
257 | background-color: #f2b137; }
258 | .gameplay__highlight {
259 | display: block;
260 | font-size: 2.4rem;
261 | font-weight: 700;
262 | line-height: 3rem; }
263 | @media only screen and (max-width: 37.5em) {
264 | .gameplay__highlight {
265 | font-size: 2rem;
266 | line-height: 2.5rem; } }
267 | .gameplay__opponent-message {
268 | grid-column: span 3; }
269 | .gameplay__opponent-message > p {
270 | display: inline; }
271 |
272 | .backdrop {
273 | position: absolute;
274 | top: 0;
275 | left: 0;
276 | width: 100%;
277 | height: 100%;
278 | background-color: rgba(0, 0, 0, 0.5);
279 | display: none; }
280 |
281 | .modal {
282 | position: absolute;
283 | top: 50%;
284 | left: 0;
285 | width: 100%;
286 | padding: 6.7rem 0;
287 | background-color: #1f3641;
288 | text-align: center;
289 | transform: translateY(-50%);
290 | animation: fadeIn 0.4s ease-in-out;
291 | display: none; }
292 | .modal__result {
293 | display: flex;
294 | align-items: center;
295 | justify-content: center;
296 | margin-top: 3rem; }
297 | @media only screen and (max-width: 37.5em) {
298 | .modal__result {
299 | margin-top: 1.5rem; } }
300 | .modal__result > *:last-child {
301 | margin-left: 2.5rem; }
302 | @media only screen and (max-width: 37.5em) {
303 | .modal__result > *:last-child {
304 | margin-left: 1.5rem; } }
305 | .modal__icon {
306 | width: 6.4rem;
307 | height: 6.4rem; }
308 | @media only screen and (max-width: 37.5em) {
309 | .modal__icon {
310 | width: 2.8rem;
311 | height: 2.8rem; } }
312 | .modal__buttons {
313 | margin-top: 3rem; }
314 |
--------------------------------------------------------------------------------
/assets/styles/test.txt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/designs/accessability-score.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OracleBrain/Tic-Tac-Toe-Game/5e647f0af15f0f2ebb0a33b7b4e5e367c560e960/designs/accessability-score.PNG
--------------------------------------------------------------------------------
/designs/css-validation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OracleBrain/Tic-Tac-Toe-Game/5e647f0af15f0f2ebb0a33b7b4e5e367c560e960/designs/css-validation.png
--------------------------------------------------------------------------------
/designs/desktop-game-multiplayer-player1-win.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OracleBrain/Tic-Tac-Toe-Game/5e647f0af15f0f2ebb0a33b7b4e5e367c560e960/designs/desktop-game-multiplayer-player1-win.png
--------------------------------------------------------------------------------
/designs/desktop-game-multiplayer-player2-win.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OracleBrain/Tic-Tac-Toe-Game/5e647f0af15f0f2ebb0a33b7b4e5e367c560e960/designs/desktop-game-multiplayer-player2-win.png
--------------------------------------------------------------------------------
/designs/desktop-game-multiplayer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OracleBrain/Tic-Tac-Toe-Game/5e647f0af15f0f2ebb0a33b7b4e5e367c560e960/designs/desktop-game-multiplayer.png
--------------------------------------------------------------------------------
/designs/desktop-game-solo-player-loss.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OracleBrain/Tic-Tac-Toe-Game/5e647f0af15f0f2ebb0a33b7b4e5e367c560e960/designs/desktop-game-solo-player-loss.png
--------------------------------------------------------------------------------
/designs/desktop-game-solo-player-win.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OracleBrain/Tic-Tac-Toe-Game/5e647f0af15f0f2ebb0a33b7b4e5e367c560e960/designs/desktop-game-solo-player-win.png
--------------------------------------------------------------------------------
/designs/desktop-game-solo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OracleBrain/Tic-Tac-Toe-Game/5e647f0af15f0f2ebb0a33b7b4e5e367c560e960/designs/desktop-game-solo.png
--------------------------------------------------------------------------------
/designs/desktop-game-start.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OracleBrain/Tic-Tac-Toe-Game/5e647f0af15f0f2ebb0a33b7b4e5e367c560e960/designs/desktop-game-start.png
--------------------------------------------------------------------------------
/designs/desktop-new-game-menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OracleBrain/Tic-Tac-Toe-Game/5e647f0af15f0f2ebb0a33b7b4e5e367c560e960/designs/desktop-new-game-menu.png
--------------------------------------------------------------------------------
/designs/desktop-restart-game.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OracleBrain/Tic-Tac-Toe-Game/5e647f0af15f0f2ebb0a33b7b4e5e367c560e960/designs/desktop-restart-game.png
--------------------------------------------------------------------------------
/designs/desktop-round-tied.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OracleBrain/Tic-Tac-Toe-Game/5e647f0af15f0f2ebb0a33b7b4e5e367c560e960/designs/desktop-round-tied.png
--------------------------------------------------------------------------------
/designs/html-validation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OracleBrain/Tic-Tac-Toe-Game/5e647f0af15f0f2ebb0a33b7b4e5e367c560e960/designs/html-validation.png
--------------------------------------------------------------------------------
/designs/js-validation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OracleBrain/Tic-Tac-Toe-Game/5e647f0af15f0f2ebb0a33b7b4e5e367c560e960/designs/js-validation.png
--------------------------------------------------------------------------------
/designs/main-screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OracleBrain/Tic-Tac-Toe-Game/5e647f0af15f0f2ebb0a33b7b4e5e367c560e960/designs/main-screenshot.png
--------------------------------------------------------------------------------
/designs/page-speed-desktop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OracleBrain/Tic-Tac-Toe-Game/5e647f0af15f0f2ebb0a33b7b4e5e367c560e960/designs/page-speed-desktop.png
--------------------------------------------------------------------------------
/designs/page-speed-mobile.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/OracleBrain/Tic-Tac-Toe-Game/5e647f0af15f0f2ebb0a33b7b4e5e367c560e960/designs/page-speed-mobile.png
--------------------------------------------------------------------------------
/designs/test.txt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
14 |
15 |
16 |
20 |
21 |
22 |
23 |
24 | Tic Tac Toe
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
41 |
42 |
43 |
46 |
47 |
48 |
53 |
54 |
55 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
80 |
81 |
82 |
83 |
84 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
Your opponent is thinking
106 |

107 |
108 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tic-tac-toe-new",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1
5 | }
6 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tic-tac-toe-new",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "watch:sass": "node-sass sass/main.scss assets/styles/style.css -w"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "devDependencies": {}
13 | }
14 |
--------------------------------------------------------------------------------
/sass/_base.scss:
--------------------------------------------------------------------------------
1 | // COLOR VARIABLES
2 | $dark-navy: #1a2a33;
3 | $semi-dark-navy: #1f3641;
4 | $silver: #a8bfc9;
5 | $silver-hover: #dbe8ed;
6 | $light-blue: #31c3bd;
7 | $light-blue-hover: #65e9e4;
8 | $light-yellow: #f2b137;
9 | $light-yellow-hover: #ffc860;
10 |
11 | // MIXINS
12 | @mixin headingsCommon {
13 | font-weight: 700;
14 | text-transform: uppercase;
15 | }
16 |
17 | // MEDIA QUERY MANAGER
18 | @mixin respond($breakpoint) {
19 | @if $breakpoint == small {
20 | @media only screen and (max-width: 37.5em) {
21 | @content;
22 | }
23 | }
24 |
25 | @if $breakpoint == ex-small {
26 | @media only screen and (max-width: 18.75em) {
27 | @content;
28 | }
29 | }
30 | }
31 |
32 | // RESET
33 | *,
34 | *::before,
35 | *::after {
36 | margin: 0;
37 | padding: 0;
38 | box-sizing: inherit;
39 | }
40 |
41 | // MAIN STYLE
42 | html {
43 | font-size: 62.5%;
44 | box-sizing: border-box;
45 |
46 | @include respond(small) {
47 | font-size: 56.25%;
48 | }
49 |
50 | @include respond(ex-small) {
51 | font-size: 35%;
52 | }
53 | }
54 |
55 | body {
56 | font-family: 'Outfit', sans-serif;
57 | font-size: 1.4rem;
58 | font-weight: 500;
59 | line-height: 1.8rem;
60 | color: $silver;
61 | background-color: $dark-navy;
62 | min-height: 100vh;
63 | display: flex;
64 | align-items: center;
65 | justify-content: center;
66 | overflow-x: hidden;
67 | position: relative;
68 | }
69 |
70 | // UTILITIES
71 | .container {
72 | max-width: 90%;
73 | }
74 |
75 | .heading-lg {
76 | @include headingsCommon;
77 | font-size: 4rem;
78 | line-height: 5rem;
79 |
80 | &--blue {
81 | color: $light-blue;
82 | }
83 |
84 | &--yellow {
85 | color: $light-yellow;
86 | }
87 |
88 | @include respond(small) {
89 | font-size: 2.4rem;
90 | }
91 | }
92 |
93 | .heading-xs {
94 | @include headingsCommon;
95 | font-size: 1.6rem;
96 | line-height: 2rem;
97 | }
98 |
99 | // .text-yellow {
100 | // color: $light-yellow;
101 | // }
102 |
103 | // .text-blue {
104 | // color: $light-blue;
105 | // }
106 |
107 | .d-none {
108 | display: none !important;
109 | }
110 |
111 | .d-grid {
112 | display: grid !important;
113 | }
114 |
115 | .d-block {
116 | display: block !important;
117 | }
118 |
119 | // ANIMATION
120 | @keyframes fadeIn {
121 | 0% {
122 | opacity: 0;
123 | }
124 |
125 | 100% {
126 | opacity: 1;
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/sass/_components.scss:
--------------------------------------------------------------------------------
1 | // Logo
2 | .logo {
3 | &__icon {
4 | width: 7.2rem;
5 | height: 3.2rem;
6 | }
7 | }
8 |
9 | // GAME START SECTION
10 | .game-start {
11 | text-align: center;
12 | // min-width: 40.6rem;
13 | max-width: 90%;
14 | animation: fadeIn 1s ease-in-out;
15 |
16 | &__choices {
17 | margin: 4rem 0;
18 | background-color: $semi-dark-navy;
19 | padding: 2.4rem;
20 | border-radius: 1.5rem;
21 | box-shadow: inset 0 -0.8rem 0 darken($color: $dark-navy, $amount: 5);
22 | }
23 |
24 | &__marks {
25 | margin: 2.4rem 0 1.7rem 0;
26 | padding: 0.9rem 0.8rem;
27 | background-color: $dark-navy;
28 | border-radius: 1rem;
29 | display: flex;
30 | height: 7rem;
31 | align-items: center;
32 | justify-content: space-between;
33 | }
34 |
35 | &__x-mark,
36 | &__o-mark {
37 | position: relative;
38 | width: 100%;
39 | height: 100%;
40 | border-radius: inherit;
41 | transition: all 0.4s ease;
42 | display: flex;
43 | align-items: center;
44 | justify-content: center;
45 | flex-basis: 50%;
46 |
47 | &:hover {
48 | background-color: lighten($color: $dark-navy, $amount: 2);
49 | cursor: pointer;
50 | }
51 |
52 | &.selected {
53 | background-color: $silver;
54 |
55 | & > svg {
56 | fill: $dark-navy;
57 | }
58 | }
59 | }
60 |
61 | &__x-icon,
62 | &__o-icon {
63 | width: 3.2rem;
64 | height: 3.2rem;
65 | fill: $silver;
66 | }
67 |
68 | &__footer {
69 | text-transform: uppercase;
70 | opacity: 0.5;
71 | }
72 | }
73 |
74 | // BUTTONS - BUTTON STYLE
75 | .btn {
76 | display: block;
77 | width: 100%;
78 | font-size: 2rem;
79 | font-weight: 700;
80 | color: $dark-navy;
81 | text-transform: uppercase;
82 | line-height: 2.5rem;
83 | text-align: center;
84 | padding: 2.5rem 12rem;
85 | border: none;
86 | border-radius: 1.5rem;
87 | transition: all 0.4s ease;
88 |
89 | @include respond(small) {
90 | font-size: 1.6rem;
91 | }
92 |
93 | &:hover {
94 | cursor: pointer;
95 | }
96 |
97 | &--small {
98 | width: auto;
99 | display: inline-block;
100 | padding: 1.5rem 1.8rem;
101 |
102 | &:last-child {
103 | margin-left: 1rem;
104 | }
105 | }
106 |
107 | &:not(:last-child) {
108 | margin-bottom: 1.5rem;
109 | }
110 |
111 | &--yellow {
112 | background-color: $light-yellow;
113 | box-shadow: inset 0 -0.8rem 0 darken($color: $light-yellow, $amount: 20);
114 |
115 | &-small {
116 | background-color: $light-yellow;
117 | box-shadow: inset 0 -0.4rem 0 darken($color: $light-yellow, $amount: 20);
118 |
119 | &:hover {
120 | background-color: $light-yellow-hover;
121 | }
122 | }
123 |
124 | &:hover {
125 | background-color: $light-yellow-hover;
126 | }
127 | }
128 |
129 | &--blue {
130 | background-color: $light-blue;
131 | box-shadow: inset 0 -0.8rem 0 darken($color: $light-blue, $amount: 20);
132 |
133 | &:hover {
134 | background-color: $light-blue-hover;
135 | }
136 | }
137 |
138 | &--silver {
139 | background-color: $silver;
140 | box-shadow: inset 0 -0.8rem 0 darken($color: $silver, $amount: 20);
141 |
142 | &-small {
143 | background-color: $silver;
144 | box-shadow: inset 0 -0.4rem 0 darken($color: $silver, $amount: 20);
145 |
146 | &:hover {
147 | background-color: $silver-hover;
148 | }
149 | }
150 |
151 | &:hover {
152 | background-color: $silver-hover;
153 | }
154 | }
155 | }
156 |
157 | // GAMEPLAY SECTION
158 | .gameplay {
159 | display: grid;
160 | margin: 5rem 0;
161 | grid-template-columns: repeat(3, 1fr);
162 | grid-gap: 2rem;
163 | align-items: center;
164 | text-align: center;
165 | animation: fadeIn 0.4s ease-in-out;
166 | display: none;
167 |
168 | &__turn {
169 | width: 100%;
170 | background-color: $semi-dark-navy;
171 | padding: 2rem 3rem;
172 | border-radius: 1rem;
173 | box-shadow: inset 0 -0.4rem 0 darken($color: $semi-dark-navy, $amount: 10);
174 | display: flex;
175 | align-items: center;
176 |
177 | @include respond(small) {
178 | font-size: 1.4rem;
179 | padding: 2rem 1.5rem;
180 | }
181 |
182 | &-icon {
183 | width: 2rem;
184 | height: 2rem;
185 | fill: $silver;
186 | }
187 | }
188 |
189 | &__restart {
190 | width: 50%;
191 | background-color: $silver;
192 | padding: 1.6rem;
193 | border-radius: 1rem;
194 | box-shadow: inset 0 -0.4rem 0 darken($color: $silver, $amount: 20);
195 | transition: all 0.4s ease;
196 | justify-self: flex-end;
197 |
198 | &:hover {
199 | cursor: pointer;
200 | background-color: $silver-hover;
201 | }
202 |
203 | &-icon {
204 | width: 2rem;
205 | height: 2rem;
206 | }
207 | }
208 |
209 | &__board {
210 | grid-column: 1 / -1;
211 | display: grid;
212 | grid-template-columns: repeat(3, 14rem);
213 | grid-template-rows: repeat(3, 14rem);
214 | grid-gap: 2rem;
215 |
216 | @include respond(small) {
217 | grid-template-columns: repeat(3, minmax(9.6rem, 1fr));
218 | grid-template-rows: repeat(3, minmax(9.6rem, 1fr));
219 | }
220 | }
221 |
222 | &__card {
223 | padding: 3.8rem;
224 | background-color: $semi-dark-navy;
225 | box-shadow: inset 0 -0.8rem 0 darken($color: $semi-dark-navy, $amount: 10);
226 | border-radius: 1.5rem;
227 |
228 | @include respond(small) {
229 | padding: 2.8rem;
230 | }
231 |
232 | &:hover {
233 | cursor: pointer;
234 | }
235 |
236 | &.x,
237 | &.o {
238 | cursor: not-allowed;
239 | box-shadow: inset 0 -0.5rem 0 darken($color: $semi-dark-navy, $amount: 10);
240 | }
241 |
242 | &.x::before {
243 | content: url('../images/SVG/icon-x.svg');
244 | }
245 |
246 | &.x.win {
247 | background-color: $light-blue;
248 |
249 | &::before {
250 | content: url('../images/SVG/icon-x-win.svg');
251 | }
252 | }
253 |
254 | &.o.win {
255 | background-color: $light-yellow;
256 |
257 | &::before {
258 | content: url('../images/SVG/icon-o-win.svg');
259 | }
260 | }
261 |
262 | &.o::before {
263 | content: url('../images/SVG/icon-o.svg');
264 | }
265 | }
266 |
267 | &__board.x &__card:not(.x):not(.o):hover::before {
268 | content: url('../images/SVG/icon-x-outline.svg');
269 | }
270 |
271 | &__board.o &__card:not(.x):not(.o):hover::before {
272 | content: url('../images/SVG/icon-o-outline.svg');
273 | }
274 |
275 | &__win,
276 | &__tie,
277 | &__loss {
278 | color: $dark-navy;
279 | text-transform: uppercase;
280 | padding: 1.3rem 4rem;
281 | border-radius: 1.5rem;
282 |
283 | @include respond(small) {
284 | padding: 1rem 2.3rem;
285 | }
286 | }
287 |
288 | &__win {
289 | background-color: $light-blue;
290 | }
291 |
292 | &__tie {
293 | background-color: $silver;
294 | }
295 |
296 | &__loss {
297 | background-color: $light-yellow;
298 | }
299 |
300 | &__highlight {
301 | display: block;
302 | font-size: 2.4rem;
303 | font-weight: 700;
304 | line-height: 3rem;
305 |
306 | @include respond(small) {
307 | font-size: 2rem;
308 | line-height: 2.5rem;
309 | }
310 | }
311 |
312 | &__opponent-message {
313 | grid-column: span 3;
314 |
315 | & > p {
316 | display: inline;
317 | }
318 | }
319 | }
320 |
321 | // BACKDROP & MODEL
322 | .backdrop {
323 | position: absolute;
324 | top: 0;
325 | left: 0;
326 | width: 100%;
327 | height: 100%;
328 | background-color: rgba(#000, 0.5);
329 | display: none;
330 | }
331 |
332 | .modal {
333 | position: absolute;
334 | top: 50%;
335 | left: 0;
336 | width: 100%;
337 | padding: 6.7rem 0;
338 | background-color: $semi-dark-navy;
339 | text-align: center;
340 | transform: translateY(-50%);
341 | animation: fadeIn 0.4s ease-in-out;
342 | display: none;
343 |
344 | &__result {
345 | display: flex;
346 | align-items: center;
347 | justify-content: center;
348 | margin-top: 3rem;
349 |
350 | @include respond(small) {
351 | margin-top: 1.5rem;
352 | }
353 |
354 | & > *:last-child {
355 | margin-left: 2.5rem;
356 |
357 | @include respond(small) {
358 | margin-left: 1.5rem;
359 | }
360 | }
361 | }
362 |
363 | &__icon {
364 | width: 6.4rem;
365 | height: 6.4rem;
366 |
367 | @include respond(small) {
368 | width: 2.8rem;
369 | height: 2.8rem;
370 | }
371 | }
372 |
373 | &__buttons {
374 | margin-top: 3rem;
375 | }
376 | }
377 |
--------------------------------------------------------------------------------
/sass/main.scss:
--------------------------------------------------------------------------------
1 | @import 'base';
2 | @import 'components';
3 |
--------------------------------------------------------------------------------
/sass/test.txt+:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------