feat: youtube preview
This commit is contained in:
169
specs/002-youtube-design-preview/analysis.md
Normal file
169
specs/002-youtube-design-preview/analysis.md
Normal file
@@ -0,0 +1,169 @@
|
||||
# Cross-Artifact Consistency Analysis
|
||||
|
||||
**Feature**: `002-youtube-design-preview`
|
||||
**Analyzed**: 2026-01-29
|
||||
**Artifacts**: spec.md, plan.md, research.md, data-model.md, contracts/types.ts, tasks.md
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Total Requirements (FR) | 20 |
|
||||
| Total User Stories | 4 |
|
||||
| Total Tasks | 50 |
|
||||
| Requirements Covered | 20/20 (100%) |
|
||||
| Constitution Alignment | PASS |
|
||||
| Critical Issues | 0 |
|
||||
| Warnings | 3 |
|
||||
| Suggestions | 4 |
|
||||
|
||||
**Overall Status**: Ready for implementation
|
||||
|
||||
---
|
||||
|
||||
## Findings
|
||||
|
||||
| ID | Category | Severity | Location | Summary | Recommendation |
|
||||
|----|----------|----------|----------|---------|----------------|
|
||||
| A-001 | Underspecification | Warning | spec.md:FR-017 | Weekly screenshot capture mechanism not detailed | Add cron/scheduler details to plan.md or tasks.md |
|
||||
| A-002 | Inconsistency | Warning | tasks.md:T041 | Script path uses `.ts` but may need to be executable | Verify ts-node setup or use compiled JS for scheduler |
|
||||
| A-003 | Coverage Gap | Warning | tasks.md | FR-009 (responsive breakpoints) only partially addressed by T043 | Consider adding explicit breakpoint tests |
|
||||
| A-004 | Suggestion | Info | data-model.md | `publishedAt` stored as Date but templates show "X days ago" | Add utility function for relative time formatting |
|
||||
| A-005 | Suggestion | Info | contracts/types.ts | `VALIDATION_CONSTANTS.DURATION_REGEX` allows single-digit minutes | Consider stricter format `^\d{1,3}:\d{2}$` for edge cases |
|
||||
| A-006 | Suggestion | Info | research.md | Sidebar dimensions (168x94) have different ratio than 16:9 | Document this intentional deviation from standard 16:9 |
|
||||
| A-007 | Suggestion | Info | tasks.md | T017 hover states may conflict with mobile (no hover on touch) | Add note to disable hover states in mobile variant |
|
||||
|
||||
---
|
||||
|
||||
## Requirement Coverage Matrix
|
||||
|
||||
| Requirement | User Story | Tasks | Status |
|
||||
|-------------|------------|-------|--------|
|
||||
| FR-001 (Desktop pixel-perfect) | US1 | T012-T020 | Covered |
|
||||
| FR-002 (Mobile pixel-perfect) | US2 | T021-T026 | Covered |
|
||||
| FR-003 (Sidebar pixel-perfect) | US3 | T027-T033 | Covered |
|
||||
| FR-004 (Real-time upload) | US1-3 | T010-T011 | Covered |
|
||||
| FR-005 (Metadata elements) | US1-3 | T012-T016, T020-T032 | Covered |
|
||||
| FR-006 (Typography) | US1-3 | T013-T014, T023, T029 | Covered |
|
||||
| FR-007 (Spacing/padding) | US1-3 | T018, T024, T031 | Covered |
|
||||
| FR-008 (Hover states) | US1 | T017 | Covered |
|
||||
| FR-009 (Responsive breakpoints) | - | T043 | Partial |
|
||||
| FR-010 (Playwright integration) | US4 | T034-T042 | Covered |
|
||||
| FR-011 (Visual diff reports) | US4 | T035 | Covered |
|
||||
| FR-012 (Customizable metadata) | US1 | T020 | Covered |
|
||||
| FR-013 (Duration badge) | US1-3 | T016 | Covered |
|
||||
| FR-014 (Channel avatar) | US1-2 | T015, T022, T032 | Covered |
|
||||
| FR-015 (Local storage) | - | T004, T006-T007, T047 | Covered |
|
||||
| FR-016 (Theme toggle) | - | T008, T019 | Covered |
|
||||
| FR-017 (Weekly capture) | US4 | T041 | Covered |
|
||||
| FR-018 (Historical screenshots) | US4 | T036 | Covered |
|
||||
| FR-019 (5MB limit) | - | T010-T011 | Covered |
|
||||
| FR-020 (Image formats) | - | T010-T011 | Covered |
|
||||
|
||||
---
|
||||
|
||||
## Constitution Alignment Check
|
||||
|
||||
| Principle | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| I. Tech Stack | PASS | React 19.x, TS 5.x, Tailwind 4.x, shadcn/ui, Zustand - all aligned |
|
||||
| II. Architecture | PASS | Feature code in `frontend/src/`, correct directory structure |
|
||||
| III. Styling | PASS | YouTube CSS variables defined, Tailwind utilities used |
|
||||
| IV. Data Management | PASS | Zustand for state, localStorage for persistence |
|
||||
| V. Development Practices | PASS | TypeScript strict, ESLint, explicit types in contracts |
|
||||
|
||||
**No constitution violations detected.**
|
||||
|
||||
---
|
||||
|
||||
## Duplication Analysis
|
||||
|
||||
| Area | Finding |
|
||||
|------|---------|
|
||||
| Type Definitions | `contracts/types.ts` and `data-model.md` contain same interfaces - **Acceptable** (one is documentation, one is code) |
|
||||
| CSS Values | `research.md` and `quickstart.md` both list CSS values - **Minor overlap**, quickstart is intentional quick reference |
|
||||
| Default Values | `data-model.md` and `contracts/types.ts` both define defaults - **Need to keep in sync** |
|
||||
|
||||
**Recommendation**: Consider generating `data-model.md` from `contracts/types.ts` to prevent drift.
|
||||
|
||||
---
|
||||
|
||||
## Ambiguity Detection
|
||||
|
||||
| Location | Ambiguity | Resolution |
|
||||
|----------|-----------|------------|
|
||||
| spec.md:FR-017 | "Automated weekly capture" - when exactly? | Suggest: Sunday 00:00 UTC, document in tasks.md |
|
||||
| spec.md:SC-004 | "90% of users" - how to measure in blind test? | This is a qualitative metric, may be hard to automate |
|
||||
| tasks.md:T036 | "Copy reference screenshots" - from where? | From `specs/002-youtube-design-preview/reference/` (clarified in research.md) |
|
||||
|
||||
---
|
||||
|
||||
## Task Dependency Analysis
|
||||
|
||||
### Critical Path
|
||||
|
||||
```
|
||||
Phase 1 (Setup) → Phase 2 (Foundational) → Phase 3-6 (User Stories) → Phase 7 (Polish)
|
||||
T001-T005 [parallel] T006-T011 [sequential] US1-US4 [parallel] T043-T050 [parallel]
|
||||
```
|
||||
|
||||
### Blocking Dependencies
|
||||
|
||||
| Task | Blocked By | Blocks |
|
||||
|------|------------|--------|
|
||||
| T006-T011 | T001-T005 | T012-T050 (all user stories) |
|
||||
| T037-T040 | T034-T036 | None (test tasks) |
|
||||
| T049 (lint) | All implementation | T050 (validation) |
|
||||
|
||||
### Parallel Opportunities
|
||||
|
||||
- **Phase 1**: T001, T002, T003, T004 can all run in parallel
|
||||
- **Phase 2**: T008, T009 can run in parallel after T006-T007
|
||||
- **Phase 3-5**: US1, US2, US3 can be implemented in parallel by different developers
|
||||
- **Phase 6**: T034, T035, T036 can run in parallel (test setup)
|
||||
- **Phase 7**: T043, T044, T045, T046, T047 can all run in parallel
|
||||
|
||||
---
|
||||
|
||||
## Risk Assessment
|
||||
|
||||
| Risk | Likelihood | Impact | Mitigation |
|
||||
|------|------------|--------|------------|
|
||||
| YouTube design changes during development | Medium | High | Reference screenshots versioned, weekly updates |
|
||||
| Visual test flakiness | Medium | Medium | 98% threshold allows minor variance |
|
||||
| localStorage quota exceeded | Low | Medium | T047 adds 10-thumbnail limit |
|
||||
| Playwright MCP unavailable | Low | High | Manual screenshot capture as fallback |
|
||||
|
||||
---
|
||||
|
||||
## Recommendations
|
||||
|
||||
### High Priority
|
||||
|
||||
1. **Clarify weekly capture schedule**: Add specific timing (e.g., "Sundays at 00:00 UTC") to avoid ambiguity
|
||||
2. **Add responsive test coverage**: T043 covers responsive breakpoints but no visual tests for intermediate sizes
|
||||
|
||||
### Medium Priority
|
||||
|
||||
3. **Consider relative time utility**: Add `formatRelativeTime(date: Date): string` to lib functions
|
||||
4. **Document sidebar ratio**: Note in research.md that sidebar uses slightly different aspect ratio
|
||||
|
||||
### Low Priority
|
||||
|
||||
5. **Sync default values**: Ensure `data-model.md` defaults match `contracts/types.ts`
|
||||
6. **Mobile hover handling**: Explicitly disable hover states for mobile variant to avoid touch UX issues
|
||||
|
||||
---
|
||||
|
||||
## Next Actions
|
||||
|
||||
1. Address Warning items (A-001, A-002, A-003) before implementation begins
|
||||
2. Proceed with Phase 1 (Setup) - all tasks can run in parallel
|
||||
3. After Phase 2 completion, checkpoint to verify foundation before user stories
|
||||
4. Consider running US1-US3 in parallel if multiple developers available
|
||||
|
||||
---
|
||||
|
||||
**Analysis complete. Feature is ready for implementation with minor clarifications recommended.**
|
||||
37
specs/002-youtube-design-preview/checklists/requirements.md
Normal file
37
specs/002-youtube-design-preview/checklists/requirements.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# Specification Quality Checklist: YouTube Design Preview Replica
|
||||
|
||||
**Purpose**: Validate specification completeness and quality before proceeding to planning
|
||||
**Created**: 2026-01-29
|
||||
**Feature**: [spec.md](../spec.md)
|
||||
|
||||
## Content Quality
|
||||
|
||||
- [x] No implementation details (languages, frameworks, APIs)
|
||||
- [x] Focused on user value and business needs
|
||||
- [x] Written for non-technical stakeholders
|
||||
- [x] All mandatory sections completed
|
||||
|
||||
## Requirement Completeness
|
||||
|
||||
- [x] No [NEEDS CLARIFICATION] markers remain
|
||||
- [x] Requirements are testable and unambiguous
|
||||
- [x] Success criteria are measurable
|
||||
- [x] Success criteria are technology-agnostic (no implementation details)
|
||||
- [x] All acceptance scenarios are defined
|
||||
- [x] Edge cases are identified
|
||||
- [x] Scope is clearly bounded
|
||||
- [x] Dependencies and assumptions identified
|
||||
|
||||
## Feature Readiness
|
||||
|
||||
- [x] All functional requirements have clear acceptance criteria
|
||||
- [x] User scenarios cover primary flows
|
||||
- [x] Feature meets measurable outcomes defined in Success Criteria
|
||||
- [x] No implementation details leak into specification
|
||||
|
||||
## Notes
|
||||
|
||||
- All items passed validation
|
||||
- Spec is ready for `/speckit.clarify` or `/speckit.plan`
|
||||
- Playwright MCP referenced for visual testing methodology (not implementation detail)
|
||||
- Reference baseline defined as YouTube's current design (January 2026)
|
||||
278
specs/002-youtube-design-preview/contracts/types.ts
Normal file
278
specs/002-youtube-design-preview/contracts/types.ts
Normal file
@@ -0,0 +1,278 @@
|
||||
/**
|
||||
* YouTube Design Preview - TypeScript Contracts
|
||||
*
|
||||
* These interfaces define the data structures used in the preview tool.
|
||||
* This file serves as the contract between components.
|
||||
*/
|
||||
|
||||
// ============================================================================
|
||||
// Enums
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Preview display modes matching YouTube's layout variations
|
||||
*/
|
||||
export type PreviewMode = 'desktop' | 'sidebar' | 'mobile';
|
||||
|
||||
/**
|
||||
* Theme modes matching YouTube's color schemes
|
||||
*/
|
||||
export type ThemeMode = 'light' | 'dark';
|
||||
|
||||
/**
|
||||
* Supported image formats for thumbnail upload
|
||||
*/
|
||||
export type SupportedImageFormat = 'image/jpeg' | 'image/png' | 'image/webp';
|
||||
|
||||
// ============================================================================
|
||||
// Core Entities
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Video metadata displayed alongside the thumbnail
|
||||
*/
|
||||
export interface VideoMetadata {
|
||||
/** Video title (1-100 characters) */
|
||||
title: string;
|
||||
|
||||
/** Channel name (1-50 characters) */
|
||||
channelName: string;
|
||||
|
||||
/** Optional channel avatar URL or base64 data URL */
|
||||
channelAvatarUrl?: string;
|
||||
|
||||
/** Duration in format "MM:SS" or "H:MM:SS" */
|
||||
duration: string;
|
||||
|
||||
/** View count as raw number (formatted for display) */
|
||||
viewCount: number;
|
||||
|
||||
/** Publication date (used for relative time display) */
|
||||
publishedAt: Date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploaded thumbnail image with metadata
|
||||
*/
|
||||
export interface UploadedThumbnail {
|
||||
/** Unique identifier (UUID) */
|
||||
id: string;
|
||||
|
||||
/** Base64 data URL of the image */
|
||||
dataUrl: string;
|
||||
|
||||
/** Original filename */
|
||||
originalName: string;
|
||||
|
||||
/** File size in bytes (max 5MB = 5,242,880) */
|
||||
fileSize: number;
|
||||
|
||||
/** MIME type */
|
||||
mimeType: SupportedImageFormat;
|
||||
|
||||
/** Original image dimensions */
|
||||
width: number;
|
||||
height: number;
|
||||
|
||||
/** Upload timestamp */
|
||||
uploadedAt: Date;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// State Management
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Complete preview state persisted to localStorage
|
||||
*/
|
||||
export interface PreviewState {
|
||||
/** Currently active thumbnail for preview */
|
||||
currentThumbnail: UploadedThumbnail | null;
|
||||
|
||||
/** Active preview layout mode */
|
||||
previewMode: PreviewMode;
|
||||
|
||||
/** Active color theme */
|
||||
themeMode: ThemeMode;
|
||||
|
||||
/** Video metadata for display */
|
||||
metadata: VideoMetadata;
|
||||
|
||||
/** Recently used thumbnails (max 10) */
|
||||
recentThumbnails: UploadedThumbnail[];
|
||||
|
||||
/** UI state: metadata editor visibility */
|
||||
showMetadataEditor: boolean;
|
||||
|
||||
/** Last persistence timestamp */
|
||||
lastSaved: Date;
|
||||
|
||||
/** Schema version for migrations */
|
||||
version: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* localStorage serialization wrapper
|
||||
*/
|
||||
export interface LocalStorageSchema {
|
||||
version: 1;
|
||||
state: PreviewState;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Visual Testing
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Result of a visual comparison test
|
||||
*/
|
||||
export interface VisualTestResult {
|
||||
/** Unique test run identifier */
|
||||
testId: string;
|
||||
|
||||
/** Preview mode being tested */
|
||||
previewMode: PreviewMode;
|
||||
|
||||
/** Theme mode being tested */
|
||||
themeMode: ThemeMode;
|
||||
|
||||
/** Test execution timestamp */
|
||||
capturedAt: Date;
|
||||
|
||||
/** Similarity percentage (0-100) */
|
||||
matchPercentage: number;
|
||||
|
||||
/** Pass/fail based on 98% threshold */
|
||||
passed: boolean;
|
||||
|
||||
/** Path to captured screenshot */
|
||||
actualScreenshotPath: string;
|
||||
|
||||
/** Path to reference screenshot */
|
||||
expectedScreenshotPath: string;
|
||||
|
||||
/** Path to diff image (only if failed) */
|
||||
diffImagePath?: string;
|
||||
|
||||
/** Error message if test failed */
|
||||
errorMessage?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* YouTube reference screenshot metadata
|
||||
*/
|
||||
export interface ReferenceScreenshot {
|
||||
/** Unique identifier */
|
||||
id: string;
|
||||
|
||||
/** Preview mode this reference is for */
|
||||
previewMode: PreviewMode;
|
||||
|
||||
/** Theme mode this reference is for */
|
||||
themeMode: ThemeMode;
|
||||
|
||||
/** When captured from YouTube */
|
||||
capturedAt: Date;
|
||||
|
||||
/** YouTube URL captured from */
|
||||
capturedFrom: string;
|
||||
|
||||
/** File system path */
|
||||
filePath: string;
|
||||
|
||||
/** Viewport dimensions */
|
||||
viewportWidth: number;
|
||||
viewportHeight: number;
|
||||
|
||||
/** Browser used for capture */
|
||||
browserType: 'chromium' | 'firefox' | 'webkit';
|
||||
|
||||
/** Whether this is the current baseline */
|
||||
isActive: boolean;
|
||||
|
||||
/** ID of newer version if replaced */
|
||||
replacedBy?: string;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Component Props
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Props for YouTubeVideoCard component
|
||||
*/
|
||||
export interface YouTubeVideoCardProps {
|
||||
/** Thumbnail image URL or data URL */
|
||||
thumbnailUrl: string;
|
||||
|
||||
/** Video title */
|
||||
title: string;
|
||||
|
||||
/** Channel name */
|
||||
channelTitle: string;
|
||||
|
||||
/** Formatted view count (e.g., "1.2M views") */
|
||||
viewCount?: string;
|
||||
|
||||
/** Relative time string (e.g., "3 days ago") */
|
||||
publishedAt?: string;
|
||||
|
||||
/** Whether this is user's uploaded thumbnail (for highlighting) */
|
||||
isUserThumbnail?: boolean;
|
||||
|
||||
/** Layout variant */
|
||||
variant?: PreviewMode;
|
||||
|
||||
/** Video duration string */
|
||||
duration?: string;
|
||||
|
||||
/** Optional channel avatar URL */
|
||||
channelAvatarUrl?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Props for ThemeToggle component
|
||||
*/
|
||||
export interface ThemeToggleProps {
|
||||
/** Current theme mode */
|
||||
theme: ThemeMode;
|
||||
|
||||
/** Callback when theme changes */
|
||||
onThemeChange: (theme: ThemeMode) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Props for PreviewModeSelector component
|
||||
*/
|
||||
export interface PreviewModeSelectorProps {
|
||||
/** Current preview mode */
|
||||
mode: PreviewMode;
|
||||
|
||||
/** Callback when mode changes */
|
||||
onModeChange: (mode: PreviewMode) => void;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Validation
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Thumbnail upload validation result
|
||||
*/
|
||||
export interface ThumbnailValidationResult {
|
||||
valid: boolean;
|
||||
errors: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Validation constants
|
||||
*/
|
||||
export const VALIDATION_CONSTANTS = {
|
||||
MAX_FILE_SIZE: 5 * 1024 * 1024, // 5MB
|
||||
MAX_TITLE_LENGTH: 100,
|
||||
MAX_CHANNEL_NAME_LENGTH: 50,
|
||||
MAX_RECENT_THUMBNAILS: 10,
|
||||
VISUAL_MATCH_THRESHOLD: 98, // percentage
|
||||
SUPPORTED_FORMATS: ['image/jpeg', 'image/png', 'image/webp'] as const,
|
||||
DURATION_REGEX: /^\d{1,2}:\d{2}(:\d{2})?$/,
|
||||
} as const;
|
||||
257
specs/002-youtube-design-preview/data-model.md
Normal file
257
specs/002-youtube-design-preview/data-model.md
Normal file
@@ -0,0 +1,257 @@
|
||||
# Data Model: YouTube Design Preview Replica
|
||||
|
||||
**Date**: 2026-01-29
|
||||
|
||||
## Entities
|
||||
|
||||
### PreviewMode (Enum)
|
||||
|
||||
```typescript
|
||||
type PreviewMode = 'desktop' | 'sidebar' | 'mobile';
|
||||
```
|
||||
|
||||
| Value | Description | Thumbnail Size |
|
||||
|-------|-------------|----------------|
|
||||
| `desktop` | YouTube homepage/search grid layout | 360x202px |
|
||||
| `sidebar` | YouTube related videos sidebar | 168x94px |
|
||||
| `mobile` | YouTube mobile app full-width layout | 100% width |
|
||||
|
||||
### ThemeMode (Enum)
|
||||
|
||||
```typescript
|
||||
type ThemeMode = 'light' | 'dark';
|
||||
```
|
||||
|
||||
### VideoMetadata
|
||||
|
||||
User-customizable metadata for preview display.
|
||||
|
||||
```typescript
|
||||
interface VideoMetadata {
|
||||
title: string; // Video title (max 100 chars for display)
|
||||
channelName: string; // Channel name
|
||||
channelAvatarUrl?: string; // Optional channel avatar image URL
|
||||
duration: string; // Format: "MM:SS" or "H:MM:SS"
|
||||
viewCount: number; // Raw number, formatted for display
|
||||
publishedAt: Date; // Used for "X days ago" calculation
|
||||
}
|
||||
```
|
||||
|
||||
**Validation Rules:**
|
||||
- `title`: Required, 1-100 characters
|
||||
- `channelName`: Required, 1-50 characters
|
||||
- `duration`: Required, format `/^\d{1,2}:\d{2}(:\d{2})?$/`
|
||||
- `viewCount`: Required, non-negative integer
|
||||
- `publishedAt`: Required, valid Date, not in future
|
||||
|
||||
### UploadedThumbnail
|
||||
|
||||
Represents a user-uploaded thumbnail image.
|
||||
|
||||
```typescript
|
||||
interface UploadedThumbnail {
|
||||
id: string; // UUID
|
||||
dataUrl: string; // Base64 data URL of the image
|
||||
originalName: string; // Original filename
|
||||
fileSize: number; // Size in bytes (max 5MB)
|
||||
mimeType: string; // 'image/jpeg' | 'image/png' | 'image/webp'
|
||||
width: number; // Original image width
|
||||
height: number; // Original image height
|
||||
uploadedAt: Date; // Timestamp
|
||||
}
|
||||
```
|
||||
|
||||
**Validation Rules:**
|
||||
- `fileSize`: Max 5,242,880 bytes (5MB)
|
||||
- `mimeType`: Only 'image/jpeg', 'image/png', 'image/webp'
|
||||
- `width`, `height`: Positive integers
|
||||
|
||||
### PreviewState
|
||||
|
||||
Complete state for the preview tool, persisted to localStorage.
|
||||
|
||||
```typescript
|
||||
interface PreviewState {
|
||||
// Active preview configuration
|
||||
currentThumbnail: UploadedThumbnail | null;
|
||||
previewMode: PreviewMode;
|
||||
themeMode: ThemeMode;
|
||||
metadata: VideoMetadata;
|
||||
|
||||
// History for quick switching
|
||||
recentThumbnails: UploadedThumbnail[]; // Max 10 items
|
||||
|
||||
// UI state
|
||||
showMetadataEditor: boolean;
|
||||
|
||||
// Persistence metadata
|
||||
lastSaved: Date;
|
||||
version: number; // Schema version for migrations
|
||||
}
|
||||
```
|
||||
|
||||
### VisualTestResult
|
||||
|
||||
Result of Playwright visual comparison test.
|
||||
|
||||
```typescript
|
||||
interface VisualTestResult {
|
||||
testId: string;
|
||||
previewMode: PreviewMode;
|
||||
themeMode: ThemeMode;
|
||||
capturedAt: Date;
|
||||
|
||||
// Comparison metrics
|
||||
matchPercentage: number; // 0-100
|
||||
passed: boolean; // matchPercentage >= threshold (98%)
|
||||
|
||||
// File references
|
||||
actualScreenshotPath: string;
|
||||
expectedScreenshotPath: string;
|
||||
diffImagePath?: string; // Only if failed
|
||||
|
||||
// Error info
|
||||
errorMessage?: string;
|
||||
}
|
||||
```
|
||||
|
||||
### ReferenceScreenshot
|
||||
|
||||
Stored YouTube reference for visual comparison.
|
||||
|
||||
```typescript
|
||||
interface ReferenceScreenshot {
|
||||
id: string;
|
||||
previewMode: PreviewMode;
|
||||
themeMode: ThemeMode;
|
||||
capturedAt: Date;
|
||||
capturedFrom: string; // YouTube URL
|
||||
filePath: string;
|
||||
|
||||
// Metadata
|
||||
viewportWidth: number;
|
||||
viewportHeight: number;
|
||||
browserType: string; // 'chromium' | 'firefox' | 'webkit'
|
||||
|
||||
// Version tracking
|
||||
isActive: boolean; // Current baseline
|
||||
replacedBy?: string; // ID of newer version
|
||||
}
|
||||
```
|
||||
|
||||
## Local Storage Schema
|
||||
|
||||
Key: `thumbpreview_state`
|
||||
|
||||
```typescript
|
||||
interface LocalStorageSchema {
|
||||
version: 1;
|
||||
state: PreviewState;
|
||||
}
|
||||
```
|
||||
|
||||
**Storage Limits:**
|
||||
- Total localStorage budget: ~5MB
|
||||
- Each thumbnail (base64): ~1-2MB typical
|
||||
- Keep max 10 recent thumbnails
|
||||
- Automatic cleanup of oldest when limit reached
|
||||
|
||||
## State Transitions
|
||||
|
||||
### Thumbnail Upload Flow
|
||||
|
||||
```
|
||||
[No Thumbnail]
|
||||
|
|
||||
v (user uploads file)
|
||||
[Validating]
|
||||
|
|
||||
+-- (invalid) --> [Error: show message] --> [No Thumbnail]
|
||||
|
|
||||
v (valid)
|
||||
[Processing]
|
||||
|
|
||||
v (resize/optimize if needed)
|
||||
[Preview Ready]
|
||||
|
|
||||
v (auto-save to localStorage)
|
||||
[Persisted]
|
||||
```
|
||||
|
||||
### Preview Mode Switch
|
||||
|
||||
```
|
||||
[Current Mode]
|
||||
|
|
||||
v (user selects new mode)
|
||||
[Transitioning]
|
||||
|
|
||||
v (re-render with new layout)
|
||||
[New Mode Active]
|
||||
|
|
||||
v (persist preference)
|
||||
[Saved]
|
||||
```
|
||||
|
||||
### Theme Mode Switch
|
||||
|
||||
```
|
||||
[Current Theme]
|
||||
|
|
||||
v (user clicks toggle)
|
||||
[Apply Theme Class]
|
||||
|
|
||||
v (CSS variables update)
|
||||
[New Theme Active]
|
||||
|
|
||||
v (persist preference)
|
||||
[Saved]
|
||||
```
|
||||
|
||||
## Relationships
|
||||
|
||||
```
|
||||
PreviewState
|
||||
|
|
||||
+-- 1:1 --> currentThumbnail (UploadedThumbnail)
|
||||
|
|
||||
+-- 1:N --> recentThumbnails (UploadedThumbnail[])
|
||||
|
|
||||
+-- 1:1 --> metadata (VideoMetadata)
|
||||
|
|
||||
+-- enum --> previewMode (PreviewMode)
|
||||
|
|
||||
+-- enum --> themeMode (ThemeMode)
|
||||
|
||||
VisualTestResult
|
||||
|
|
||||
+-- ref --> ReferenceScreenshot (expectedScreenshotPath)
|
||||
|
|
||||
+-- enum --> previewMode
|
||||
|
|
||||
+-- enum --> themeMode
|
||||
```
|
||||
|
||||
## Default Values
|
||||
|
||||
```typescript
|
||||
const DEFAULT_METADATA: VideoMetadata = {
|
||||
title: 'Your Video Title Here',
|
||||
channelName: 'Your Channel',
|
||||
channelAvatarUrl: undefined,
|
||||
duration: '10:30',
|
||||
viewCount: 125000,
|
||||
publishedAt: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) // 1 week ago
|
||||
};
|
||||
|
||||
const DEFAULT_STATE: PreviewState = {
|
||||
currentThumbnail: null,
|
||||
previewMode: 'desktop',
|
||||
themeMode: 'light',
|
||||
metadata: DEFAULT_METADATA,
|
||||
recentThumbnails: [],
|
||||
showMetadataEditor: false,
|
||||
lastSaved: new Date(),
|
||||
version: 1
|
||||
};
|
||||
```
|
||||
92
specs/002-youtube-design-preview/plan.md
Normal file
92
specs/002-youtube-design-preview/plan.md
Normal file
@@ -0,0 +1,92 @@
|
||||
# Implementation Plan: YouTube Design Preview Replica
|
||||
|
||||
**Branch**: `002-youtube-design-preview` | **Date**: 2026-01-29 | **Spec**: [spec.md](./spec.md)
|
||||
**Input**: Feature specification from `/specs/002-youtube-design-preview/spec.md`
|
||||
|
||||
## Summary
|
||||
|
||||
Enhance the existing thumbnail preview tool to achieve pixel-perfect visual accuracy with YouTube's current design across three view modes (Desktop, Mobile, Sidebar). The implementation focuses on extracting exact CSS values from YouTube, implementing local storage persistence, adding a manual theme toggle, and integrating Playwright MCP for automated visual regression testing with weekly reference screenshot updates.
|
||||
|
||||
## Technical Context
|
||||
|
||||
**Language/Version**: TypeScript 5.9.x (frontend + backend)
|
||||
**Primary Dependencies**: React 19.x, Tailwind CSS 4.x, shadcn/ui, Zustand, Playwright MCP
|
||||
**Storage**: Browser localStorage (preview persistence), file system (reference screenshots)
|
||||
**Testing**: Playwright visual comparison, Jest (unit tests)
|
||||
**Target Platform**: Web browsers (Chrome, Firefox, Safari), responsive mobile views
|
||||
**Project Type**: Monorepo (frontend + backend)
|
||||
**Performance Goals**: Preview render < 2 seconds, 98% visual match with YouTube
|
||||
**Constraints**: 5MB max thumbnail upload, JPG/PNG/WebP formats only
|
||||
**Scale/Scope**: Single-user local tool, no concurrent users
|
||||
|
||||
## Constitution Check
|
||||
|
||||
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
|
||||
|
||||
| Principle | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| I. Tech Stack | PASS | React 19.x, TypeScript 5.x, Tailwind CSS 4.x, shadcn/ui, Zustand - all aligned |
|
||||
| II. Architecture | PASS | Feature code goes in existing frontend structure |
|
||||
| III. Styling | PASS | YouTube CSS variables already defined in index.css; extend with Tailwind utilities |
|
||||
| IV. Data Management | PASS | Zustand for preview state, localStorage for persistence (no backend changes needed) |
|
||||
| V. Development Practices | PASS | TypeScript strict mode, ESLint, existing patterns followed |
|
||||
|
||||
**Gate Result**: PASS - All constitution principles satisfied.
|
||||
|
||||
## Project Structure
|
||||
|
||||
### Documentation (this feature)
|
||||
|
||||
```text
|
||||
specs/002-youtube-design-preview/
|
||||
├── plan.md # This file
|
||||
├── research.md # Phase 0: YouTube design extraction research
|
||||
├── data-model.md # Phase 1: Data entities and storage schema
|
||||
├── quickstart.md # Phase 1: Development setup guide
|
||||
├── contracts/ # Phase 1: API contracts (minimal - mostly frontend)
|
||||
└── tasks.md # Phase 2: Implementation tasks (created by /speckit.tasks)
|
||||
```
|
||||
|
||||
### Source Code (repository root)
|
||||
|
||||
```text
|
||||
frontend/
|
||||
├── src/
|
||||
│ ├── components/
|
||||
│ │ ├── YouTubeVideoCard.tsx # MODIFY: Enhance for pixel-perfect accuracy
|
||||
│ │ ├── PreviewModeSelector.tsx # NEW: Desktop/Mobile/Sidebar switcher
|
||||
│ │ ├── ThemeToggle.tsx # NEW: Light/dark mode toggle
|
||||
│ │ ├── ThumbnailUploader.tsx # MODIFY: Add file validation (5MB, formats)
|
||||
│ │ └── ui/ # shadcn/ui components (no changes)
|
||||
│ ├── hooks/
|
||||
│ │ └── useLocalStorage.ts # NEW: Persist preview state
|
||||
│ ├── store/
|
||||
│ │ └── previewStore.ts # MODIFY: Add persistence, theme state
|
||||
│ ├── types/
|
||||
│ │ └── index.ts # MODIFY: Add new type definitions
|
||||
│ ├── lib/
|
||||
│ │ └── youtube-styles.ts # NEW: Extracted YouTube CSS constants
|
||||
│ └── index.css # MODIFY: Add precise YouTube CSS variables
|
||||
├── tests/
|
||||
│ └── visual/
|
||||
│ ├── desktop.spec.ts # NEW: Playwright visual tests
|
||||
│ ├── mobile.spec.ts # NEW: Playwright visual tests
|
||||
│ ├── sidebar.spec.ts # NEW: Playwright visual tests
|
||||
│ └── reference/ # NEW: YouTube reference screenshots
|
||||
└── playwright.config.ts # NEW: Playwright configuration
|
||||
|
||||
scripts/
|
||||
└── capture-youtube-reference.ts # NEW: Weekly reference screenshot capture
|
||||
```
|
||||
|
||||
**Structure Decision**: Extending existing frontend structure. No backend changes required as persistence uses browser localStorage per clarification.
|
||||
|
||||
## Complexity Tracking
|
||||
|
||||
> No constitution violations to justify.
|
||||
|
||||
| Aspect | Decision | Rationale |
|
||||
|--------|----------|-----------|
|
||||
| Storage | localStorage only | Per clarification - no server persistence needed |
|
||||
| Theme | Manual toggle | Per clarification - no system preference detection |
|
||||
| Reference updates | Automated weekly | Per clarification - Playwright captures from live YouTube |
|
||||
145
specs/002-youtube-design-preview/quickstart.md
Normal file
145
specs/002-youtube-design-preview/quickstart.md
Normal file
@@ -0,0 +1,145 @@
|
||||
# Quickstart: YouTube Design Preview Replica
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Node.js 20+
|
||||
- pnpm or npm
|
||||
- Playwright (for visual testing)
|
||||
|
||||
## Setup
|
||||
|
||||
1. **Install dependencies** (if not already done):
|
||||
```bash
|
||||
cd frontend
|
||||
npm install
|
||||
```
|
||||
|
||||
2. **Install Playwright browsers** (for visual testing):
|
||||
```bash
|
||||
npx playwright install chromium firefox webkit
|
||||
```
|
||||
|
||||
3. **Start development server**:
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
4. **Access the app**:
|
||||
Open http://localhost:5173 in your browser
|
||||
|
||||
## Development Workflow
|
||||
|
||||
### Working on Preview Components
|
||||
|
||||
The main component to modify is:
|
||||
- `frontend/src/components/YouTubeVideoCard.tsx`
|
||||
|
||||
YouTube CSS variables are defined in:
|
||||
- `frontend/src/index.css` (search for "YOUTUBE THEME VARIABLES")
|
||||
|
||||
### Adding New Preview Modes
|
||||
|
||||
1. Update `ViewMode` type in `frontend/src/types/index.ts`
|
||||
2. Add new variant case in `YouTubeVideoCard.tsx`
|
||||
3. Update `ViewSwitcher.tsx` to include new option
|
||||
4. Add visual test in `frontend/tests/visual/`
|
||||
|
||||
### Testing Visual Accuracy
|
||||
|
||||
**Manual Testing:**
|
||||
1. Open YouTube in a browser tab
|
||||
2. Open the preview tool in another tab
|
||||
3. Compare side-by-side at same zoom level
|
||||
|
||||
**Automated Testing (Playwright):**
|
||||
```bash
|
||||
cd frontend
|
||||
npx playwright test tests/visual/
|
||||
```
|
||||
|
||||
### Updating Reference Screenshots
|
||||
|
||||
Reference screenshots are stored in:
|
||||
`specs/002-youtube-design-preview/reference/`
|
||||
|
||||
To capture new references:
|
||||
```bash
|
||||
# Using Playwright MCP or manual script
|
||||
npx ts-node scripts/capture-youtube-reference.ts
|
||||
```
|
||||
|
||||
## Key Files
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `src/components/YouTubeVideoCard.tsx` | Main preview component |
|
||||
| `src/components/PreviewModeSelector.tsx` | Desktop/Mobile/Sidebar switcher |
|
||||
| `src/components/ThemeToggle.tsx` | Light/Dark mode toggle |
|
||||
| `src/store/previewStore.ts` | Zustand state management |
|
||||
| `src/hooks/useLocalStorage.ts` | Persistence hook |
|
||||
| `src/lib/youtube-styles.ts` | YouTube CSS constants |
|
||||
| `src/index.css` | YouTube CSS variables |
|
||||
|
||||
## CSS Variables Reference
|
||||
|
||||
### Light Mode Colors
|
||||
```css
|
||||
--yt-bg: #ffffff;
|
||||
--yt-title: #0f0f0f;
|
||||
--yt-meta: #606060;
|
||||
```
|
||||
|
||||
### Dark Mode Colors
|
||||
```css
|
||||
--yt-bg: #0f0f0f;
|
||||
--yt-title: #f1f1f1;
|
||||
--yt-meta: #aaaaaa;
|
||||
```
|
||||
|
||||
### Dimensions
|
||||
```css
|
||||
/* Desktop thumbnail */
|
||||
width: 360px;
|
||||
height: 202px;
|
||||
border-radius: 12px;
|
||||
|
||||
/* Sidebar thumbnail */
|
||||
width: 168px;
|
||||
height: 94px;
|
||||
border-radius: 8px;
|
||||
|
||||
/* Avatar */
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 50%;
|
||||
|
||||
/* Duration badge */
|
||||
padding: 3px 4px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
```
|
||||
|
||||
## Common Tasks
|
||||
|
||||
### Add support for new YouTube element
|
||||
|
||||
1. Capture reference from YouTube using Playwright
|
||||
2. Extract computed styles using browser DevTools
|
||||
3. Add CSS variables to `index.css` if new colors needed
|
||||
4. Update component JSX and Tailwind classes
|
||||
5. Run visual tests to verify accuracy
|
||||
|
||||
### Debug visual differences
|
||||
|
||||
1. Run Playwright test to generate diff image
|
||||
2. Check `specs/002-youtube-design-preview/reference/` for comparison
|
||||
3. Use browser DevTools to compare computed styles
|
||||
4. Adjust Tailwind classes or CSS variables
|
||||
|
||||
### Update for YouTube design changes
|
||||
|
||||
1. Capture new reference screenshots from YouTube
|
||||
2. Run visual tests - they should fail showing differences
|
||||
3. Update CSS values in component/styles
|
||||
4. Re-run tests until passing
|
||||
5. Commit new reference screenshots
|
||||
168
specs/002-youtube-design-preview/research.md
Normal file
168
specs/002-youtube-design-preview/research.md
Normal file
@@ -0,0 +1,168 @@
|
||||
# Research: YouTube Design Preview Replica
|
||||
|
||||
**Date**: 2026-01-29
|
||||
**Method**: Playwright MCP automated extraction from live YouTube (1920x1080)
|
||||
|
||||
## YouTube CSS Design Tokens (January 2026)
|
||||
|
||||
### Typography
|
||||
|
||||
| Element | Font Family | Size | Weight | Line Height | Color (Light) | Color (Dark) |
|
||||
|---------|-------------|------|--------|-------------|---------------|--------------|
|
||||
| Title (Desktop Grid) | Roboto, Arial, sans-serif | 16px | 500 | 22px | #0f0f0f | #f1f1f1 |
|
||||
| Title (Search) | Roboto, Arial, sans-serif | 18px | 400 | 26px | #0f0f0f | #f1f1f1 |
|
||||
| Title (Sidebar) | Roboto, Arial, sans-serif | 14px | 500 | 20px | #0f0f0f | #f1f1f1 |
|
||||
| Channel Name | Roboto, Arial, sans-serif | 12px | 400 | 18px | #606060 | #aaaaaa |
|
||||
| Metadata (views, time) | Roboto, Arial, sans-serif | 12px | 400 | 18px | #606060 | #aaaaaa |
|
||||
| Duration Badge | Roboto, Arial, sans-serif | 12px | 500 | normal | #ffffff | #ffffff |
|
||||
|
||||
### Thumbnail Dimensions
|
||||
|
||||
| View Mode | Width | Height | Aspect Ratio | Border Radius |
|
||||
|-----------|-------|--------|--------------|---------------|
|
||||
| Desktop Grid (Home) | 360px | 202px | 16:9 (1.778) | 12px |
|
||||
| Search Results | 360px | 202px | 16:9 (1.778) | 12px |
|
||||
| Sidebar (Related) | 168px | 94px | 16:9 (1.787) | 8px |
|
||||
| Mobile (Full Width) | 100% | auto | 16:9 | 0px (edge-to-edge) |
|
||||
|
||||
### Avatar Dimensions
|
||||
|
||||
| View Mode | Size | Shape |
|
||||
|-----------|------|-------|
|
||||
| Desktop Grid | 36x36px | Circle |
|
||||
| Search Results | 36x36px | Circle |
|
||||
| Sidebar | N/A (no avatar) | - |
|
||||
| Mobile | 36x36px | Circle |
|
||||
|
||||
### Duration Badge Styling
|
||||
|
||||
```css
|
||||
.duration-badge {
|
||||
background-color: rgba(0, 0, 0, 0.8);
|
||||
color: #ffffff;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
font-family: Roboto, Arial, sans-serif;
|
||||
padding: 3px 4px;
|
||||
border-radius: 4px;
|
||||
position: absolute;
|
||||
bottom: 4px;
|
||||
right: 4px;
|
||||
}
|
||||
```
|
||||
|
||||
### Color Palette
|
||||
|
||||
#### Light Mode
|
||||
```css
|
||||
:root {
|
||||
--yt-bg: #ffffff;
|
||||
--yt-surface: #ffffff;
|
||||
--yt-title: #0f0f0f;
|
||||
--yt-meta: #606060;
|
||||
--yt-icon: #606060;
|
||||
--yt-border: #e5e5e5;
|
||||
--yt-hover: rgba(0, 0, 0, 0.05);
|
||||
--yt-chip-bg: #f2f2f2;
|
||||
--yt-chip-active-bg: #0f0f0f;
|
||||
--yt-chip-active-text: #ffffff;
|
||||
--yt-blue: #065fd4;
|
||||
--yt-red: #ff0000;
|
||||
}
|
||||
```
|
||||
|
||||
#### Dark Mode
|
||||
```css
|
||||
.dark {
|
||||
--yt-bg: #0f0f0f;
|
||||
--yt-surface: #0f0f0f;
|
||||
--yt-title: #f1f1f1;
|
||||
--yt-meta: #aaaaaa;
|
||||
--yt-icon: #aaaaaa;
|
||||
--yt-border: #3f3f3f;
|
||||
--yt-hover: rgba(255, 255, 255, 0.1);
|
||||
--yt-chip-bg: #272727;
|
||||
--yt-chip-active-bg: #f1f1f1;
|
||||
--yt-chip-active-text: #0f0f0f;
|
||||
--yt-blue: #3ea6ff;
|
||||
--yt-red: #ff0000;
|
||||
}
|
||||
```
|
||||
|
||||
### Spacing & Layout
|
||||
|
||||
| Property | Desktop Grid | Search | Sidebar |
|
||||
|----------|--------------|--------|---------|
|
||||
| Card Gap (horizontal) | 16px | N/A | N/A |
|
||||
| Card Gap (vertical) | 40px | 16px | 8px |
|
||||
| Thumbnail-to-metadata gap | 12px | 12px | 8px |
|
||||
| Avatar-to-text gap | 12px | 12px | N/A |
|
||||
| Title max lines | 2 | 2 | 2 |
|
||||
| Channel name max lines | 1 | 1 | 1 |
|
||||
|
||||
### Hover States (Desktop)
|
||||
|
||||
```css
|
||||
.video-card:hover .thumbnail {
|
||||
/* YouTube shows preview animation on hover after delay */
|
||||
transform: none; /* No scale transform */
|
||||
}
|
||||
|
||||
.video-card:hover .watch-later-button,
|
||||
.video-card:hover .add-to-queue-button {
|
||||
opacity: 1; /* Buttons appear on hover */
|
||||
}
|
||||
|
||||
.channel-name:hover {
|
||||
color: var(--yt-title); /* Slightly darker on hover */
|
||||
}
|
||||
```
|
||||
|
||||
### Responsive Breakpoints
|
||||
|
||||
| Breakpoint | Grid Columns | Thumbnail Width |
|
||||
|------------|--------------|-----------------|
|
||||
| > 2136px | 6 columns | ~356px |
|
||||
| 1712px - 2136px | 5 columns | ~342px |
|
||||
| 1288px - 1712px | 4 columns | ~320px |
|
||||
| 888px - 1288px | 3 columns | ~290px |
|
||||
| 512px - 888px | 2 columns | ~280px |
|
||||
| < 512px | 1 column (mobile) | 100% |
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1. CSS Variable Naming
|
||||
**Decision**: Use `--yt-*` prefix for YouTube-specific variables
|
||||
**Rationale**: Already implemented in current codebase; keeps YouTube styles isolated from app design system
|
||||
**Alternatives**: Using Tailwind theme tokens directly - rejected because we need YouTube-specific colors separate from app theme
|
||||
|
||||
### 2. Component Structure
|
||||
**Decision**: Single `YouTubeVideoCard` component with `variant` prop for mode switching
|
||||
**Rationale**: Existing component already supports variants; minimizes code duplication
|
||||
**Alternatives**: Separate components per mode - rejected due to shared logic (formatting, truncation)
|
||||
|
||||
### 3. Visual Testing Strategy
|
||||
**Decision**: Playwright screenshot comparison with 98% match threshold
|
||||
**Rationale**: Allows for minor anti-aliasing differences while catching layout regressions
|
||||
**Alternatives**: Pixel-perfect 100% match - too brittle; structural testing only - doesn't catch visual issues
|
||||
|
||||
### 4. Reference Screenshot Updates
|
||||
**Decision**: Weekly automated capture via Playwright MCP
|
||||
**Rationale**: Per clarification; balances freshness with stability
|
||||
**Alternatives**: Manual updates - inconsistent; daily updates - too frequent, unstable baselines
|
||||
|
||||
### 5. Local Storage Schema
|
||||
**Decision**: Store serialized preview state with thumbnail as base64
|
||||
**Rationale**: Self-contained, no external file dependencies
|
||||
**Alternatives**: IndexedDB with blob storage - more complex, minimal benefit for single-user tool
|
||||
|
||||
## Reference Screenshots Captured
|
||||
|
||||
- `youtube-desktop-homepage-*.png` - Homepage grid layout
|
||||
- `youtube-search-fullhd-*.png` - Search results at 1920x1080
|
||||
- `youtube-video-with-sidebar-*.png` - Watch page with related videos
|
||||
- `youtube-watch-related-*.png` - Sidebar compact video cards
|
||||
- `youtube-trending-grid-*.png` - Trending page grid
|
||||
- `youtube-dark-mode-*.png` - Dark theme reference
|
||||
|
||||
All screenshots stored in: `specs/002-youtube-design-preview/reference/`
|
||||
146
specs/002-youtube-design-preview/spec.md
Normal file
146
specs/002-youtube-design-preview/spec.md
Normal file
@@ -0,0 +1,146 @@
|
||||
# Feature Specification: YouTube Design Preview Replica
|
||||
|
||||
**Feature Branch**: `002-youtube-design-preview`
|
||||
**Created**: 2026-01-29
|
||||
**Status**: Draft
|
||||
**Input**: User description: "необходимо в превью полностью повторить дизайн youtube для десктопа, мобильной версии и sidebar, превью должно полностью повторять youtube 1 в 1, используй mcp playwright для проверки соответствия"
|
||||
|
||||
## User Scenarios & Testing *(mandatory)*
|
||||
|
||||
### User Story 1 - Desktop Video Preview (Priority: P1)
|
||||
|
||||
A content creator wants to preview how their thumbnail will appear in YouTube's desktop grid layout, including the video duration badge, channel avatar, video title, channel name, view count, and upload time - exactly as it appears on YouTube's homepage.
|
||||
|
||||
**Why this priority**: Desktop is the primary platform for content creators reviewing their work. The grid layout preview provides the most common context where thumbnails are seen by viewers.
|
||||
|
||||
**Independent Test**: Can be tested by uploading a thumbnail and verifying it renders in a pixel-perfect YouTube desktop grid card format with all metadata elements positioned correctly.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** a user has uploaded a thumbnail image, **When** they select desktop preview mode, **Then** they see their thumbnail displayed in an exact replica of YouTube's desktop video card with 16:9 aspect ratio, rounded corners, duration badge in bottom-right, and hover effects
|
||||
2. **Given** the desktop preview is displayed, **When** the user views the video metadata section, **Then** they see channel avatar (circular, left-aligned), title (multi-line truncation), channel name, view count, and relative upload time styled identically to YouTube
|
||||
3. **Given** the desktop preview is active, **When** the user hovers over the thumbnail, **Then** they see YouTube's hover state including preview animation placeholder and watch later/add to queue icons
|
||||
|
||||
---
|
||||
|
||||
### User Story 2 - Mobile Video Preview (Priority: P1)
|
||||
|
||||
A content creator wants to see how their thumbnail appears on YouTube's mobile app layout, where the thumbnail takes full width and the metadata is displayed below in YouTube's mobile-specific format.
|
||||
|
||||
**Why this priority**: Mobile accounts for over 70% of YouTube views. Creators must verify their thumbnails are effective on mobile devices where layout and text sizes differ significantly.
|
||||
|
||||
**Independent Test**: Can be tested by switching to mobile preview mode and verifying the layout matches YouTube's mobile app exactly, including touch-friendly sizing and mobile-specific typography.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** a user has uploaded a thumbnail, **When** they select mobile preview mode, **Then** the thumbnail displays full-width with YouTube's mobile aspect ratio and rounded corners matching the app
|
||||
2. **Given** mobile preview is active, **When** viewing video metadata, **Then** the channel avatar, title, channel name, views, and time are displayed in YouTube's mobile typography and spacing
|
||||
3. **Given** mobile preview is active, **When** the preview is compared to actual YouTube mobile app, **Then** the layout, spacing, fonts, and element sizes match within 2px tolerance
|
||||
|
||||
---
|
||||
|
||||
### User Story 3 - Sidebar/Related Videos Preview (Priority: P2)
|
||||
|
||||
A content creator wants to preview how their thumbnail will appear in YouTube's sidebar (related videos section) where thumbnails are displayed at a smaller size alongside video titles.
|
||||
|
||||
**Why this priority**: The sidebar is a major discovery surface for videos. Thumbnails must be legible and compelling at this smaller size to drive click-through from related videos.
|
||||
|
||||
**Independent Test**: Can be tested by selecting sidebar preview mode and verifying the thumbnail and title display match YouTube's sidebar format exactly.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** a user has uploaded a thumbnail, **When** they select sidebar preview mode, **Then** the thumbnail displays at YouTube's exact sidebar dimensions (168x94px) with correct aspect ratio
|
||||
2. **Given** sidebar preview is active, **When** viewing the video card, **Then** the title displays to the right of the thumbnail with proper truncation (2 lines), channel name below, and view count/time in YouTube's sidebar format
|
||||
3. **Given** sidebar preview mode, **When** multiple preview cards are shown, **Then** they stack vertically with YouTube's exact spacing between cards
|
||||
|
||||
---
|
||||
|
||||
### User Story 4 - Visual Accuracy Validation with Playwright (Priority: P2)
|
||||
|
||||
The system uses automated visual comparison testing with Playwright MCP to ensure the preview components match YouTube's actual design with pixel-level accuracy.
|
||||
|
||||
**Why this priority**: Automated validation ensures design fidelity is maintained during development and catches visual regressions early. This supports the "1:1 replica" requirement.
|
||||
|
||||
**Independent Test**: Can be tested by running Playwright visual comparison tests against captured YouTube reference screenshots.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** a preview component is rendered, **When** Playwright captures a screenshot, **Then** the screenshot can be compared against a YouTube reference image with configurable threshold (default 98% match)
|
||||
2. **Given** a visual comparison test runs, **When** differences are detected above threshold, **Then** a diff image is generated highlighting discrepancies for developer review
|
||||
3. **Given** all three preview modes exist, **When** the full visual test suite runs, **Then** each mode (desktop, mobile, sidebar) passes the visual comparison against corresponding YouTube references
|
||||
|
||||
---
|
||||
|
||||
### Edge Cases
|
||||
|
||||
- What happens when the thumbnail image has non-standard aspect ratio? System should crop/scale to 16:9 with preview of crop area
|
||||
- How does system handle very long video titles? Truncation with ellipsis matching YouTube's exact behavior per view mode
|
||||
- What happens when channel name contains special characters or emojis? Render identically to YouTube's handling
|
||||
- How does system handle missing metadata fields (no view count yet, no upload time)? Display "No views" and "Just now" as YouTube does
|
||||
- What happens on intermediate screen sizes between desktop and mobile breakpoints? Follow YouTube's responsive behavior
|
||||
- What happens when uploaded thumbnail exceeds 5MB? Display error message and reject upload, prompting user to reduce file size
|
||||
- What happens when user uploads unsupported image format (e.g., GIF, BMP)? Display error message listing accepted formats (JPG, PNG, WebP)
|
||||
|
||||
## Requirements *(mandatory)*
|
||||
|
||||
### Functional Requirements
|
||||
|
||||
- **FR-001**: System MUST render desktop video card preview matching YouTube's current design with pixel-level accuracy
|
||||
- **FR-002**: System MUST render mobile video card preview matching YouTube's mobile app layout exactly
|
||||
- **FR-003**: System MUST render sidebar video card preview matching YouTube's related videos section design
|
||||
- **FR-004**: System MUST support real-time thumbnail image upload and preview update
|
||||
- **FR-005**: System MUST display all YouTube video metadata elements: thumbnail, duration badge, channel avatar, title, channel name, view count, upload time
|
||||
- **FR-006**: System MUST replicate YouTube's typography including font family, sizes, weights, and colors for each view mode
|
||||
- **FR-007**: System MUST replicate YouTube's spacing, padding, and margins for all preview components
|
||||
- **FR-008**: System MUST replicate YouTube's hover states and interactive elements on desktop preview
|
||||
- **FR-009**: System MUST implement responsive breakpoints matching YouTube's desktop-to-mobile transitions
|
||||
- **FR-010**: System MUST support Playwright MCP integration for automated visual comparison testing
|
||||
- **FR-011**: System MUST generate visual diff reports when preview design deviates from YouTube reference
|
||||
- **FR-012**: System MUST allow users to customize preview metadata (title, channel name, view count, etc.) for realistic previews
|
||||
- **FR-013**: System MUST display duration badge with correct formatting (HH:MM:SS or MM:SS) positioned in bottom-right of thumbnail
|
||||
- **FR-014**: System MUST render channel avatar as circular image with YouTube's exact sizing per view mode
|
||||
- **FR-015**: System MUST persist preview data (thumbnail, metadata) in browser local storage, surviving page refresh without requiring server-side storage or user accounts
|
||||
- **FR-016**: System MUST provide manual theme toggle allowing users to switch between light and dark modes matching YouTube's respective color schemes (no auto-detection of system preference)
|
||||
- **FR-017**: System MUST automatically capture fresh reference screenshots from live YouTube on a weekly schedule for visual comparison baseline updates
|
||||
- **FR-018**: System MUST store historical reference screenshots to enable rollback if automated capture produces invalid baselines
|
||||
- **FR-019**: System MUST enforce 5MB maximum file size for thumbnail uploads, matching YouTube's actual limit, and display clear error message for oversized files
|
||||
- **FR-020**: System MUST accept JPG, PNG, and WebP image formats for thumbnail upload, rejecting unsupported formats with clear error message
|
||||
|
||||
### Key Entities
|
||||
|
||||
- **ThumbnailPreview**: Represents a preview instance containing user-uploaded image, selected view mode, and customizable metadata
|
||||
- **PreviewMode**: Enumeration of view modes (Desktop, Mobile, Sidebar) each with distinct layout specifications
|
||||
- **VideoMetadata**: User-customizable data including title, channel name, channel avatar, duration, view count, upload time
|
||||
- **VisualTestResult**: Comparison result containing match percentage, diff image reference, and pass/fail status
|
||||
|
||||
## Success Criteria *(mandatory)*
|
||||
|
||||
### Measurable Outcomes
|
||||
|
||||
- **SC-001**: All three preview modes (desktop, mobile, sidebar) achieve 98% or higher visual match when compared to YouTube reference screenshots via Playwright
|
||||
- **SC-002**: Users can upload a thumbnail and see the preview render in under 2 seconds
|
||||
- **SC-003**: Preview accurately reflects YouTube's current design as verified by side-by-side manual comparison
|
||||
- **SC-004**: 90% of users can correctly identify the preview as "YouTube-like" in blind comparison tests
|
||||
- **SC-005**: Visual regression tests pass with less than 2% pixel difference from established baseline
|
||||
- **SC-006**: Preview maintains visual accuracy across Chrome, Firefox, and Safari browsers
|
||||
- **SC-007**: Mobile preview renders correctly on actual mobile devices (iOS Safari, Android Chrome) matching the YouTube app appearance
|
||||
|
||||
## Clarifications
|
||||
|
||||
### Session 2026-01-29
|
||||
|
||||
- Q: Should previews be persisted for later access? → A: Local storage (preview persisted in browser, no server storage)
|
||||
- Q: How should theme mode (light/dark) be handled? → A: Both themes available, user must manually select (no auto-detection)
|
||||
- Q: How should reference screenshots be maintained for visual comparison? → A: Automated weekly capture from live YouTube
|
||||
- Q: What is the maximum thumbnail file size for upload? → A: 5MB maximum (matches YouTube's actual limit)
|
||||
- Q: Which image formats are supported for thumbnail upload? → A: JPG, PNG, and WebP (matches YouTube's supported formats)
|
||||
|
||||
## Assumptions
|
||||
|
||||
- YouTube's current design (as of January 2026) serves as the reference baseline
|
||||
- Font family uses YouTube's public-facing fonts (Roboto for body text)
|
||||
- Color values will be extracted from YouTube's current production CSS
|
||||
- Playwright MCP is available and configured in the development environment
|
||||
- Reference screenshots will be automatically captured from live YouTube weekly and stored for comparison testing, with historical versions retained for rollback
|
||||
- The preview tool focuses on static visual accuracy; video playback preview is out of scope
|
||||
- Dark mode support follows YouTube's dark mode color scheme; users manually select theme via toggle (no system preference auto-detection)
|
||||
249
specs/002-youtube-design-preview/tasks.md
Normal file
249
specs/002-youtube-design-preview/tasks.md
Normal file
@@ -0,0 +1,249 @@
|
||||
# Tasks: YouTube Design Preview Replica
|
||||
|
||||
**Input**: Design documents from `/specs/002-youtube-design-preview/`
|
||||
**Prerequisites**: plan.md, spec.md, research.md, data-model.md, contracts/
|
||||
|
||||
**Tests**: Visual regression tests included as User Story 4 (P2) per specification requirements.
|
||||
|
||||
**Organization**: Tasks are grouped by user story to enable independent implementation and testing of each story.
|
||||
|
||||
## Format: `[ID] [P?] [Story] Description`
|
||||
|
||||
- **[P]**: Can run in parallel (different files, no dependencies)
|
||||
- **[Story]**: Which user story this task belongs to (e.g., US1, US2, US3, US4)
|
||||
- Include exact file paths in descriptions
|
||||
|
||||
## Path Conventions
|
||||
|
||||
- **Frontend**: `frontend/src/`
|
||||
- **Tests**: `frontend/tests/visual/`
|
||||
- **Scripts**: `scripts/`
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Setup (Shared Infrastructure)
|
||||
|
||||
**Purpose**: Project initialization, type definitions, and shared utilities
|
||||
|
||||
- [x] T001 [P] Add new type definitions (PreviewMode, ThemeMode, VideoMetadata, UploadedThumbnail) in `frontend/src/types/index.ts`
|
||||
- [x] T002 [P] Create YouTube style constants module with extracted CSS tokens in `frontend/src/lib/youtube-styles.ts`
|
||||
- [x] T003 [P] Update CSS variables with precise YouTube values from research in `frontend/src/index.css`
|
||||
- [x] T004 [P] Create useLocalStorage hook for state persistence in `frontend/src/hooks/useLocalStorage.ts`
|
||||
- [x] T005 Install Playwright as dev dependency: `cd frontend && npm install -D @playwright/test`
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Foundational (Blocking Prerequisites)
|
||||
|
||||
**Purpose**: Core infrastructure that MUST be complete before ANY user story can be implemented
|
||||
|
||||
**⚠️ CRITICAL**: No user story work can begin until this phase is complete
|
||||
|
||||
- [x] T006 Update previewStore with new state shape (themeMode, persistence) in `frontend/src/store/previewStore.ts`
|
||||
- [x] T007 Add localStorage persistence integration to previewStore in `frontend/src/store/previewStore.ts`
|
||||
- [x] T008 [P] Create ThemeToggle component with light/dark switch in `frontend/src/components/ThemeToggle.tsx`
|
||||
- [x] T009 [P] Create PreviewModeSelector component (desktop/mobile/sidebar) in `frontend/src/components/PreviewModeSelector.tsx`
|
||||
- [x] T010 Update ThumbnailUploader with 5MB limit and format validation (JPG/PNG/WebP) in `frontend/src/components/ThumbnailUploader.tsx`
|
||||
- [x] T011 Add error messages for file validation failures in `frontend/src/components/ThumbnailUploader.tsx`
|
||||
|
||||
**Checkpoint**: Foundation ready - user story implementation can now begin in parallel
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: User Story 1 - Desktop Video Preview (Priority: P1) 🎯 MVP
|
||||
|
||||
**Goal**: Pixel-perfect YouTube desktop grid layout preview with all metadata elements
|
||||
|
||||
**Independent Test**: Upload a thumbnail, select desktop mode, verify visual match with YouTube homepage grid card
|
||||
|
||||
### Implementation for User Story 1
|
||||
|
||||
- [x] T012 [US1] Refactor YouTubeVideoCard desktop variant with exact YouTube dimensions (360x202px, 12px radius) in `frontend/src/components/YouTubeVideoCard.tsx`
|
||||
- [x] T013 [US1] Update desktop title typography (Roboto 16px/500, line-height 22px, 2-line clamp) in `frontend/src/components/YouTubeVideoCard.tsx`
|
||||
- [x] T014 [US1] Update channel name and metadata typography (12px, #606060/#aaaaaa) in `frontend/src/components/YouTubeVideoCard.tsx`
|
||||
- [x] T015 [US1] Update avatar display (36x36px circle) in `frontend/src/components/YouTubeVideoCard.tsx`
|
||||
- [x] T016 [US1] Update duration badge styling (rgba(0,0,0,0.8), 12px/500, 4px radius, 3px 4px padding) in `frontend/src/components/YouTubeVideoCard.tsx`
|
||||
- [x] T017 [US1] Add hover states (watch later, add to queue icons on hover) in `frontend/src/components/YouTubeVideoCard.tsx`
|
||||
- [x] T018 [US1] Add metadata spacing (12px thumbnail-to-meta gap, 12px avatar-to-text gap) in `frontend/src/components/YouTubeVideoCard.tsx`
|
||||
- [x] T019 [US1] Integrate desktop preview with ThemeToggle for light/dark mode switching in `frontend/src/pages/ToolPage.tsx`
|
||||
- [x] T020 [US1] Add metadata editor UI for customizing title, channel, views, duration in `frontend/src/components/UserInfoInputs.tsx`
|
||||
|
||||
**Checkpoint**: Desktop preview should match YouTube homepage grid exactly - test with side-by-side comparison
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: User Story 2 - Mobile Video Preview (Priority: P1)
|
||||
|
||||
**Goal**: Pixel-perfect YouTube mobile app layout preview with full-width thumbnails
|
||||
|
||||
**Independent Test**: Switch to mobile mode, verify full-width layout matches YouTube mobile app
|
||||
|
||||
### Implementation for User Story 2
|
||||
|
||||
- [x] T021 [US2] Update YouTubeVideoCard mobile variant with full-width thumbnail (100%, 16:9 ratio, 0px radius) in `frontend/src/components/YouTubeVideoCard.tsx`
|
||||
- [x] T022 [US2] Update mobile metadata layout (avatar 36x36, title below thumbnail) in `frontend/src/components/YouTubeVideoCard.tsx`
|
||||
- [x] T023 [US2] Update mobile typography (match YouTube mobile app sizes) in `frontend/src/components/YouTubeVideoCard.tsx`
|
||||
- [x] T024 [US2] Update mobile spacing (gap between metadata elements) in `frontend/src/components/YouTubeVideoCard.tsx`
|
||||
- [x] T025 [US2] Add mobile container wrapper with max-width constraint in `frontend/src/pages/ToolPage.tsx`
|
||||
- [x] T026 [US2] Add responsive viewport simulation for mobile preview in `frontend/src/pages/ToolPage.tsx`
|
||||
|
||||
**Checkpoint**: Mobile preview should match YouTube mobile app - test on mobile device or devtools
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: User Story 3 - Sidebar/Related Videos Preview (Priority: P2)
|
||||
|
||||
**Goal**: Pixel-perfect YouTube sidebar (related videos) preview with compact horizontal layout
|
||||
|
||||
**Independent Test**: Switch to sidebar mode, verify compact card layout matches YouTube watch page sidebar
|
||||
|
||||
### Implementation for User Story 3
|
||||
|
||||
- [x] T027 [US3] Update YouTubeVideoCard sidebar variant with exact dimensions (168x94px thumbnail, 8px radius) in `frontend/src/components/YouTubeVideoCard.tsx`
|
||||
- [x] T028 [US3] Update sidebar horizontal layout (thumbnail left, metadata right) in `frontend/src/components/YouTubeVideoCard.tsx`
|
||||
- [x] T029 [US3] Update sidebar title typography (14px/500, 2-line clamp) in `frontend/src/components/YouTubeVideoCard.tsx`
|
||||
- [x] T030 [US3] Update sidebar metadata (channel name, views, time below title) in `frontend/src/components/YouTubeVideoCard.tsx`
|
||||
- [x] T031 [US3] Update sidebar spacing (8px thumbnail-to-text gap, 8px between cards) in `frontend/src/components/YouTubeVideoCard.tsx`
|
||||
- [x] T032 [US3] Remove avatar from sidebar variant (YouTube sidebar has no avatar) in `frontend/src/components/YouTubeVideoCard.tsx`
|
||||
- [x] T033 [US3] Add sidebar list container with vertical stacking in `frontend/src/pages/ToolPage.tsx`
|
||||
|
||||
**Checkpoint**: Sidebar preview should match YouTube watch page related videos section
|
||||
|
||||
---
|
||||
|
||||
## Phase 6: User Story 4 - Visual Accuracy Validation with Playwright (Priority: P2)
|
||||
|
||||
**Goal**: Automated visual regression testing to ensure 98% match with YouTube reference screenshots
|
||||
|
||||
**Independent Test**: Run `npx playwright test` and verify all visual tests pass with 98% threshold
|
||||
|
||||
### Implementation for User Story 4
|
||||
|
||||
- [x] T034 [P] [US4] Create Playwright configuration file in `frontend/playwright.config.ts`
|
||||
- [x] T035 [P] [US4] Create visual test utilities (screenshot comparison, diff generation) in `frontend/tests/visual/utils.ts`
|
||||
- [x] T036 [P] [US4] Copy reference screenshots to test directory in `frontend/tests/visual/reference/`
|
||||
- [x] T037 [US4] Create desktop visual test (light mode) in `frontend/tests/visual/desktop.spec.ts`
|
||||
- [x] T038 [US4] Add desktop visual test (dark mode) in `frontend/tests/visual/desktop.spec.ts`
|
||||
- [x] T039 [US4] Create mobile visual test (light/dark modes) in `frontend/tests/visual/mobile.spec.ts`
|
||||
- [x] T040 [US4] Create sidebar visual test (light/dark modes) in `frontend/tests/visual/sidebar.spec.ts`
|
||||
- [x] T041 [US4] Create reference screenshot capture script in `scripts/capture-youtube-reference.ts`
|
||||
- [x] T042 [US4] Add npm script for visual tests: `"test:visual": "playwright test"` in `frontend/package.json`
|
||||
|
||||
**Checkpoint**: All visual tests should pass with 98%+ match against YouTube references
|
||||
|
||||
---
|
||||
|
||||
## Phase 7: Polish & Cross-Cutting Concerns
|
||||
|
||||
**Purpose**: Improvements that affect multiple user stories
|
||||
|
||||
- [x] T043 [P] Add responsive breakpoints matching YouTube (6→5→4→3→2→1 column) in `frontend/src/components/PreviewGrid.tsx`
|
||||
- [x] T044 [P] Add edge case handling: non-standard aspect ratio thumbnails (crop to 16:9) in `frontend/src/components/ThumbnailUploader.tsx`
|
||||
- [x] T045 [P] Add edge case handling: long titles (ellipsis truncation per mode) in `frontend/src/components/YouTubeVideoCard.tsx`
|
||||
- [x] T046 [P] Add edge case handling: missing metadata ("No views", "Just now") in `frontend/src/components/YouTubeVideoCard.tsx`
|
||||
- [x] T047 [P] Add localStorage cleanup (limit 10 recent thumbnails) in `frontend/src/store/previewStore.ts`
|
||||
- [x] T048 Performance optimization: lazy load thumbnails, optimize re-renders in `frontend/src/components/YouTubeVideoCard.tsx`
|
||||
- [x] T049 Run ESLint and fix any errors: `cd frontend && npm run lint`
|
||||
- [ ] T050 Manual validation against quickstart.md test scenarios
|
||||
|
||||
---
|
||||
|
||||
## Dependencies & Execution Order
|
||||
|
||||
### Phase Dependencies
|
||||
|
||||
- **Setup (Phase 1)**: No dependencies - can start immediately
|
||||
- **Foundational (Phase 2)**: Depends on Setup completion - BLOCKS all user stories
|
||||
- **User Stories (Phase 3-6)**: All depend on Foundational phase completion
|
||||
- User stories can then proceed in parallel (if staffed)
|
||||
- Or sequentially in priority order (US1 → US2 → US3 → US4)
|
||||
- **Polish (Phase 7)**: Depends on all user stories being complete
|
||||
|
||||
### User Story Dependencies
|
||||
|
||||
- **User Story 1 (P1)**: Can start after Foundational (Phase 2) - No dependencies on other stories
|
||||
- **User Story 2 (P1)**: Can start after Foundational (Phase 2) - No dependencies on other stories
|
||||
- **User Story 3 (P2)**: Can start after Foundational (Phase 2) - No dependencies on other stories
|
||||
- **User Story 4 (P2)**: Can start after US1/US2/US3 have implementations to test
|
||||
|
||||
### Within Each User Story
|
||||
|
||||
- Core styling before hover states
|
||||
- Layout before typography fine-tuning
|
||||
- Component implementation before page integration
|
||||
- Story complete before moving to next priority
|
||||
|
||||
### Parallel Opportunities
|
||||
|
||||
- All Setup tasks T001-T004 can run in parallel
|
||||
- Foundational tasks T008, T009 can run in parallel
|
||||
- User Stories 1, 2, 3 can be worked on in parallel after Foundational
|
||||
- Visual test setup (T034-T036) can run in parallel
|
||||
- All Polish tasks marked [P] can run in parallel
|
||||
|
||||
---
|
||||
|
||||
## Parallel Example: Setup Phase
|
||||
|
||||
```bash
|
||||
# Launch all setup tasks in parallel:
|
||||
Task: "Add new type definitions in frontend/src/types/index.ts"
|
||||
Task: "Create YouTube style constants in frontend/src/lib/youtube-styles.ts"
|
||||
Task: "Update CSS variables in frontend/src/index.css"
|
||||
Task: "Create useLocalStorage hook in frontend/src/hooks/useLocalStorage.ts"
|
||||
```
|
||||
|
||||
## Parallel Example: User Stories After Foundational
|
||||
|
||||
```bash
|
||||
# With multiple developers after Phase 2:
|
||||
Developer A: User Story 1 (Desktop) - T012-T020
|
||||
Developer B: User Story 2 (Mobile) - T021-T026
|
||||
Developer C: User Story 3 (Sidebar) - T027-T033
|
||||
# Then User Story 4 (Visual Tests) once implementations exist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation Strategy
|
||||
|
||||
### MVP First (User Story 1 Only)
|
||||
|
||||
1. Complete Phase 1: Setup (T001-T005)
|
||||
2. Complete Phase 2: Foundational (T006-T011)
|
||||
3. Complete Phase 3: User Story 1 - Desktop (T012-T020)
|
||||
4. **STOP and VALIDATE**: Side-by-side comparison with YouTube
|
||||
5. Deploy/demo if ready
|
||||
|
||||
### Incremental Delivery
|
||||
|
||||
1. Setup + Foundational → Foundation ready
|
||||
2. Add User Story 1 (Desktop) → Test independently → Demo (MVP!)
|
||||
3. Add User Story 2 (Mobile) → Test independently → Demo
|
||||
4. Add User Story 3 (Sidebar) → Test independently → Demo
|
||||
5. Add User Story 4 (Visual Tests) → Automated validation
|
||||
6. Polish phase → Production ready
|
||||
|
||||
### Parallel Team Strategy
|
||||
|
||||
With multiple developers:
|
||||
|
||||
1. Team completes Setup + Foundational together
|
||||
2. Once Foundational is done:
|
||||
- Developer A: User Story 1 (Desktop)
|
||||
- Developer B: User Story 2 (Mobile)
|
||||
- Developer C: User Story 3 (Sidebar)
|
||||
3. All developers: User Story 4 (Visual Tests)
|
||||
4. Team: Polish phase
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- [P] tasks = different files, no dependencies
|
||||
- [Story] label maps task to specific user story for traceability
|
||||
- Each user story should be independently completable and testable
|
||||
- Commit after each task or logical group
|
||||
- Stop at any checkpoint to validate story independently
|
||||
- YouTube CSS values from research.md are the source of truth
|
||||
- Reference screenshots in specs/002-youtube-design-preview/reference/
|
||||
Reference in New Issue
Block a user