A simple Tic Tac Toe game built with SvelteKit, TypeScript, and Bun. This guide is written for junior high students who are new to Svelte!
š NEW TO THIS PROJECT? Check out DOCS_INDEX.md for a guide to all documentation!
ā” IN A HURRY? See QUICKSTART.md for a 5-minute setup guide!
Before starting, make sure you have Bun installed on your computer:
# Check if Bun is installed
bun --version
# If not installed, visit: https://bun.sh
bun install
bun run dev
http://localhost:5173š You should see your Tic Tac Toe game!
SvelteKit is a framework for building web applications. Think of it like a toolbox that makes creating websites easier!
svelte-easy-tictactoe/
āāā src/
ā āāā routes/
ā ā āāā +page.svelte ā Our game is here!
ā ā āāā +layout.svelte ā Page wrapper/layout
ā āāā app.html ā HTML template
āāā package.json ā Project info & dependencies
āāā README.md ā You are here!
Key Concept: In SvelteKit, files in src/routes/ become pages on your website!
+page.svelte = The homepage (/)about/+page.svelte = The about page (/about)Let's break down src/routes/+page.svelte section by section:
<script> Tag - The Brain š§ <script lang="ts">
// This is where our game logic lives!
</script>
What does lang="ts" mean?
ts = TypeScriptlet board: string[] = $state(['', '', '', '', '', '', '', '', '']);
let currentPlayer: 'X' | 'O' = $state('X');
let winner: string | null = $state(null);
let isDraw: boolean = $state(false);
Breaking it down:
board - An array with 9 empty strings (one for each cell)
string[] means "an array of strings"currentPlayer - Who's turn it is: 'X' or 'O'
'X' | 'O' means "only X or O, nothing else!"winner - Who won? (null = nobody yet)
string | null means "either a string or null"isDraw - Is the game a tie?
boolean means true or falseWhat is $state()?
$state() makes variables reactiveconst winPatterns = [
[0, 1, 2], [3, 4, 5], [6, 7, 8], // rows
[0, 3, 6], [1, 4, 7], [2, 5, 8], // columns
[0, 4, 8], [2, 4, 6] // diagonals
];
What is this?
[0, 1, 2] = top row (positions 0, 1, 2)[0, 4, 8] = diagonal from top-left to bottom-rightBoard positions:
0 | 1 | 2
---------
3 | 4 | 5
---------
6 | 7 | 8
handleClick Functionfunction handleClick(index: number) {
// Don't allow click if game is over or cell is already filled
if (winner || isDraw || board[index] !== '') return;
// Place the current player's mark
board[index] = currentPlayer;
// Check if current player won
if (checkWinner()) {
winner = currentPlayer;
return;
}
// Check if board is full (draw)
if (board.every(cell => cell !== '')) {
isDraw = true;
return;
}
// Switch to the other player
currentPlayer = currentPlayer === 'X' ? 'O' : 'X';
}
Step-by-step explanation:
Check if move is valid:
Place the mark:
Check for winner:
Check for draw:
board.every(...) checks if every cell is filledSwitch players:
condition ? valueIfTrue : valueIfFalse is called a "ternary operator"checkWinner Functionfunction checkWinner(): boolean {
return winPatterns.some(pattern => {
const [a, b, c] = pattern;
return board[a] !== '' &&
board[a] === board[b] &&
board[a] === board[c];
});
}
What does this do?
.some() checks if ANY pattern is a win[a, b, c] = pattern breaks pattern into 3 positionsa is not emptya equals position ba equals position ctrue if a winning pattern is foundExample: If positions 0, 1, 2 all have 'X', we have a winner!
resetGame Functionfunction resetGame() {
board = ['', '', '', '', '', '', '', '', ''];
currentPlayer = 'X';
winner = null;
isDraw = false;
}
Simple! Just reset everything back to starting values.
<div class="container">
<h1>Tic Tac Toe</h1>
<div class="status">
{#if winner}
<p class="winner">š Player {winner} wins!</p>
{:else if isDraw}
<p class="draw">It's a draw!</p>
{:else}
<p>Current player: <span class="player-{currentPlayer}">{currentPlayer}</span></p>
{/if}
</div>
<div class="board">
{#each board as cell, index}
<button
class="cell"
class:x={cell === 'X'}
class:o={cell === 'O'}
onclick={() => handleClick(index)}
>
{cell}
</button>
{/each}
</div>
<button class="reset-btn" onclick={resetGame}>Reset Game</button>
</div>
Svelte Special Syntax:
{#if}...{:else if}...{:else}...{/if}
{#each board as cell, index}
cell = the value ('X', 'O', or '')index = the position (0-8){cell}
{} mean "run JavaScript here"class:x={cell === 'X'}
onclick={() => handleClick(index)}
handleClick with the index() => is an arrow function (a way to write functions)<style>
.container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.board {
display: grid;
grid-template-columns: repeat(3, 120px);
grid-template-rows: repeat(3, 120px);
gap: 10px;
}
.cell {
width: 120px;
height: 120px;
font-size: 3rem;
/* ... more styles ... */
}
</style>
CSS Concepts Explained:
Flexbox (display: flex)
flex-direction: column = stack items verticallyalign-items: center = center items horizontallyjustify-content: center = center items verticallyCSS Grid (display: grid)
grid-template-columns: repeat(3, 120px) = 3 columns, each 120px widegap: 10px = 10px space between cellsViewport Height (100vh)
vh = viewport height100vh = 100% of the screen heightPseudo-classes (:hover, :active)
:hover = when mouse is over the element:active = when element is being clickedAnimations
@keyframes bounce {
from { transform: translateY(0); }
to { transform: translateY(-10px); }
}
translateY moves element up/downNow that you understand the code, try these challenges:
Change the colors
#667eea) and change themChange the cell size
120px and make cells bigger or smallerAdd your name to the title
<h1>Tic Tac Toe</h1> to include your nameAdd a score counter
xWins and oWinsAdd sound effects
Change X and O to emojis
Add an AI opponent
Add a timer
Make it online multiplayer
$state() do?A: In Svelte 5, $state() creates reactive state. When you change it, Svelte automatically updates the page. It's like magic! āØ
A: TypeScript adds "types" to JavaScript:
let age: number = 15; // ā
OK
let age: number = "fifteen"; // ā Error!
It helps catch mistakes before your code runs!
let and const?A:
let = can be changed laterconst = cannot be changed (constant)let score = 0;
score = 10; // ā
OK
const pi = 3.14;
pi = 3.15; // ā Error!
onclick={() => handleClick(index)} mean?A: It's an arrow function that gets called when you click:
onclick = when clicked() => = arrow function syntaxhandleClick(index) = call this function with indexWithout the arrow function, handleClick would run immediately!
A: Svelte compiles your code and tracks which variables affect the display. When you change a $state() variable, Svelte knows exactly what to update. No need for virtual DOM like React!
Svelte Tutorial - learn.svelte.dev
SvelteKit Docs - kit.svelte.dev
TypeScript Handbook - typescriptlang.org/docs
Once you're comfortable with this game, try building:
# Try deleting node_modules and reinstalling
rm -rf node_modules
bun install
bun run dev
# Kill the process using port 5173
lsof -ti:5173 | xargs kill -9
# Then start again
bun run dev
Make sure you're using the latest version:
bun update
You've just learned:
$state()Keep coding and have fun! š
This project is open source and free to use for learning purposes.
Found a bug or want to improve the tutorial? Feel free to contribute!
Made with ā¤ļø for learning
Happy coding! š®āØ