feat: scaffold Svelte + Tailwind + Vite project (Story 1.1)

Initialize project with Svelte 5, Vite 8, Tailwind CSS v4, and Vitest.
Set up folder structure (src/lib, src/components, public/fonts),
constants module with game values and 12-tier tile colors, Clear Sans
font, and page background styling.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-13 21:35:26 +07:00
co-authored by Claude Opus 4.6
parent 877c0ad599
commit 685ccf8e90
17 changed files with 2096 additions and 60 deletions
+2
View File
@@ -137,3 +137,5 @@ dist
# Vite logs files
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
node_modules
dist
@@ -1,6 +1,6 @@
# Story 1.1: Project Scaffold & Dev Environment
Status: ready-for-dev
Status: review
## Story
@@ -20,53 +20,44 @@ so that all subsequent stories have a solid foundation to build on.
## Tasks / Subtasks
- [ ] Task 1: Initialize project (AC: #1)
- [ ] Run `npm create vite@latest try-bmad -- --template svelte` (or init in current directory)
- [ ] Verify `npm run dev` starts dev server at localhost:5173
- [ ] Remove default Svelte demo content (Counter component, default styles, Svelte/Vite logos)
- [x] Task 1: Initialize project (AC: #1)
- [x] Run `npm create vite@latest try-bmad -- --template svelte` (or init in current directory)
- [x] Verify `npm run dev` starts dev server at localhost:5173
- [x] Remove default Svelte demo content (Counter component, default styles, Svelte/Vite logos)
- [ ] Task 2: Install and configure Tailwind CSS v4 (AC: #2)
- [ ] Run `npm install tailwindcss @tailwindcss/vite`
- [ ] Add `tailwindcss()` plugin to `vite.config.js` plugins array
- [ ] Replace content in `src/app.css` with `@import "tailwindcss";`
- [ ] Verify Tailwind utilities work in App.svelte (e.g., a `bg-red-500` class renders correctly)
- [x] Task 2: Install and configure Tailwind CSS v4 (AC: #2)
- [x] Run `npm install tailwindcss @tailwindcss/vite`
- [x] Add `tailwindcss()` plugin to `vite.config.js` plugins array
- [x] Replace content in `src/app.css` with `@import "tailwindcss";`
- [x] Verify Tailwind utilities work in App.svelte (e.g., a `bg-red-500` class renders correctly)
- [ ] Task 3: Install Vitest (AC: #3)
- [ ] Run `npm install -D vitest`
- [ ] Add `"test": "vitest"` script to `package.json`
- [ ] Create a placeholder test file `src/lib/game-logic.test.js` with a single passing test
- [ ] Verify `npx vitest run` passes
- [x] Task 3: Install Vitest (AC: #3)
- [x] Run `npm install -D vitest`
- [x] Add `"test": "vitest"` script to `package.json`
- [x] Create a placeholder test file `src/lib/game-logic.test.js` with a single passing test
- [x] Verify `npx vitest run` passes
- [ ] Task 4: Create folder structure (AC: #4)
- [ ] Create `src/lib/` directory (for pure JS game logic modules)
- [ ] Create `src/components/` directory (for Svelte UI components)
- [ ] Create `public/fonts/` directory (for Clear Sans font files)
- [x] Task 4: Create folder structure (AC: #4)
- [x] Create `src/lib/` directory (for pure JS game logic modules)
- [x] Create `src/components/` directory (for Svelte UI components)
- [x] Create `public/fonts/` directory (for Clear Sans font files)
- [ ] Task 5: Create constants module (AC: #5)
- [ ] Create `src/lib/constants.js` with all game constants:
- [x] Task 5: Create constants module (AC: #5)
- [x] Create `src/lib/constants.js` with all game constants:
- `GRID_SIZE = 4`
- `WIN_VALUE = 2048`
- `SPAWN_PROBABILITY = 0.9` (probability of spawning a 2 vs 4)
- `DIRECTIONS = { UP: 'up', DOWN: 'down', LEFT: 'left', RIGHT: 'right' }`
- `TILE_COLORS` map: `{ 2: { bg: '#eee4da', text: '#776e65' }, 4: { bg: '#ede0c8', text: '#776e65' }, 8: { bg: '#f2b179', text: '#f9f6f2' }, 16: { bg: '#f59563', text: '#f9f6f2' }, 32: { bg: '#f67c5f', text: '#f9f6f2' }, 64: { bg: '#f65e3b', text: '#f9f6f2' }, 128: { bg: '#edcf72', text: '#f9f6f2' }, 256: { bg: '#edcc61', text: '#f9f6f2' }, 512: { bg: '#edc850', text: '#f9f6f2' }, 1024: { bg: '#edc53f', text: '#f9f6f2' }, 2048: { bg: '#edc22e', text: '#f9f6f2' }, super: { bg: '#3c3a32', text: '#f9f6f2' } }`
- `TILE_COLORS` map with all 12 tiers
- [ ] Task 6: Add Clear Sans font (AC: #6)
- [ ] Download Clear Sans woff2 from Intel's open-source release or a CDN
- [ ] Place `ClearSans-Bold.woff2` (and regular weight if available) in `public/fonts/`
- [ ] Add `@font-face` declaration in `src/app.css` after the Tailwind import:
```css
@font-face {
font-family: 'Clear Sans';
src: url('/fonts/ClearSans-Bold.woff2') format('woff2');
font-weight: bold;
font-style: normal;
font-display: swap;
}
```
- [x] Task 6: Add Clear Sans font (AC: #6)
- [x] Download Clear Sans woff2 from @fontsource/clear-sans npm package
- [x] Place `ClearSans-Bold.woff2` and `ClearSans-Regular.woff2` in `public/fonts/`
- [x] Add `@font-face` declarations in `src/app.css` for both bold and regular weights
- [ ] Task 7: Set page background and base styles (AC: #7)
- [ ] In `src/app.css`, add base body style: `body { background: #faf8ef; font-family: 'Clear Sans', 'Helvetica Neue', Arial, sans-serif; margin: 0; }`
- [ ] Clean up `App.svelte` to render a minimal placeholder (e.g., "2048" heading) confirming styles work
- [x] Task 7: Set page background and base styles (AC: #7)
- [x] In `src/app.css`, add base body style with background #faf8ef and Clear Sans font family
- [x] Clean up `App.svelte` to render a minimal "2048" heading confirming styles work
## Dev Notes
@@ -93,27 +84,6 @@ so that all subsequent stories have a solid foundation to build on.
- Functions: `camelCase` (e.g., `initGame`, `moveTiles`)
- Constants: `UPPER_SNAKE_CASE` (e.g., `GRID_SIZE`, `WIN_VALUE`)
### Project Structure After This Story
```
try-bmad/
├── index.html
├── package.json
├── vite.config.js
├── .gitignore
├── public/
│ └── fonts/
│ └── ClearSans-Bold.woff2
├── src/
│ ├── main.js
│ ├── App.svelte
│ ├── app.css
│ ├── components/ (empty, ready for Story 1.3+)
│ └── lib/
│ ├── constants.js
│ └── game-logic.test.js (placeholder)
```
### References
- [Source: _bmad-output/planning-artifacts/architecture.md#Starter Template Evaluation]
@@ -126,8 +96,35 @@ try-bmad/
### Agent Model Used
Claude Opus 4.6 (1M context)
### Debug Log References
- Scaffolded to temp directory and copied files (current directory was non-empty)
- Clear Sans font sourced from @fontsource/clear-sans npm package, copied static woff2 files to public/fonts
### Completion Notes List
- Project scaffolded with Svelte 5.55.1 + Vite 8.0.4
- Tailwind CSS v4.2.2 configured via @tailwindcss/vite plugin
- Vitest v4.1.4 installed with 5 passing constants tests
- Constants module created with all 12-tier tile colors, directions, and game values
- Clear Sans Bold + Regular fonts placed in public/fonts with @font-face declarations
- Page background #faf8ef set, minimal App.svelte with 2048 heading
- Production build: 24KB JS + 11KB CSS (well under 50KB gzipped target)
### File List
- index.html (from scaffold)
- package.json (configured with project name, test script)
- vite.config.js (Svelte + Tailwind plugins)
- jsconfig.json (from scaffold)
- svelte.config.js (from scaffold)
- src/main.js (from scaffold)
- src/app.css (Tailwind import + @font-face + body styles)
- src/App.svelte (minimal 2048 heading)
- src/lib/constants.js (game constants)
- src/lib/game-logic.test.js (constants validation tests)
- public/fonts/ClearSans-Bold.woff2
- public/fonts/ClearSans-Regular.woff2
- public/favicon.svg (from scaffold)
@@ -44,7 +44,7 @@ story_location: _bmad-output/implementation-artifacts
development_status:
# Epic 1: Play a Complete Game (Desktop)
epic-1: in-progress
1-1-project-scaffold-and-dev-environment: ready-for-dev
1-1-project-scaffold-and-dev-environment: review
1-2-game-logic-module: backlog
1-3-game-board-and-tile-rendering: backlog
1-4-game-header-score-display-and-new-game-button: backlog
+13
View File
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>try-bmad-scaffold</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
+33
View File
@@ -0,0 +1,33 @@
{
"compilerOptions": {
"moduleResolution": "bundler",
"target": "ESNext",
"module": "ESNext",
/**
* svelte-preprocess cannot figure out whether you have
* a value or a type, so tell TypeScript to enforce using
* `import type` instead of `import` for Types.
*/
"verbatimModuleSyntax": true,
"isolatedModules": true,
"resolveJsonModule": true,
/**
* To have warnings / errors of the Svelte compiler at the
* correct position, enable source maps by default.
*/
"sourceMap": true,
"esModuleInterop": true,
"types": ["vite/client"],
"skipLibCheck": true,
/**
* Typecheck JS in `.svelte` and `.js` files by default.
* Disable this if you'd like to use dynamic types.
*/
"checkJs": true
},
/**
* Use global.d.ts instead of compilerOptions.types
* to avoid limiting type declarations.
*/
"include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"]
}
+1869
View File
File diff suppressed because it is too large Load Diff
+20
View File
@@ -0,0 +1,20 @@
{
"name": "try-bmad",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"test": "vitest"
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^7.0.0",
"@tailwindcss/vite": "^4.2.2",
"svelte": "^5.55.1",
"tailwindcss": "^4.2.2",
"vite": "^8.0.4",
"vitest": "^4.1.4"
}
}
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
<h1 class="text-center text-[80px] font-bold" style="color: #776e65;">2048</h1>
+23
View File
@@ -0,0 +1,23 @@
@import "tailwindcss";
@font-face {
font-family: 'Clear Sans';
src: url('/fonts/ClearSans-Regular.woff2') format('woff2');
font-weight: normal;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Clear Sans';
src: url('/fonts/ClearSans-Bold.woff2') format('woff2');
font-weight: bold;
font-style: normal;
font-display: swap;
}
body {
background: #faf8ef;
font-family: 'Clear Sans', 'Helvetica Neue', Arial, sans-serif;
margin: 0;
}
+25
View File
@@ -0,0 +1,25 @@
export const GRID_SIZE = 4;
export const WIN_VALUE = 2048;
export const SPAWN_PROBABILITY = 0.9;
export const DIRECTIONS = {
UP: 'up',
DOWN: 'down',
LEFT: 'left',
RIGHT: 'right',
};
export const TILE_COLORS = {
2: { bg: '#eee4da', text: '#776e65' },
4: { bg: '#ede0c8', text: '#776e65' },
8: { bg: '#f2b179', text: '#f9f6f2' },
16: { bg: '#f59563', text: '#f9f6f2' },
32: { bg: '#f67c5f', text: '#f9f6f2' },
64: { bg: '#f65e3b', text: '#f9f6f2' },
128: { bg: '#edcf72', text: '#f9f6f2' },
256: { bg: '#edcc61', text: '#f9f6f2' },
512: { bg: '#edc850', text: '#f9f6f2' },
1024: { bg: '#edc53f', text: '#f9f6f2' },
2048: { bg: '#edc22e', text: '#f9f6f2' },
super: { bg: '#3c3a32', text: '#f9f6f2' },
};
+33
View File
@@ -0,0 +1,33 @@
import { describe, it, expect } from 'vitest';
import { GRID_SIZE, WIN_VALUE, SPAWN_PROBABILITY, DIRECTIONS, TILE_COLORS } from './constants.js';
describe('constants', () => {
it('should have correct grid size', () => {
expect(GRID_SIZE).toBe(4);
});
it('should have correct win value', () => {
expect(WIN_VALUE).toBe(2048);
});
it('should have correct spawn probability', () => {
expect(SPAWN_PROBABILITY).toBe(0.9);
});
it('should have all four directions', () => {
expect(DIRECTIONS).toEqual({
UP: 'up',
DOWN: 'down',
LEFT: 'left',
RIGHT: 'right',
});
});
it('should have 12 tile color tiers', () => {
const keys = Object.keys(TILE_COLORS);
expect(keys).toHaveLength(12);
expect(TILE_COLORS[2].bg).toBe('#eee4da');
expect(TILE_COLORS[2048].bg).toBe('#edc22e');
expect(TILE_COLORS.super.bg).toBe('#3c3a32');
});
});
+9
View File
@@ -0,0 +1,9 @@
import { mount } from 'svelte'
import './app.css'
import App from './App.svelte'
const app = mount(App, {
target: document.getElementById('app'),
})
export default app
+2
View File
@@ -0,0 +1,2 @@
/** @type {import("@sveltejs/vite-plugin-svelte").SvelteConfig} */
export default {}
+8
View File
@@ -0,0 +1,8 @@
import { defineConfig } from 'vite'
import { svelte } from '@sveltejs/vite-plugin-svelte'
import tailwindcss from '@tailwindcss/vite'
// https://vite.dev/config/
export default defineConfig({
plugins: [svelte(), tailwindcss()],
})