+ + ๐ฎ Rock Paper Scissors + Game + + + + Classic strategy game with AI opponent. Master the mind games! + + + + Beginner + โฑ๏ธ 5 min + + + + Play Now + +
` | +| **Keyboard Navigation** | Tab, Enter, Escape, Arrow keys functional | +| **Focus Indicators** | 2px outline ring at 2px offset | +| **Color Contrast** | 4.5:1 text, 3:1 large text | +| **Screen Readers** | ARIA labels, landmarks, live regions | +| **Reduced Motion** | Animations disabled when requested | +| **Alt Text** | Provided for meaningful images | +| **Form Labels** | Associated with inputs via `` | + +### โจ๏ธ Keyboard Navigation + +**Focusable Elements:** +- `` and `` +- ``, ``, `` +- Elements with `tabindex="0"` + +**Keyboard Shortcuts:** +| Key | Action | +|-----|--------| +| `Tab` | Move to next element | +| `Shift+Tab` | Move to previous element | +| `Enter` | Activate button, submit form | +| `Space` | Toggle checkbox | +| `/` | Focus search input | +| `Escape` | Close modal, menu | +| `Arrow Keys` | Navigate menu/list | + +### ๐ท๏ธ ARIA Essentials + +```html + +ร + + + + Modal Title + + + + + Form validation message + + + + + + + +Enter your email + + + + Tab 1 + +``` + +### โฟ Accessibility Checklist + +```markdown +โ Semantic HTML used correctly +โ All interactive elements keyboard accessible +โ Focus indicators visible +โ Color contrast meets 4.5:1 (WCAG AA) +โ Form inputs have labels +โ Images have alt text +โ Modals trap focus +โ Screen reader tested +โ Keyboard-only navigation works +โ No keyboard traps +โ Error messages associated with inputs +โ Reduced motion respected +โ Touch targets 44ร44px+ +``` + +--- + +## ๐ฑ Responsive Design System + +### ๐ Breakpoints & Strategy + +**Mobile-First Approach:** +```css +/* Base: Mobile (320px+) */ +.element { padding: 1rem; font-size: 1rem; } + +/* Small devices (640px+) */ +@media (min-width: 640px) { + .element { padding: 1.5rem; } +} + +/* Tablets (768px+) */ +@media (min-width: 768px) { + .element { padding: 2rem; } +} + +/* Desktop (1024px+) */ +@media (min-width: 1024px) { + .element { padding: 2.5rem; max-width: 1200px; } +} + +/* Large screens (1440px+) */ +@media (min-width: 1440px) { + .element { padding: 3rem; } +} +``` + +### Grid Behavior + +```css +/* Mobile: single column */ +.grid { + display: grid; + grid-template-columns: 1fr; + gap: 1rem; +} + +/* Tablet: 2 columns */ +@media (min-width: 640px) { + .grid { + grid-template-columns: repeat(2, 1fr); + gap: 1.5rem; + } +} + +/* Desktop: auto-fill */ +@media (min-width: 1024px) { + .grid { + grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); + gap: 2rem; + } +} +``` + +### ๐ง Fluid Typography + +```css +/* Scales between breakpoints */ +h1 { font-size: clamp(1.75rem, 5vw, 3rem); } +h2 { font-size: clamp(1.5rem, 4vw, 2.25rem); } +body { font-size: clamp(0.95rem, 2vw, 1.1rem); } +``` + +### ๐ฑ๏ธ Touch-Friendly Design + +```css +/* Minimum 44ร44px touch targets */ +.btn { + min-width: 44px; + min-height: 44px; + padding: 0.75rem 1rem; +} + +/* Remove 300ms click delay */ +a, button, input { + touch-action: manipulation; +} +``` + +--- + +## ๐ Folder Structure + +``` +web-app/ +โโโ ๐ index.html # Homepage +โโโ ๐ games.html # Games category +โโโ ๐ math.html # Math category +โโโ ๐ utilities.html # Utilities category +โ +โโโ ๐ css/ +โ โโโ ๐ styles.css # All styles +โ +โโโ ๐ js/ +โ โโโ ๐ main.js # Orchestration +โ โโโ ๐ playground.js # Pyodide interface +โ โโโ ๐ playground-worker.js # Web Worker +โ โโโ ๐ projects.js # Project registry +โ โโโ ๐ audio.js # Sound effects +โ โโโ ๐ storage.js # localStorage +โ โโโ ๐ projects/ # 40+ project files +โ +โโโ ๐ assets/ + โโโ favicon.svg + โโโ logo.png + โโโ images/ +``` + +--- + +## ๐ JavaScript Modules + +### Module Relationships + +```mermaid +graph LR + main["๐ main.js\n(orchestration)"] + playground["๐ playground.js\n(Pyodide)"] + projects["๐ฆ projects.js\n(registry)"] + storage["๐พ storage.js"] + audio["๐ audio.js"] + worker["โ๏ธ worker.js"] + + main -->|control| playground + main -->|render| projects + main -->|read/write| storage + main -->|play| audio + playground -->|spawn| worker +``` + +### Module Responsibilities + +| Module | Responsibility | Key Functions | +|--------|-----------------|-----------------| +| **main.js** | App orchestration, DOM events | toggleTheme(), openModal(), filterProjects() | +| **playground.js** | Pyodide interface, Worker lifecycle | initPlayground(), runCode(), stopExecution() | +| **playground-worker.js** | Python execution | loadPyodide(), execute code, capture output | +| **projects.js** | Project registry | getProjectHTML(), initialize projects | +| **storage.js** | localStorage wrapper | saveToStorage(), loadFromStorage() | +| **audio.js** | Sound effects | play(), toggleMute() | + +--- + +## ๐จโ๐ป Contributor Guide + +### ๐ Quick Start + +```bash +# Clone repo +git clone https://github.com/steam-bell-92/python-mini-project.git +cd python-mini-project/web-app + +# Serve locally +python -m http.server 8000 + +# Open browser: http://localhost:8000 +# Open DevTools: F12 or Ctrl+Shift+I +``` + +### ๐ง Development Workflow + +```bash +# Create feature branch +git checkout -b feature/your-feature-name + +# Make changes, test thoroughly + +# Test checklist: +# - Keyboard navigation (Tab, Enter, Escape) +# - Focus indicators visible +# - Mobile responsive (320px, 768px, 1024px) +# - Dark and light themes +# - No console errors +# - No a11y violations + +# Commit +git add web-app/ +git commit -m "feat: your message" +git push origin feature/your-feature-name +``` + +### โ PR Review Checklist + +```markdown +## Code Quality +โ Semantic HTML used +โ CSS uses variables +โ No hardcoded colors +โ No console errors + +## Functionality +โ All features work +โ Modals open/close +โ Theme toggle works +โ Search functions + +## Accessibility +โ Keyboard nav works +โ Focus visible +โ Color contrast OK +โ Screen reader OK + +## Responsive +โ Mobile (320px) +โ Tablet (768px) +โ Desktop (1024px) +โ Touch targets 44ร44px + +## Themes +โ Dark mode works +โ Light mode works +โ Transitions smooth +``` + +--- + +## โจ Adding New Projects + +### Step 1: Create Project File + +Create `web-app/js/projects/your-project.js`: + +```javascript +function getYourProjectNameHTML() { + return ` + + ๐ฎ Your Project + + + Start + Reset + + Score: 0 + + `; +} + +function initializeYourProjectName() { + const startBtn = document.getElementById('startBtn'); + const resetBtn = document.getElementById('resetBtn'); + let score = 0; + + startBtn.addEventListener('click', () => { + // Start game + score++; + document.getElementById('score').textContent = score; + }); + + resetBtn.addEventListener('click', () => { + score = 0; + document.getElementById('score').textContent = score; + }); +} +``` + +### Step 2: Register in projects.js + +```javascript +function getProjectHTML(projectName) { + const projects = { + 'your-project': () => getYourProjectNameHTML(), + // ... others ... + }; + return projects[projectName]?.() || 'Coming Soon'; +} +``` + +### Step 3: Add to HTML + +```html + + + + ๐ฎ Your Project + Brief description + + Game + โฑ๏ธ 5 min + + Play Now + +``` + +### Step 4: Test + +```markdown +โ Modal opens +โ UI renders +โ Keyboard nav works +โ Mobile responsive +โ Themes work +โ No console errors +``` + +--- + +## ๐จ Adding New Components + +### Component Template + +```html + + Title + Content + +``` + +```css +.component { + background: var(--surface-color); + border: 1px solid var(--border-color); + border-radius: 0.5rem; + padding: 1.5rem; + transition: var(--transition); +} + +.component:hover { + border-color: var(--primary-color); + box-shadow: var(--shadow); +} + +.component:focus-within { + outline: 2px solid var(--primary-color); + outline-offset: 2px; +} + +@media (max-width: 640px) { + .component { padding: 1rem; } +} +``` + +### Component Requirements + +- โ Semantic HTML +- โ CSS variables only +- โ Keyboard accessible +- โ Focus visible +- โ Mobile responsive +- โ Theme compatible +- โ Screen reader friendly + +--- + +## โ๏ธ Storage & Persistence API + +```javascript +// storage.js utilities + +function saveToStorage(key, value) { + try { + localStorage.setItem(key, JSON.stringify(value)); + return true; + } catch (error) { + console.error('Storage error:', error); + return false; + } +} + +function loadFromStorage(key, defaultValue = null) { + try { + const data = localStorage.getItem(key); + return data ? JSON.parse(data) : defaultValue; + } catch (error) { + return defaultValue; + } +} + +function removeFromStorage(key) { + localStorage.removeItem(key); +} +``` + +--- + +## ๐งช Testing & Debugging + +### Manual Testing Checklist + +```markdown +## Functionality +โ Buttons work +โ Forms submit +โ Modals open/close +โ Search filters +โ Theme toggles + +## Keyboard Navigation +โ Tab navigation works +โ Shift+Tab works +โ Enter activates +โ Escape closes +โ / focuses search + +## Responsive +โ Mobile (320px) +โ Tablet (768px) +โ Desktop (1024px) +โ No horizontal scroll +โ Touch targets adequate + +## Accessibility +โ Focus visible +โ Color contrast OK +โ Form labels present +โ ARIA attributes OK +โ Screen reader OK + +## Cross-Browser +โ Chrome +โ Firefox +โ Safari +โ Edge +โ Mobile browsers +``` + +### DevTools Tips + +``` +F12 / Ctrl+Shift+I โ Open DevTools + โข Elements โ HTML structure + โข Console โ JavaScript errors + โข Sources โ Set breakpoints + โข Network โ Resource loading + โข Performance โ Animation profile +``` + +--- + +## ๐ Performance Optimization + +| Technique | Benefit | +|-----------|---------| +| **Debounce** | Reduce function calls (search 300ms) | +| **Cache DOM** | Fewer queries | +| **Event delegation** | Fewer listeners | +| **Lazy load** | Faster initial load | +| **CSS animations** | 60fps performance | +| **Web Workers** | Non-blocking UI | + +--- + +## ๐ CSS Variables Reference + +```css +:root { + /* Brand Colors */ + --primary-color: #22c55e; + --secondary-color: #06b6d4; + + /* Semantic */ + --success-color: #10b981; + --danger-color: #ef4444; + --warning-color: #f59e0b; + + /* Backgrounds */ + --bg-color: #081120; + --surface-color: #111827; + --panel-color: #0f172a; + + /* Text */ + --text-color: #e5e7eb; + --text-secondary: #94a3b8; + + /* Borders */ + --border-color: #1f2937; + + /* Effects */ + --shadow: 0 10px 30px rgba(0, 0, 0, 0.3); + --shadow-modal: 0 25px 50px -12px rgba(0, 0, 0, 0.45); + + /* Timing */ + --transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + --transition-fast: 0.15s cubic-bezier(0.4, 0, 0.2, 1); +} +``` + +--- + +## ๐ Performance Stats + +| Operation | Time | +|-----------|------| +| First Pyodide load | 3โ5s | +| Cached Pyodide | 100โ300ms | +| Modal animation | 300ms | +| Theme transition | 300ms | +| Search debounce | 300ms | + +--- + +## ๏ฟฝ Form Components & Validation + +### Input Field + +```html + + Username + + + 2-20 characters, letters and numbers only + + + +``` + +```css +.form-group { + display: flex; + flex-direction: column; + gap: 0.5rem; + margin-bottom: 1.5rem; +} + +.form-label { + font-weight: 600; + color: var(--text-color); + font-size: 0.95rem; +} + +.form-input { + padding: 0.75rem; + border: 1px solid var(--border-color); + border-radius: 0.5rem; + background: var(--panel-color); + color: var(--text-color); + font: inherit; + transition: var(--transition); +} + +.form-input:focus { + outline: none; + border-color: var(--primary-color); + box-shadow: 0 0 0 2px var(--primary-color); + opacity: 0.2; +} + +.form-input:invalid:not(:placeholder-shown) { + border-color: var(--danger-color); +} + +.form-help { + font-size: 0.85rem; + color: var(--text-secondary); +} + +.form-error { + font-size: 0.85rem; + color: var(--danger-color); + animation: slideDown 0.3s ease; +} + +@keyframes slideDown { + from { + opacity: 0; + transform: translateY(-8px); + } + to { + opacity: 1; + transform: translateY(0); + } +} +``` + +### Form Validation (JavaScript) + +```javascript +function validateForm() { + const username = document.getElementById('username'); + const usernameError = document.getElementById('usernameError'); + const isValid = /^[a-zA-Z0-9]{2,20}$/.test(username.value); + + if (!isValid) { + usernameError.textContent = 'Username invalid'; + usernameError.removeAttribute('hidden'); + username.setAttribute('aria-invalid', 'true'); + username.setAttribute('aria-describedby', 'usernameError'); + } else { + usernameError.setAttribute('hidden', ''); + username.removeAttribute('aria-invalid'); + } + + return isValid; +} + +document.getElementById('username').addEventListener('blur', validateForm); +``` + +--- + +## ๐ Data Visualization & Tables + +### Responsive Table + +```html + + + + + Project + Category + Difficulty + Time + Players + + + + + ๐ฎ Snake Game + Game + Easy + 10 min + 1 + + + + +``` + +```css +.table-wrapper { + overflow-x: auto; + border-radius: 0.5rem; + border: 1px solid var(--border-color); +} + +.data-table { + width: 100%; + border-collapse: collapse; + font-size: 0.95rem; +} + +.data-table thead { + background: var(--surface-color); +} + +.data-table th { + padding: 1rem; + text-align: left; + font-weight: 600; + color: var(--text-color); + border-bottom: 2px solid var(--border-color); +} + +.data-table td { + padding: 1rem; + border-bottom: 1px solid var(--border-color); + color: var(--text-color); +} + +.data-table tr:hover { + background: var(--panel-color); +} + +/* Mobile: Show as cards */ +@media (max-width: 768px) { + .data-table thead { display: none; } + + .data-table tr { + display: block; + margin-bottom: 1.5rem; + border: 1px solid var(--border-color); + border-radius: 0.5rem; + padding: 1rem; + } + + .data-table td { + display: block; + padding: 0.5rem 0; + border: none; + } + + .data-table td::before { + content: attr(data-label); + font-weight: 600; + display: block; + margin-bottom: 0.25rem; + color: var(--text-secondary); + } +} +``` + +--- + +## ๐ Notification & Toast System + +```html + + + +``` + +```javascript +const NotificationSystem = { + show(message, type = 'info', duration = 3000) { + const container = document.getElementById('notificationContainer'); + const toast = document.createElement('div'); + toast.className = `notification notification-${type} notification-enter`; + toast.setAttribute('role', 'alert'); + toast.setAttribute('aria-atomic', 'true'); + + const icon = { + success: 'โ', + error: 'โ', + warning: '!', + info: 'โน' + }[type] || 'โน'; + + toast.innerHTML = ` + ${icon} + ${message} + ร + `; + + container.appendChild(toast); + + toast.querySelector('.notification-close').addEventListener('click', () => { + toast.classList.replace('notification-enter', 'notification-exit'); + setTimeout(() => toast.remove(), 300); + }); + + if (duration > 0) { + setTimeout(() => { + toast.classList.replace('notification-enter', 'notification-exit'); + setTimeout(() => toast.remove(), 300); + }, duration); + } + + return toast; + }, + + success: (msg, duration) => NotificationSystem.show(msg, 'success', duration), + error: (msg, duration) => NotificationSystem.show(msg, 'error', duration), + warning: (msg, duration) => NotificationSystem.show(msg, 'warning', duration), + info: (msg, duration) => NotificationSystem.show(msg, 'info', duration), +}; + +// Usage +NotificationSystem.success('Changes saved!'); +NotificationSystem.error('An error occurred'); +``` + +```css +.notification-container { + position: fixed; + top: 1rem; + right: 1rem; + z-index: 5000; + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.notification { + display: flex; + align-items: center; + gap: 0.75rem; + padding: 1rem; + border-radius: 0.5rem; + background: var(--surface-color); + border-left: 4px solid; + box-shadow: var(--shadow); + font-weight: 500; + animation: slideInRight 0.3s ease; +} + +.notification-success { border-left-color: var(--success-color); } +.notification-error { border-left-color: var(--danger-color); } +.notification-warning { border-left-color: var(--warning-color); } +.notification-info { border-left-color: var(--secondary-color); } + +.notification-enter { + animation: slideInRight 0.3s ease; +} + +.notification-exit { + animation: slideOutRight 0.3s ease; +} + +@keyframes slideInRight { + from { + transform: translateX(400px); + opacity: 0; + } + to { + transform: translateX(0); + opacity: 1; + } +} + +@keyframes slideOutRight { + from { + transform: translateX(0); + opacity: 1; + } + to { + transform: translateX(400px); + opacity: 0; + } +} +``` + +--- + +## ๐ฏ Event Handling Patterns + +### Debounce Utility + +```javascript +function debounce(fn, delay = 300) { + let timeoutId; + return function(...args) { + clearTimeout(timeoutId); + timeoutId = setTimeout(() => { + fn.apply(this, args); + }, delay); + }; +} + +// Usage: Debounced search +const handleSearchChange = debounce((query) => { + filterProjects(query); +}, 300); + +document.getElementById('search').addEventListener('input', (e) => { + handleSearchChange(e.target.value); +}); +``` + +### Throttle Utility + +```javascript +function throttle(fn, delay = 300) { + let lastTime = 0; + return function(...args) { + const now = Date.now(); + if (now - lastTime >= delay) { + fn.apply(this, args); + lastTime = now; + } + }; +} + +// Usage: Throttled scroll handler +const handleScroll = throttle(() => { + updateScrollPosition(); +}, 300); + +window.addEventListener('scroll', handleScroll); +``` + +### Event Delegation + +```javascript +// Single listener for all project cards +document.addEventListener('click', (e) => { + const card = e.target.closest('.project-card'); + if (card) { + const projectName = card.dataset.project; + openProjectModal(projectName); + } +}); + +// Advantages: +// - Single listener instead of multiple +// - Works for dynamically added elements +// - Better performance +// - Easier to manage +``` + +--- + +## ๐พ Advanced Storage Patterns + +### Structured Data Storage + +```javascript +class ProjectStorage { + static saveProgress(projectName, progress) { + const data = { + project: projectName, + progress: progress, + timestamp: Date.now(), + version: 1 + }; + saveToStorage(`progress_${projectName}`, data); + } + + static getProgress(projectName) { + return loadFromStorage(`progress_${projectName}`, { + progress: 0, + timestamp: null + }); + } + + static getAllProgress() { + const keys = Object.keys(localStorage); + return keys + .filter(k => k.startsWith('progress_')) + .map(k => loadFromStorage(k)); + } + + static clearProgress(projectName) { + removeFromStorage(`progress_${projectName}`); + } +} + +// Usage +ProjectStorage.saveProgress('snake-game', 150); +const progress = ProjectStorage.getProgress('snake-game'); +const allProgress = ProjectStorage.getAllProgress(); +``` + +### Cache Pattern + +```javascript +class CacheManager { + constructor(maxAge = 3600000) { // 1 hour + this.cache = new Map(); + this.maxAge = maxAge; + } + + set(key, value) { + this.cache.set(key, { + value, + timestamp: Date.now() + }); + } + + get(key) { + const entry = this.cache.get(key); + if (!entry) return null; + + const age = Date.now() - entry.timestamp; + if (age > this.maxAge) { + this.cache.delete(key); + return null; + } + + return entry.value; + } + + clear() { + this.cache.clear(); + } +} + +const cache = new CacheManager(); +cache.set('projects', projectList); +const cached = cache.get('projects'); +``` + +--- + +## ๐ฎ Game Loop Pattern + +```javascript +class GameManager { + constructor() { + this.isRunning = false; + this.score = 0; + this.level = 1; + this.fps = 60; + this.frameTime = 1000 / this.fps; + this.lastTime = 0; + } + + start() { + this.isRunning = true; + this.score = 0; + this.level = 1; + this.gameLoop(0); + } + + gameLoop(currentTime) { + if (!this.isRunning) return; + + const deltaTime = currentTime - this.lastTime; + + if (deltaTime >= this.frameTime) { + this.update(deltaTime / 1000); + this.render(); + this.lastTime = currentTime; + } + + requestAnimationFrame((t) => this.gameLoop(t)); + } + + update(deltaTime) { + // Game logic + } + + render() { + // Render UI + } + + stop() { + this.isRunning = false; + } + + addScore(points) { + this.score += points; + document.getElementById('score').textContent = this.score; + if (this.score % 100 === 0) { + this.levelUp(); + } + } + + levelUp() { + this.level++; + NotificationSystem.success(`Level ${this.level}!`); + } +} + +const game = new GameManager(); +``` + +--- + +## ๐ก Message Protocol (Web Worker) + +```javascript +// main.js +function runPythonCode(code) { + const worker = new Worker('js/playground-worker.js'); + + worker.postMessage({ + type: 'run', + code: code, + id: Date.now() + }); + + worker.onmessage = (e) => { + const { type, data } = e.data; + + switch (type) { + case 'ready': + console.log('Pyodide ready'); + break; + case 'done': + displayOutput(data); + break; + case 'error': + displayError(data); + break; + case 'stderr': + addToOutput(data, 'error'); + break; + case 'stdout': + addToOutput(data, 'output'); + break; + } + }; +} + +// playground-worker.js +let pyodide; + +self.onmessage = async (e) => { + const { type, code, id } = e.data; + + if (type === 'run') { + try { + if (!pyodide) { + pyodide = await loadPyodide(); + self.postMessage({ type: 'ready' }); + } + + const output = await pyodide.runPythonAsync(code); + self.postMessage({ + type: 'done', + data: output, + id: id + }); + } catch (error) { + self.postMessage({ + type: 'error', + data: error.message, + id: id + }); + } + } +}; +``` + +--- + +## ๐ฌ Animation Orchestration + +```javascript +class AnimationOrchestrator { + static async fadeInSequence(elements, delay = 100) { + for (let i = 0; i < elements.length; i++) { + await new Promise(resolve => { + setTimeout(() => { + elements[i].classList.add('fade-in'); + resolve(); + }, i * delay); + }); + } + } + + static staggerAnimation(elements, animationClass = 'slide-up', delay = 50) { + elements.forEach((el, i) => { + setTimeout(() => { + el.classList.add(animationClass); + }, i * delay); + }); + } + + static parallaxScroll() { + window.addEventListener('scroll', () => { + const scrolled = window.scrollY; + document.querySelectorAll('[data-parallax]').forEach(el => { + const rate = el.dataset.parallax || 0.5; + el.style.transform = `translateY(${scrolled * rate}px)`; + }); + }); + } +} + +// Usage +const cards = document.querySelectorAll('.project-card'); +AnimationOrchestrator.staggerAnimation(cards, 'fade-in', 100); +``` + +--- + +## ๐ Debugging Tips + +### Console Helper Functions + +```javascript +// Log with styling +function logInfo(title, data) { + console.log(`%c${title}`, 'color: #22c55e; font-weight: bold;', data); +} + +function logError(title, error) { + console.error(`%c${title}`, 'color: #ef4444; font-weight: bold;', error); +} + +// Performance timing +function timeOperation(name, fn) { + const start = performance.now(); + const result = fn(); + const duration = performance.now() - start; + logInfo(`${name} completed in ${duration.toFixed(2)}ms`, result); + return result; +} + +// Usage +timeOperation('Project List Load', () => { + return loadProjectList(); +}); +``` + +### Common Debugging Patterns + +```javascript +// Check if element exists +if (!document.getElementById('modalBody')) { + logError('Fatal', 'Modal body not found'); +} + +// Verify localStorage +console.log('Theme:', localStorage.getItem('theme')); +console.log('Storage size:', JSON.stringify(localStorage).length / 1024 + ' KB'); + +// Monitor events +document.addEventListener('click', (e) => { + if (e.target.matches('button')) { + logInfo('Button clicked', e.target.textContent); + } +}, true); +``` + +--- + +## ๐ Deployment Checklist + +```markdown +## Pre-Deployment +โ All tests pass +โ No console errors +โ No console warnings +โ No broken links +โ No dead code +โ CSS minified +โ JS minified (optional) +โ Images optimized + +## Accessibility +โ Keyboard nav works +โ Focus visible +โ Color contrast OK +โ Alt text present +โ Form labels OK +โ ARIA attributes OK + +## Performance +โ Lighthouse score > 90 +โ Load time < 2s +โ Pyodide cache working +โ No layout shifts +โ Animations 60fps + +## Cross-Browser +โ Chrome OK +โ Firefox OK +โ Safari OK +โ Edge OK +โ Mobile browsers OK + +## Security +โ No hardcoded secrets +โ XSS protection +โ CSRF tokens (if needed) +โ Content Security Policy OK +``` + +--- + +## ๐ Common Tasks Reference + +### Add New Project Category + +```html + + + ๐งฉ Puzzle Games + Challenge your mind + + + + + + + +``` + +### Implement Custom Theme + +```css +/* custom-theme.css */ +html[data-theme="custom"] { + --primary-color: #a855f7; + --secondary-color: #ec4899; + --bg-color: #1a0033; + --surface-color: #2d1b4e; + --text-color: #f3e8ff; +} +``` + +```javascript +function applyCustomTheme() { + document.documentElement.setAttribute('data-theme', 'custom'); + localStorage.setItem('theme', 'custom'); +} +``` + +--- + +--- + +## ๐๏ธ Advanced Architecture Patterns + +### State Management Pattern + +```javascript +class StateManager { + constructor(initialState = {}) { + this.state = initialState; + this.listeners = []; + this.history = [JSON.parse(JSON.stringify(initialState))]; + this.historyIndex = 0; + } + + subscribe(listener) { + this.listeners.push(listener); + return () => { + this.listeners = this.listeners.filter(l => l !== listener); + }; + } + + setState(updates) { + this.state = { ...this.state, ...updates }; + this.history = this.history.slice(0, this.historyIndex + 1); + this.history.push(JSON.parse(JSON.stringify(this.state))); + this.historyIndex++; + + this.listeners.forEach(listener => { + listener(this.state, updates); + }); + } + + getState() { + return { ...this.state }; + } + + undo() { + if (this.historyIndex > 0) { + this.historyIndex--; + this.state = JSON.parse(JSON.stringify(this.history[this.historyIndex])); + this.notifyListeners(); + } + } + + redo() { + if (this.historyIndex < this.history.length - 1) { + this.historyIndex++; + this.state = JSON.parse(JSON.stringify(this.history[this.historyIndex])); + this.notifyListeners(); + } + } + + notifyListeners() { + this.listeners.forEach(listener => { + listener(this.state, {}); + }); + } +} + +// Usage +const appState = new StateManager({ + theme: 'dark', + score: 0, + currentProject: null +}); + +const unsubscribe = appState.subscribe((state, updates) => { + console.log('State updated:', updates); + updateUI(state); +}); + +appState.setState({ score: 100 }); +appState.undo(); +appState.redo(); +``` + +### Component Registry Pattern + +```javascript +class ComponentRegistry { + static components = new Map(); + + static register(name, component) { + if (this.components.has(name)) { + console.warn(`Component "${name}" already registered`); + } + this.components.set(name, component); + } + + static get(name) { + const component = this.components.get(name); + if (!component) { + console.error(`Component "${name}" not found`); + } + return component; + } + + static render(name, props = {}) { + const component = this.get(name); + if (component) { + return component.render(props); + } + return 'Component not found'; + } + + static exists(name) { + return this.components.has(name); + } + + static list() { + return Array.from(this.components.keys()); + } +} + +// Register components +ComponentRegistry.register('button', { + render: (props) => ` + + ${props.text} + + ` +}); + +ComponentRegistry.register('card', { + render: (props) => ` + + ${props.title} + ${props.description} + + ` +}); + +// Usage +const cardHTML = ComponentRegistry.render('card', { + title: 'Welcome', + description: 'This is a card' +}); +``` + +--- + +## ๐ API Integration Pattern + +```javascript +class APIClient { + constructor(baseURL = '/api') { + this.baseURL = baseURL; + this.defaultHeaders = { + 'Content-Type': 'application/json' + }; + this.timeout = 5000; + } + + async request(endpoint, options = {}) { + const url = `${this.baseURL}${endpoint}`; + const config = { + method: options.method || 'GET', + headers: { ...this.defaultHeaders, ...options.headers }, + timeout: options.timeout || this.timeout + }; + + if (options.body) { + config.body = JSON.stringify(options.body); + } + + try { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), config.timeout); + + const response = await fetch(url, { + ...config, + signal: controller.signal + }); + + clearTimeout(timeoutId); + + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`); + } + + return await response.json(); + } catch (error) { + console.error('API Error:', error); + throw error; + } + } + + get(endpoint, options) { + return this.request(endpoint, { ...options, method: 'GET' }); + } + + post(endpoint, body, options) { + return this.request(endpoint, { ...options, method: 'POST', body }); + } + + put(endpoint, body, options) { + return this.request(endpoint, { ...options, method: 'PUT', body }); + } + + delete(endpoint, options) { + return this.request(endpoint, { ...options, method: 'DELETE' }); + } +} + +const api = new APIClient(); + +// Usage +try { + const projects = await api.get('/projects'); + console.log('Projects:', projects); +} catch (error) { + NotificationSystem.error('Failed to load projects'); +} +``` + +--- + +## ๐งช Testing Patterns + +### Unit Test Example + +```javascript +// test-utils.js +class TestRunner { + static tests = []; + + static test(name, fn) { + this.tests.push({ name, fn }); + } + + static async run() { + let passed = 0; + let failed = 0; + + for (const test of this.tests) { + try { + await test.fn(); + console.log(`โ ${test.name}`); + passed++; + } catch (error) { + console.error(`โ ${test.name}: ${error.message}`); + failed++; + } + } + + console.log(`\n${passed} passed, ${failed} failed`); + return { passed, failed }; + } +} + +function assertEqual(actual, expected, message = '') { + if (actual !== expected) { + throw new Error(`${message}\nExpected: ${expected}\nActual: ${actual}`); + } +} + +// Tests +TestRunner.test('debounce function', () => { + let callCount = 0; + const fn = debounce(() => callCount++, 100); + + fn(); + fn(); + fn(); + + return new Promise(resolve => { + setTimeout(() => { + assertEqual(callCount, 1, 'debounce should call only once'); + resolve(); + }, 150); + }); +}); + +TestRunner.test('StateManager', () => { + const state = new StateManager({ count: 0 }); + state.setState({ count: 5 }); + assertEqual(state.getState().count, 5, 'setState should update'); +}); + +// Run all tests +TestRunner.run(); +``` + +--- + +## ๐ Error Handling Strategy + +```javascript +class ErrorHandler { + static handlers = new Map(); + + static register(errorType, handler) { + this.handlers.set(errorType, handler); + } + + static handle(error) { + const errorType = error.name || 'Error'; + const handler = this.handlers.get(errorType) || this.defaultHandler; + return handler(error); + } + + static defaultHandler(error) { + console.error('Unhandled error:', error); + NotificationSystem.error('An unexpected error occurred'); + } +} + +// Register specific error handlers +ErrorHandler.register('NetworkError', (error) => { + console.error('Network error:', error); + NotificationSystem.error('Network connection failed'); +}); + +ErrorHandler.register('ValidationError', (error) => { + console.error('Validation error:', error); + NotificationSystem.warning('Please check your input'); +}); + +ErrorHandler.register('TimeoutError', (error) => { + console.error('Request timeout:', error); + NotificationSystem.error('Request took too long. Please try again'); +}); + +// Global error handler +window.addEventListener('error', (e) => { + ErrorHandler.handle(e.error); +}); + +window.addEventListener('unhandledrejection', (e) => { + ErrorHandler.handle(e.reason); +}); +``` + +--- + +## ๐ Analytics & Logging + +```javascript +class Analytics { + static events = []; + + static track(event, data = {}) { + const entry = { + event, + data, + timestamp: Date.now(), + url: window.location.href, + userAgent: navigator.userAgent + }; + + this.events.push(entry); + this.sendEvent(entry); + } + + static sendEvent(entry) { + // Send to analytics service + // fetch('/api/analytics', { method: 'POST', body: JSON.stringify(entry) }) + console.log('Analytics event:', entry); + } + + static getEvents() { + return [...this.events]; + } + + static clearEvents() { + this.events = []; + } +} + +// Track user interactions +document.addEventListener('click', (e) => { + if (e.target.matches('button')) { + Analytics.track('button_click', { + text: e.target.textContent, + id: e.target.id + }); + } +}); + +// Track project opens +function openProjectModal(projectName) { + Analytics.track('project_opened', { project: projectName }); + // ... rest of function +} + +// Track code execution +function runCode(code) { + Analytics.track('code_executed', { codeLength: code.length }); + // ... rest of function +} +``` + +--- + +## ๐จ Advanced Styling Techniques + +### CSS Grid Masonry + +```css +.masonry-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); + gap: 2rem; + grid-auto-rows: 300px; +} + +.masonry-item { + grid-row: span 1; + background: var(--surface-color); + border-radius: 0.5rem; +} + +.masonry-item:nth-child(4n + 1) { + grid-row: span 2; +} +``` + +### CSS Gradient Animations + +```css +@keyframes gradientShift { + 0% { background-position: 0% 50%; } + 50% { background-position: 100% 50%; } + 100% { background-position: 0% 50%; } +} + +.gradient-bg { + background: linear-gradient( + -45deg, + var(--primary-color), + var(--secondary-color), + var(--warning-color), + var(--success-color) + ); + background-size: 400% 400%; + animation: gradientShift 8s ease infinite; +} +``` + +### CSS Backdrop Filter + +```css +.blur-backdrop { + backdrop-filter: blur(10px); + background: rgba(0, 0, 0, 0.3); +} + +.frosted-glass { + background: rgba(255, 255, 255, 0.1); + backdrop-filter: blur(10px); + border: 1px solid rgba(255, 255, 255, 0.2); + border-radius: 0.75rem; +} +``` + +--- + +## ๐ Routing Pattern + +```javascript +class Router { + constructor() { + this.routes = new Map(); + this.currentRoute = null; + this.history = []; + } + + register(path, handler) { + this.routes.set(path, handler); + } + + async navigate(path) { + const handler = this.routes.get(path); + if (!handler) { + console.error(`No route for: ${path}`); + return false; + } + + this.history.push(this.currentRoute); + this.currentRoute = path; + + try { + const content = await handler(); + this.render(content); + return true; + } catch (error) { + console.error('Navigation error:', error); + return false; + } + } + + render(content) { + const app = document.getElementById('app'); + if (app) { + app.innerHTML = content; + } + } + + back() { + if (this.history.length > 0) { + const path = this.history.pop(); + this.navigate(path); + } + } +} + +// Usage +const router = new Router(); + +router.register('/', async () => { + return 'Home'; +}); + +router.register('/projects', async () => { + const projects = await api.get('/projects'); + return `Projects${projects.map(p => p.name).join(', ')}`; +}); + +router.navigate('/'); +``` + +--- + +## ๐ฏ Focus Management + +```javascript +class FocusManager { + static trap(element) { + const focusableElements = element.querySelectorAll( + 'a, button, input, textarea, select, [tabindex]:not([tabindex="-1"])' + ); + + const firstElement = focusableElements[0]; + const lastElement = focusableElements[focusableElements.length - 1]; + + element.addEventListener('keydown', (e) => { + if (e.key !== 'Tab') return; + + if (e.shiftKey) { + if (document.activeElement === firstElement) { + e.preventDefault(); + lastElement.focus(); + } + } else { + if (document.activeElement === lastElement) { + e.preventDefault(); + firstElement.focus(); + } + } + }); + } + + static restore(element) { + element.focus(); + } + + static findFocusableElement(element) { + const focusable = element.querySelector('a, button, input, textarea, select'); + return focusable; + } +} + +// Usage in modal +FocusManager.trap(modal); +``` + +--- + +## ๐ Internationalization (i18n) + +```javascript +class I18n { + static translations = {}; + static currentLocale = 'en'; + + static register(locale, messages) { + this.translations[locale] = messages; + } + + static setLocale(locale) { + if (this.translations[locale]) { + this.currentLocale = locale; + localStorage.setItem('locale', locale); + this.updateDOM(); + } + } + + static t(key) { + const messages = this.translations[this.currentLocale] || {}; + return messages[key] || key; + } + + static updateDOM() { + document.querySelectorAll('[data-i18n]').forEach(el => { + const key = el.dataset.i18n; + el.textContent = this.t(key); + }); + } +} + +// Register translations +I18n.register('en', { + 'project_title': 'Python Mini Projects', + 'play_button': 'Play Now', + 'projects_label': 'Projects' +}); + +I18n.register('es', { + 'project_title': 'Mini Proyectos Python', + 'play_button': 'Jugar Ahora', + 'projects_label': 'Proyectos' +}); + +// Usage in HTML +document.querySelector('h1').setAttribute('data-i18n', 'project_title'); +I18n.setLocale('en'); +``` + +--- + +## ๐ฑ Touch & Gesture Handling + +```javascript +class GestureHandler { + static handleSwipe(element, callbacks) { + let startX = 0; + let startY = 0; + + element.addEventListener('touchstart', (e) => { + startX = e.touches[0].clientX; + startY = e.touches[0].clientY; + }); + + element.addEventListener('touchend', (e) => { + const endX = e.changedTouches[0].clientX; + const endY = e.changedTouches[0].clientY; + + const deltaX = endX - startX; + const deltaY = endY - startY; + + if (Math.abs(deltaX) > Math.abs(deltaY)) { + if (deltaX > 50 && callbacks.onSwipeRight) { + callbacks.onSwipeRight(); + } else if (deltaX < -50 && callbacks.onSwipeLeft) { + callbacks.onSwipeLeft(); + } + } + }); + } + + static handlePinch(element, callback) { + let lastDistance = 0; + + element.addEventListener('touchmove', (e) => { + if (e.touches.length !== 2) return; + + const dx = e.touches[0].clientX - e.touches[1].clientX; + const dy = e.touches[0].clientY - e.touches[1].clientY; + const distance = Math.sqrt(dx * dx + dy * dy); + + if (lastDistance > 0) { + const scale = distance / lastDistance; + callback(scale); + } + + lastDistance = distance; + }); + } +} +``` + +--- + +## ๐ Utility Functions Library + +```javascript +// String utilities +const StringUtils = { + capitalize: (str) => str.charAt(0).toUpperCase() + str.slice(1), + slug: (str) => str.toLowerCase().replace(/\s+/g, '-'), + truncate: (str, len = 50) => str.length > len ? str.slice(0, len) + '...' : str, + escape: (str) => str.replace(/[&<>"']/g, (m) => ({ + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''' + }[m])) +}; + +// Array utilities +const ArrayUtils = { + shuffle: (arr) => [...arr].sort(() => Math.random() - 0.5), + unique: (arr) => [...new Set(arr)], + chunk: (arr, size) => Array.from({length: Math.ceil(arr.length / size)}, (_, i) => arr.slice(i * size, i * size + size)), + flatten: (arr) => arr.flat(Infinity), + groupBy: (arr, key) => arr.reduce((acc, item) => { + const group = item[key]; + if (!acc[group]) acc[group] = []; + acc[group].push(item); + return acc; + }, {}) +}; + +// Math utilities +const MathUtils = { + random: (min, max) => Math.floor(Math.random() * (max - min + 1)) + min, + clamp: (val, min, max) => Math.min(Math.max(val, min), max), + lerp: (a, b, t) => a + (b - a) * t, + distance: (x1, y1, x2, y2) => Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2) +}; +``` + +--- + +## ๐ Security Best Practices + +```javascript +// Sanitize user input +function sanitizeHTML(html) { + const temp = document.createElement('div'); + temp.textContent = html; + return temp.innerHTML; +} + +// Validate email +function isValidEmail(email) { + return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); +} + +// Validate URL +function isValidURL(url) { + try { + new URL(url); + return true; + } catch { + return false; + } +} + +// Prevent XSS +function createElement(tag, attrs = {}, content = '') { + const el = document.createElement(tag); + Object.entries(attrs).forEach(([key, value]) => { + if (key.startsWith('on')) { + console.warn('Event attributes not allowed'); + return; + } + el.setAttribute(key, value); + }); + el.textContent = content; + return el; +} +``` + +--- + +## ๐ Performance Stats + +| Operation | Time | +|-----------|------| +| First Pyodide load | 3โ5s | +| Cached Pyodide | 100โ300ms | +| Modal animation | 300ms | +| Theme transition | 300ms | +| Search debounce | 300ms | +| Page rendering | < 100ms | +| Keyboard response | < 16ms | + +--- + +## ๏ฟฝ Complete Folder Structure Walkthrough + +``` +web-app/ +โ +โโโ ๐ index.html (Homepage) +โ โโโ Hero section with CTA buttons +โ โโโ Featured projects carousel +โ โโโ Category navigation +โ โโโ Search bar +โ โโโ Theme toggle button +โ +โโโ ๐ games.html (Games Category) +โ โโโ Page header (๐ฎ Games) +โ โโโ Filter by difficulty +โ โโโ Sort options (newest, popular) +โ โโโ Projects grid (dynamic) +โ โโโ Pagination controls +โ +โโโ ๐ math.html (Math Category) +โ โโโ Page header (๐ข Math) +โ โโโ Topic filter +โ โโโ Projects grid +โ โโโ Learning resources +โ +โโโ ๐ utilities.html (Utilities Category) +โ โโโ Page header (โ๏ธ Utilities) +โ โโโ Use-case categories +โ โโโ Projects grid +โ โโโ Integration guides +โ +โโโ ๐ css/ +โ โโโ ๐ styles.css (45+ KB) +โ โโโ Root CSS variables +โ โโโ Dark theme definitions +โ โโโ Light theme definitions +โ โโโ Global reset and base styles +โ โโโ Layout system (grid, flex) +โ โโโ Component styles +โ โ โโโ .btn (button variants) +โ โ โโโ .card (project cards) +โ โ โโโ .modal (dialog) +โ โ โโโ .navbar (navigation) +โ โ โโโ ...more components +โ โโโ Animation keyframes +โ โ โโโ @keyframes fadeIn +โ โ โโโ @keyframes slideUp +โ โ โโโ @keyframes pulse +โ โ โโโ @keyframes spin +โ โ โโโ ...more animations +โ โโโ Responsive breakpoints +โ โ โโโ @media (min-width: 640px) +โ โ โโโ @media (min-width: 768px) +โ โ โโโ @media (min-width: 1024px) +โ โ โโโ @media (min-width: 1440px) +โ โโโ Accessibility (@media (prefers-reduced-motion)) +โ +โโโ ๐ js/ +โ โโโ ๐ main.js (15 KB - Orchestration) +โ โ โโโ DOMContentLoaded initialization +โ โ โโโ Event listener setup +โ โ โโโ Theme toggle implementation +โ โ โโโ Modal control (open, close, focus trap) +โ โ โโโ Search filter logic (debounced) +โ โ โโโ Project sorting (by date, popularity) +โ โ โโโ Keyboard navigation +โ โ โโโ Accessibility utilities +โ โ +โ โโโ ๐ playground.js (8 KB - Pyodide Interface) +โ โ โโโ Pyodide initialization +โ โ โโโ Web Worker spawning +โ โ โโโ Message protocol handling +โ โ โโโ Code execution UI +โ โ โโโ Output display +โ โ โโโ Error handling +โ โ โโโ Infinite loop protection (worker terminate) +โ โ โโโ Performance monitoring +โ โ +โ โโโ ๐ playground-worker.js (3 KB - Web Worker) +โ โ โโโ Pyodide library loading +โ โ โโโ Python code execution +โ โ โโโ Stdout/stderr capture +โ โ โโโ Message event listener +โ โ โโโ Result transmission to main +โ โ โโโ Error propagation +โ โ +โ โโโ ๐ projects.js (120 KB - Project Registry) +โ โ โโโ Project data structure definition +โ โ โโโ All 47+ projects listed +โ โ โ โโโ Snake Game +โ โ โ โโโ Rock Paper Scissors +โ โ โ โโโ Hangman +โ โ โ โโโ Blackjack +โ โ โ โโโ Tic Tac Toe +โ โ โ โโโ Guess Number +โ โ โ โโโ Quiz Game +โ โ โ โโโ Math Functions +โ โ โ โโโ Unit Converter +โ โ โ โโโ Fibonacci Generator +โ โ โ โโโ Prime Number Checker +โ โ โ โโโ ...and many more +โ โ โโโ Project HTML templates +โ โ โโโ getProjectHTML() function +โ โ โโโ Project initialization handlers +โ โ +โ โโโ ๐ storage.js (2 KB - localStorage Wrapper) +โ โ โโโ saveToStorage(key, value) +โ โ โโโ loadFromStorage(key, defaultValue) +โ โ โโโ removeFromStorage(key) +โ โ โโโ Error handling +โ โ โโโ JSON serialization/deserialization +โ โ +โ โโโ ๐ audio.js (3 KB - Sound Effects) +โ โ โโโ AudioController class +โ โ โโโ Sound effect registry +โ โ โโโ play() method +โ โ โโโ toggleMute() method +โ โ โโโ Volume control +โ โ โโโ Browser audio API integration +โ โ +โ โโโ ๐ projects/ (Deprecated - migrated to projects.js) +โ โโโ Individual project files (older structure) +โ +โโโ ๐ assets/ + โโโ favicon.svg (Branding) + โโโ logo.png (Application logo) + โโโ sun.svg (Light theme icon) + โโโ moon.svg (Dark theme icon) + โโโ background.png (Hero background) + โโโ pattern.png (Decorative patterns) + โโโ ๐ images/ + โโโ category-games.png + โโโ category-math.png + โโโ category-utilities.png + โโโ ...project-specific images +``` + +### File Size Breakdown + +| File | Size | Purpose | +|------|------|---------| +| styles.css | 45 KB | All styling | +| projects.js | 120 KB | 47+ projects | +| main.js | 15 KB | Core logic | +| playground.js | 8 KB | Pyodide | +| Total JS | ~160 KB | Functionality | +| Total CSS | 45 KB | Styling | +| **Total** | **~210 KB** | Full app | + +--- + +## ๐ Performance Profiling + +### Chrome DevTools Walkthrough + +1. **Open DevTools**: `F12` or `Ctrl+Shift+I` +2. **Performance Tab**: + ``` + โข Click Record + โข Interact with app + โข Click Stop + โข Analyze timeline + โข Look for jank (yellow/red frames) + ``` + +3. **Network Tab**: + ``` + โข Monitor resource loading + โข Check Pyodide CDN (3-5s first load) + โข Verify caching (100-300ms cached) + ``` + +4. **Console Tab**: + ``` + โข Check for errors (should be none) + โข Check for warnings (minimize) + โข Monitor analytics events + ``` + +5. **Sources Tab**: + ``` + โข Set breakpoints in code + โข Step through execution + โข Inspect variable values + โข Monitor scope + ``` + +### Performance Metrics + +```javascript +// Measure operations +performance.mark('operation-start'); +// ... code to measure +performance.mark('operation-end'); +performance.measure('operation', 'operation-start', 'operation-end'); + +const measure = performance.getEntriesByName('operation')[0]; +console.log(`Duration: ${measure.duration.toFixed(2)}ms`); +``` + +--- + +## ๐ ๏ธ Troubleshooting Guide + +### Common Issues & Solutions + +| Issue | Cause | Solution | +|-------|-------|----------| +| Modal doesn't open | Event listener not attached | Check main.js modal setup | +| Pyodide timeout | First load slow | Use cached version | +| Layout broken | Responsive CSS issue | Check breakpoints | +| Theme doesn't apply | localStorage issue | Clear storage & refresh | +| Keyboard nav broken | Focus trap issue | Check FocusManager | +| Search doesn't filter | Event not debounced | Verify debounce timing | +| Styles not loading | CSS file path wrong | Check CSS link href | +| Animations laggy | GPU not used | Use transform/opacity | +| Memory leak | Event listeners not removed | Cleanup on destroy | +| Console errors | Syntax or reference errors | Check line numbers | + +### Debugging Workflow + +```markdown +1. Open DevTools (F12) +2. Check Console tab + - Look for red errors + - Check error message + - Note line number +3. Go to Sources tab + - Click file name from error + - Set breakpoint at error line +4. Trigger the error + - Interact with app + - Debugger will pause +5. Inspect variables + - Hover over variables + - Check scope panel +6. Step through code + - Use step over (F10) + - Use step into (F11) +7. Fix issue + - Update code + - Refresh page (F5) + - Test again +``` + +--- + +## ๐ Browser DevTools Features + +### Lighthouse Audit + +``` +1. Open DevTools โ Lighthouse tab +2. Select categories: + - Performance + - Accessibility + - Best Practices + - SEO +3. Click "Analyze page load" +4. Review suggestions +5. Implement improvements +6. Re-audit to verify +``` + +**Target Scores:** +- Performance: > 90 +- Accessibility: 100 +- Best Practices: > 90 +- SEO: 100 + +### CSS Debugging + +```javascript +// Highlight all elements using CSS variables +Array.from(document.querySelectorAll('*')).forEach(el => { + const styles = window.getComputedStyle(el); + if (styles.color.includes('var(')) { + el.style.outline = '1px solid red'; + } +}); + +// Check computed styles +const element = document.getElementById('myElement'); +const computed = window.getComputedStyle(element); +console.log('Background:', computed.backgroundColor); +console.log('Font Size:', computed.fontSize); +``` + +--- + +## ๐ Version Control & Git Workflow + +### Commit Message Format + +``` +feat: add new component +fix: resolve modal animation bug +docs: update accessibility section +style: format code with prettier +perf: optimize search debounce +test: add unit tests for storage +chore: update dependencies + +Format: [type]: [description] +``` + +### Branch Naming + +``` +feature/component-name +bugfix/issue-description +docs/documentation-update +hotfix/critical-issue +``` + +### Pre-commit Checklist + +```markdown +โ Code is formatted +โ No console errors +โ No console warnings +โ Accessibility tested +โ Mobile responsive +โ Dark/light themes work +โ Keyboard navigation works +โ Tests pass (if applicable) +``` + +--- + +## ๐ Integration Patterns + +### Firebase Integration Example + +```javascript +class FirebaseService { + constructor(config) { + // Initialize Firebase + this.db = firebase.database(); + this.auth = firebase.auth(); + } + + async saveProjectProgress(userId, projectName, progress) { + try { + await this.db.ref(`users/${userId}/progress/${projectName}`).set({ + progress, + timestamp: Date.now() + }); + return true; + } catch (error) { + console.error('Save failed:', error); + return false; + } + } + + async loadProjectProgress(userId, projectName) { + try { + const snapshot = await this.db.ref( + `users/${userId}/progress/${projectName}` + ).once('value'); + return snapshot.val(); + } catch (error) { + console.error('Load failed:', error); + return null; + } + } +} +``` + +--- + +## ๐ Analytics Implementation + +```javascript +class AdvancedAnalytics { + static sessionStart = Date.now(); + static pageViews = 0; + static interactions = []; + + static init() { + window.addEventListener('beforeunload', () => { + this.trackSession(); + }); + + document.addEventListener('click', (e) => { + this.trackInteraction(e); + }); + } + + static trackPageView(page) { + this.pageViews++; + Analytics.track('page_view', { + page, + timestamp: Date.now(), + referrer: document.referrer + }); + } + + static trackInteraction(event) { + const element = event.target; + this.interactions.push({ + element: element.tagName, + class: element.className, + text: element.textContent?.substring(0, 50) + }); + } + + static trackSession() { + const sessionDuration = Date.now() - this.sessionStart; + Analytics.track('session_end', { + duration: sessionDuration, + pageViews: this.pageViews, + interactions: this.interactions.length + }); + } +} + +AdvancedAnalytics.init(); +``` + +--- + +## ๐ Learning Resources by Topic + +### JavaScript Fundamentals +- MDN: [JavaScript Basics](https://developer.mozilla.org/en-US/docs/Learn/JavaScript) +- Practice: [freeCodeCamp JS Course](https://www.freecodecamp.org/learn/javascript-algorithms-and-data-structures/) + +### Web APIs +- DOM API: [MDN Document](https://developer.mozilla.org/en-US/docs/Web/API/Document) +- Fetch API: [MDN Fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) +- Web Workers: [MDN Workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) + +### CSS Advanced +- Grid: [CSS Tricks Grid Guide](https://css-tricks.com/snippets/css/complete-guide-grid/) +- Flexbox: [CSS Tricks Flexbox Guide](https://css-tricks.com/snippets/css/a-guide-to-flexbox/) +- Animations: [MDN Animations](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations) + +### Accessibility +- WCAG 2.1: [W3C WCAG](https://www.w3.org/WAI/WCAG21/quickref/) +- ARIA: [WAI-ARIA Practices](https://www.w3.org/WAI/ARIA/apg/) +- Keyboard Nav: [WebAIM Keyboard](https://webaim.org/articles/keyboard/) + +### Performance +- Core Web Vitals: [web.dev metrics](https://web.dev/metrics/) +- Lighthouse: [web.dev Lighthouse](https://web.dev/lighthouse/) +- Chrome DevTools: [Chrome DevTools Guide](https://developer.chrome.com/docs/devtools/) + +### Python & Pyodide +- Python Docs: [python.org](https://www.python.org/doc/) +- Pyodide: [pyodide.org](https://pyodide.org/) +- Jupyter: [jupyter.org](https://jupyter.org/) + +--- + +## ๐ฏ Optimization Checklist + +```markdown +## Code +โ Unused variables removed +โ Dead code removed +โ Functions < 50 lines +โ No console.log() in production +โ Error handling present +โ Comments for complex logic + +## Performance +โ Images optimized +โ CSS critical path +โ Lazy load images +โ Defer non-critical JS +โ Cache static assets +โ Minify production code + +## Accessibility +โ Semantic HTML +โ Keyboard navigation +โ Focus visible +โ Color contrast OK +โ ARIA labels present +โ Alt text on images + +## Testing +โ Happy path tested +โ Error cases handled +โ Edge cases considered +โ Mobile tested +โ Cross-browser tested +โ Accessibility tested + +## Documentation +โ README updated +โ Code comments clear +โ API documented +โ Troubleshooting guide +โ Contributing guide +โ Examples provided +``` + +--- + +## ๐ Advanced Topics + +### Web Components + +```javascript +class ProjectCard extends HTMLElement { + constructor() { + super(); + this.attachShadow({ mode: 'open' }); + } + + connectedCallback() { + this.render(); + } + + render() { + this.shadowRoot.innerHTML = ` + + + ${this.getAttribute('title')} + ${this.getAttribute('description')} + + `; + } +} + +customElements.define('project-card', ProjectCard); + +// Usage +const card = document.createElement('project-card'); +card.setAttribute('title', 'Snake Game'); +card.setAttribute('description', 'Classic snake game'); +document.body.appendChild(card); +``` + +### Service Workers + +```javascript +// service-worker.js +self.addEventListener('install', () => { + caches.open('v1').then(cache => { + cache.addAll([ + '/', + '/css/styles.css', + '/js/main.js', + '/assets/logo.png' + ]); + }); +}); + +self.addEventListener('fetch', (event) => { + event.respondWith( + caches.match(event.request).then((response) => { + return response || fetch(event.request); + }) + ); +}); +``` + +--- + + + +### ๐ก Questions? + +[GitHub Issues](https://github.com/steam-bell-92/python-mini-project/issues) | [Discussions](https://github.com/steam-bell-92/python-mini-project/discussions) + +**MIT Licensed** | Made with โค๏ธ by Python Mini Projects team + +v1.3.0 | Updated 2024 + +