@@ -21,8 +21,259 @@ If the build fails, you must:
2121
2222Do not stop after a failed build. Keep iterating until it passes.
2323
24+ ---
25+
26+ ## UI Testing & Screenshot Process (for UI changes)
27+
28+ When making UI changes (styling, components, layout, animations, etc.), you MUST follow this process to verify and document the changes visually.
29+
30+ ### Step 1: Build the site
31+
32+ ``` bash
33+ npm install
34+ npm run build
35+ ```
36+
37+ ### Step 2: Start the local server
38+
39+ This site uses static export, so use ` serve ` to host the built files:
40+
41+ ``` bash
42+ npx serve out -l 3000 &
43+ ```
44+
45+ Wait for the server to start, then verify it's running:
46+
47+ ``` bash
48+ curl -s http://localhost:3000 -o /dev/null -w " %{http_code}"
49+ # Should return 200
50+ ```
51+
52+ ### Step 3: Ensure screenshots directory exists
53+
54+ All screenshots should be saved to ` public/screenshots/ ` directory:
55+
56+ ``` bash
57+ mkdir -p public/screenshots
58+ ```
59+
60+ ### Step 4: Take screenshots with Puppeteer
61+
62+ Puppeteer is already installed as a dev dependency. Run this inline script to capture screenshots of all major pages:
63+
64+ ``` bash
65+ node -e "
66+ const puppeteer = require('puppeteer');
67+ const path = require('path');
68+
69+ const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
70+
71+ const SCREENSHOT_DIR = '/workspace/public/screenshots';
72+
73+ async function takeScreenshots() {
74+ console.log('Starting puppeteer...');
75+ const browser = await puppeteer.launch({
76+ headless: true,
77+ args: ['--no-sandbox', '--disable-setuid-sandbox']
78+ });
79+
80+ const page = await browser.newPage();
81+
82+ // Pages to screenshot - add/remove based on what changed
83+ // Public pages
84+ const pages = [
85+ { name: 'home', url: '/' },
86+ { name: 'about', url: '/about' },
87+ { name: 'blog', url: '/blog' },
88+ { name: 'projects', url: '/projects' },
89+ { name: 'certifications', url: '/certifications' },
90+ { name: 'contact', url: '/contact' },
91+ // VIP pages
92+ { name: 'hub', url: '/hub' },
93+ { name: 'links', url: '/links' },
94+ { name: 'v2me', url: '/v2me' },
95+ ];
96+
97+ // Desktop viewport
98+ await page.setViewport({ width: 1440, height: 900 });
99+
100+ // Take screenshots in dark mode first
101+ console.log('\\ n=== DARK MODE SCREENSHOTS ===');
102+ await page.goto('http://localhost:3000', { waitUntil: 'networkidle0', timeout: 30000 });
103+ await page.evaluate(() => {
104+ const darkBtn = document.querySelector('button[aria-label=\" Dark theme\" ]');
105+ if (darkBtn) darkBtn.click();
106+ });
107+ await delay(500);
108+
109+ for (const p of pages) {
110+ console.log('Taking dark mode screenshot: ' + p.name + '...');
111+ await page.goto('http://localhost:3000' + p.url, { waitUntil: 'networkidle0', timeout: 30000 });
112+ await delay(1000);
113+ await page.screenshot({
114+ path: path.join(SCREENSHOT_DIR, 'screenshot-' + p.name + '-dark.png'),
115+ fullPage: false
116+ });
117+ }
118+
119+ // Take screenshots in light mode
120+ console.log('\\ n=== LIGHT MODE SCREENSHOTS ===');
121+ await page.evaluate(() => {
122+ const lightBtn = document.querySelector('button[aria-label=\" Light theme\" ]');
123+ if (lightBtn) lightBtn.click();
124+ });
125+ await delay(500);
126+
127+ for (const p of pages) {
128+ console.log('Taking light mode screenshot: ' + p.name + '...');
129+ await page.goto('http://localhost:3000' + p.url, { waitUntil: 'networkidle0', timeout: 30000 });
130+ await delay(1000);
131+ await page.screenshot({
132+ path: path.join(SCREENSHOT_DIR, 'screenshot-' + p.name + '-light.png'),
133+ fullPage: false
134+ });
135+ }
136+
137+ // Mobile screenshots (dark mode)
138+ console.log('\\ n=== MOBILE SCREENSHOTS ===');
139+ await page.setViewport({ width: 375, height: 812 });
140+ await page.evaluate(() => {
141+ const darkBtn = document.querySelector('button[aria-label=\" Dark theme\" ]');
142+ if (darkBtn) darkBtn.click();
143+ });
144+ await delay(500);
145+
146+ for (const p of pages) {
147+ console.log('Taking mobile dark screenshot: ' + p.name + '...');
148+ await page.goto('http://localhost:3000' + p.url, { waitUntil: 'networkidle0', timeout: 30000 });
149+ await delay(800);
150+ await page.screenshot({
151+ path: path.join(SCREENSHOT_DIR, 'screenshot-' + p.name + '-mobile-dark.png'),
152+ fullPage: false
153+ });
154+ }
155+
156+ await browser.close();
157+ console.log('\\ nAll screenshots saved to public/screenshots/');
158+ }
159+
160+ takeScreenshots().catch(console.error);
161+ "
162+ ```
163+
164+ ### Step 5: Share screenshots in the chat
165+
166+ After taking screenshots, use the Read tool to display them in the chat:
167+
168+ ```
169+ Read the image file: /workspace/public/screenshots/screenshot-home-dark.png
170+ Read the image file: /workspace/public/screenshots/screenshot-home-light.png
171+ Read the image file: /workspace/public/screenshots/screenshot-about-dark.png
172+ // etc.
173+ ```
174+
175+ This allows the user to review the UI changes visually before merging.
176+
177+ ### Step 6: Commit screenshots (optional)
178+
179+ If the user wants screenshots included in the PR for documentation:
180+
181+ ``` bash
182+ git add public/screenshots/
183+ git commit -m " docs: Add UI screenshots for PR review"
184+ git push origin < branch-name>
185+ ```
186+
187+ ---
188+
189+ ## Screenshot Directory Structure
190+
191+ All screenshots are saved to ` public/screenshots/ ` with the following naming convention:
192+
193+ ```
194+ public/screenshots/
195+ ├── screenshot-{page}-dark.png # Desktop dark mode
196+ ├── screenshot-{page}-light.png # Desktop light mode
197+ ├── screenshot-{page}-mobile-dark.png # Mobile dark mode
198+ ```
199+
200+ Where ` {page} ` is one of: ` home ` , ` about ` , ` blog ` , ` projects ` , ` certifications ` , ` contact ` , ` hub ` , ` links ` , ` v2me `
201+
202+ ---
203+
204+ ## Screenshot Checklist
205+
206+ For UI changes, the script automatically captures:
207+
208+ | Screenshot Type | Description | Files Generated |
209+ | -----------------| -------------| -----------------|
210+ | Desktop Dark Mode | All pages in dark theme | ` screenshot-*-dark.png ` |
211+ | Desktop Light Mode | All pages in light theme | ` screenshot-*-light.png ` |
212+ | Mobile Dark Mode | All pages in mobile viewport | ` screenshot-*-mobile-dark.png ` |
213+
214+ ### Minimum Required Screenshots
215+
216+ | Screenshot | Required |
217+ | ------------| ----------|
218+ | Homepage (both themes) | Always |
219+ | Pages with changes | Yes |
220+ | Mobile view | Yes (for responsive/nav changes) |
221+
222+ ---
223+
224+ ## Page-Specific Screenshots
225+
226+ The standard script captures these pages:
227+
228+ ### Public Pages
229+
230+ | Page | URL | When to capture |
231+ | ------| -----| -----------------|
232+ | Homepage | ` / ` | Always for any UI changes |
233+ | About | ` /about ` | Global layout, typography changes |
234+ | Blog | ` /blog ` | Card styles, listing changes |
235+ | Projects | ` /projects ` | Card styles, section changes |
236+ | Certifications | ` /certifications ` | Accordion, badge display changes |
237+ | Contact | ` /contact ` | Form styles, layout changes |
238+
239+ ### VIP Pages (included in standard script)
240+
241+ | Page | URL | When to capture |
242+ | ------| -----| -----------------|
243+ | Hub | ` /hub ` | Linktree-style layout, section styling |
244+ | Links | ` /links ` | Links directory, section styling |
245+ | V2ME | ` /v2me ` | V2ME framework components, progress bar |
246+
247+ ### Additional pages (add to script if needed):
248+
249+ | Page | URL | When to capture |
250+ | ------| -----| -----------------|
251+ | Blog Post | ` /blog/[slug] ` | Blog content styling |
252+
253+ ---
254+
255+ ## Troubleshooting
256+
257+ ### Server won't start
258+ - Kill any existing processes: ` pkill -f "serve out" `
259+ - Try a different port: ` npx serve out -l 3001 `
260+
261+ ### Puppeteer errors
262+ - Ensure ` --no-sandbox ` flag is used
263+ - Use ` delay() ` helper instead of deprecated ` page.waitForTimeout() `
264+ - Increase timeout for slow pages
265+
266+ ### Screenshots are blank or incomplete
267+ - Increase delay after navigation (animations need time)
268+ - Check if page requires JavaScript hydration
269+ - Verify server is responding: ` curl http://localhost:3000 `
270+
271+ ---
272+
24273## Notes
25274
26275- The ` export ` script is currently the same as ` build ` , so ` npm run build `
27276 is the canonical check for release readiness.
28277- If you are only changing content or links, the build is still required.
278+ - Screenshots should be taken AFTER the build succeeds.
279+ - Always share screenshots in the chat for user review before completing the task.
0 commit comments