mirror of
https://github.com/mmahdium/portfolio.git
synced 2026-08-17 05:24:30 +03:30
feat(epic-2): implement resume page route and design validation
- Create `app/pages/resume.vue` as standalone page with print mode detection - Add page metadata configuration with SEO noindex tag and custom title - Implement `isPrintMode` computed property for print parameter handling - Add placeholder content for ResumePreview component integration - Create design validation report identifying critical mismatches between tech spec and design template - Update Story 2.1 task completion status and add dev notes - Clean up context XML files from sprint artifacts documentation - Update tech spec and sprint status documentation for Epic 2 alignment - Story 2.1 now marked as complete with all acceptance criteria met
This commit is contained in:
@@ -1,25 +1,30 @@
|
||||
# Epic Technical Specification: Resume Preview Page
|
||||
|
||||
Date: 2025-11-30
|
||||
Author: mahdi
|
||||
Epic ID: 2
|
||||
Status: Draft
|
||||
Date: 2025-11-30
|
||||
Author: mahdi
|
||||
Epic ID: 2
|
||||
Status: **REVISED** (Post UX Validation)
|
||||
Last Update: 2025-11-30 (Winston - Architect)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Epic 2 delivers the Resume Preview Page - a pixel-perfect, standalone web page at `/resume` that renders the resume exactly as it will appear in the PDF export. This epic creates the visual foundation of the Resume Export feature, implementing all UI components with a two-column layout (sidebar + main content) using the Blue & White Clean Professional design template.
|
||||
Epic 2 delivers the Resume Preview Page - a pixel-perfect, standalone web page at `/resume` that renders the resume exactly as it will appear in the PDF export. This epic creates the visual foundation of the Resume Export feature, implementing all UI components with a **single-column layout** using the Blue & White Clean Professional design template.
|
||||
|
||||
The preview page serves dual purposes: (1) user-facing preview for verification before download, and (2) source page for server-side PDF generation via Puppeteer. The WYSIWYG (What You See Is What You Get) approach ensures perfect consistency between web and PDF output.
|
||||
|
||||
**Design Template Reference:** `design templates/Blue and White Clean and Professional Resume.png`
|
||||
|
||||
## Objectives and Scope
|
||||
|
||||
**In Scope:**
|
||||
- Standalone `/resume` route with no site navigation
|
||||
- Two-column responsive layout (35% sidebar, 65% main content)
|
||||
- Six modular Vue components for resume sections
|
||||
- **Single-column vertical layout** with A4-proportioned container
|
||||
- Five modular Vue components for resume sections
|
||||
- Blue (#2563eb) and white color scheme with Inter typography
|
||||
- Blue uppercase section headers with bottom border
|
||||
- Profile photo display in header
|
||||
- Print-optimized CSS for PDF generation compatibility
|
||||
- A4 aspect ratio container (210mm × 297mm)
|
||||
- Floating download button (hidden in print mode via `?print=true`)
|
||||
@@ -34,10 +39,10 @@ The preview page serves dual purposes: (1) user-facing preview for verification
|
||||
|
||||
## System Architecture Alignment
|
||||
|
||||
**Framework:** Nuxt 4.1.3 with Vue 3 Composition API
|
||||
**Styling:** Tailwind CSS 4.1.x with Nuxt UI 4.0.x components
|
||||
**Fonts:** Inter (via @nuxt/fonts) for English text
|
||||
**Icons:** Nuxt UI Icon component with Iconify icons
|
||||
**Framework:** Nuxt 4.1.3 with Vue 3 Composition API
|
||||
**Styling:** Tailwind CSS 4.1.x with Nuxt UI 4.0.x components
|
||||
**Fonts:** Inter (via @nuxt/fonts) for English text
|
||||
**Icons:** Nuxt UI Icon component with Iconify icons
|
||||
|
||||
**Architecture Decisions Referenced:**
|
||||
- ADR-002: Vue Components as Templates - Each resume section is a standalone component
|
||||
@@ -53,16 +58,19 @@ The preview page serves dual purposes: (1) user-facing preview for verification
|
||||
| Component | Responsibility | Inputs | Outputs | Owner |
|
||||
|-----------|---------------|--------|---------|-------|
|
||||
| `pages/resume.vue` | Route handler, layout wrapper | Query param `?print` | Renders ResumePreview | Story 2.1 |
|
||||
| `ResumePreview.vue` | Container, two-column grid | Resume data | Full resume layout | Story 2.2 |
|
||||
| `ResumeHeader.vue` | Name and job title display | `basics.name`, `basics.label` | Header section | Story 2.3 |
|
||||
| `ResumePreview.vue` | Container, single-column layout | Resume data | Full resume layout | Story 2.2 |
|
||||
| `ResumeHeader.vue` | Photo + Name + Contact Info | `basics` (name, label, email, phone, location, url, image) | Header section | Story 2.3 |
|
||||
| `ResumeSummary.vue` | Professional summary | `basics.summary` | Summary section | Story 2.3 |
|
||||
| `ResumeExperience.vue` | Work history with highlights | `work[]` array | Experience section | Story 2.3 |
|
||||
| `ResumeContact.vue` | Contact info with icons | `basics` (email, phone, location, profiles) | Contact section | Story 2.4 |
|
||||
| `ResumeSkills.vue` | Technical skills by category | `skills[]` array | Skills section | Story 2.4 |
|
||||
| `ResumeEducation.vue` | Education history | `education[]` array | Education section | Story 2.4 |
|
||||
| `ResumeLanguages.vue` | Language proficiencies | `languages[]` array | Languages section | Story 2.4 |
|
||||
| `ResumeAdditionalInfo.vue` | Skills + Languages + Certs | `skills[]`, `languages[]`, `certificates[]` | Additional Info section | Story 2.4 |
|
||||
| `ResumeDownloadButton.vue` | Floating action button | `?print` query param | Download trigger | Story 2.5 |
|
||||
|
||||
**Removed Components (vs. Previous Draft):**
|
||||
- `ResumeContact.vue` - Merged into `ResumeHeader.vue`
|
||||
- `ResumeSkills.vue` - Merged into `ResumeAdditionalInfo.vue`
|
||||
- `ResumeLanguages.vue` - Merged into `ResumeAdditionalInfo.vue`
|
||||
|
||||
### Data Models and Contracts
|
||||
|
||||
**Input:** All components consume data from `useResumeData()` composable:
|
||||
@@ -72,11 +80,13 @@ const { resume, formatDate, getFullName, getPdfFilename } = useResumeData()
|
||||
```
|
||||
|
||||
**Resume Data Structure (from Epic 1):**
|
||||
- `resume.basics`: Name, label, email, phone, location, profiles, summary
|
||||
- `resume.basics`: Name, label, email, phone, location, url, image, summary
|
||||
- `resume.work[]`: Company, position, startDate, endDate, highlights[]
|
||||
- `resume.education[]`: Institution, area, studyType, startDate, endDate
|
||||
- `resume.skills[]`: Name (category), keywords[]
|
||||
- `resume.languages[]`: Language, fluency
|
||||
- `resume.certificates[]`: Name, date, issuer (optional)
|
||||
- `resume.awards[]`: Title, date, awarder, summary (optional)
|
||||
|
||||
**Date Format:** YYYY-MM strings formatted to "Jan 2023" via `formatDate()` helper
|
||||
|
||||
@@ -92,6 +102,16 @@ const { resume, formatDate, getFullName, getPdfFilename } = useResumeData()
|
||||
interface Props {
|
||||
name: string
|
||||
label: string // Job title
|
||||
email: string
|
||||
phone: string
|
||||
location: string
|
||||
url: string
|
||||
image?: string // Profile photo URL
|
||||
}
|
||||
|
||||
// ResumeSummary.vue
|
||||
interface Props {
|
||||
summary: string
|
||||
}
|
||||
|
||||
// ResumeExperience.vue
|
||||
@@ -99,12 +119,23 @@ interface Props {
|
||||
work: WorkExperience[]
|
||||
}
|
||||
|
||||
// ResumeContact.vue
|
||||
// ResumeEducation.vue
|
||||
interface Props {
|
||||
basics: ResumeBasics
|
||||
education: Education[]
|
||||
}
|
||||
|
||||
// Similar pattern for other components
|
||||
// ResumeAdditionalInfo.vue
|
||||
interface Props {
|
||||
skills: Skill[]
|
||||
languages: Language[]
|
||||
certificates?: Certificate[]
|
||||
awards?: Award[]
|
||||
}
|
||||
|
||||
// ResumeDownloadButton.vue
|
||||
interface Props {
|
||||
isPrintMode: boolean
|
||||
}
|
||||
```
|
||||
|
||||
**Query Parameters:**
|
||||
@@ -116,9 +147,12 @@ interface Props {
|
||||
1. User navigates to `/resume`
|
||||
2. Page loads with `layout: false` (standalone)
|
||||
3. `ResumePreview.vue` fetches data via `useResumeData()`
|
||||
4. Two-column grid renders:
|
||||
- Left sidebar: Contact → Skills → Education → Languages
|
||||
- Right main: Header → Summary → Experience
|
||||
4. Single-column layout renders in order:
|
||||
- Header (Photo + Name + Contact Info)
|
||||
- Summary
|
||||
- Experience
|
||||
- Education
|
||||
- Additional Information (Skills, Languages, Certs)
|
||||
5. Floating download button appears (bottom-right)
|
||||
6. User clicks download → triggers Epic 3 PDF generation
|
||||
|
||||
@@ -176,121 +210,127 @@ interface Props {
|
||||
## Acceptance Criteria (Authoritative)
|
||||
|
||||
### AC1: Standalone Resume Route
|
||||
**Given** I navigate to `/resume`
|
||||
**When** the page loads
|
||||
**Then** I see the resume preview with no site header/footer
|
||||
**And** page title is "Resume - Ali Arghyani"
|
||||
**Given** I navigate to `/resume`
|
||||
**When** the page loads
|
||||
**Then** I see the resume preview with no site header/footer
|
||||
**And** page title is "Resume - Ali Arghyani"
|
||||
**And** meta tag `<meta name="robots" content="noindex">` is present
|
||||
|
||||
### AC2: Two-Column Layout
|
||||
**Given** the resume page is rendered on desktop
|
||||
**When** I view the layout
|
||||
**Then** I see a two-column grid:
|
||||
- Left sidebar (35% width): Contact, Skills, Education, Languages
|
||||
- Right main content (65% width): Header, Summary, Experience
|
||||
**And** container has A4 aspect ratio (210mm × 297mm)
|
||||
### AC2: Single-Column Layout
|
||||
**Given** the resume page is rendered on desktop
|
||||
**When** I view the layout
|
||||
**Then** I see a single-column vertical stack with:
|
||||
- Header section at top (photo + name + contact)
|
||||
- Summary section
|
||||
- Work Experience section
|
||||
- Education section
|
||||
- Additional Information section at bottom
|
||||
**And** container has A4 aspect ratio (210mm × 297mm)
|
||||
**And** page margins are 24px (1.5rem)
|
||||
|
||||
### AC3: Responsive Behavior
|
||||
**Given** the resume page is rendered on mobile
|
||||
**When** viewport width < 768px
|
||||
**Then** layout switches to single column
|
||||
**And** sidebar sections appear above main content
|
||||
**Given** the resume page is rendered on mobile
|
||||
**When** viewport width < 768px
|
||||
**Then** layout remains single column but adapts spacing
|
||||
**And** profile photo may resize or reposition for mobile
|
||||
**And** section order remains the same
|
||||
|
||||
### AC4: Color Scheme and Typography
|
||||
**Given** the resume is displayed
|
||||
**When** I inspect the styling
|
||||
**Then** primary color is blue (#2563eb) for headers and icons
|
||||
**And** background is white
|
||||
**And** text is dark gray (#1f2937)
|
||||
**And** font family is Inter
|
||||
**Given** the resume is displayed
|
||||
**When** I inspect the styling
|
||||
**Then** section headers are blue (#2563eb), uppercase, bold, with blue bottom border
|
||||
**And** background is white
|
||||
**And** text is dark gray (#1f2937)
|
||||
**And** font family is Inter
|
||||
**And** body text size is 14px (0.875rem)
|
||||
|
||||
### AC5: Header Section
|
||||
**Given** the header component renders
|
||||
**When** I view the top of the resume
|
||||
**Then** I see full name in large bold text (2rem)
|
||||
**And** job title below name (1.25rem, gray)
|
||||
**And** blue accent line or background element
|
||||
**Given** the header component renders
|
||||
**When** I view the top of the resume
|
||||
**Then** I see profile photo on the left (if provided)
|
||||
**And** name in large bold text (2rem) on the right
|
||||
**And** job title below name (1.25rem, gray)
|
||||
**And** contact details (address, phone, email, website) displayed with proper formatting
|
||||
**And** all contact links are clickable (mailto:, tel:, https://)
|
||||
|
||||
### AC6: Contact Section
|
||||
**Given** the contact component renders
|
||||
**When** I view the sidebar
|
||||
**Then** I see email with icon (i-mdi-email)
|
||||
**And** phone with icon (i-mdi-phone)
|
||||
**And** location with icon (i-mdi-map-marker)
|
||||
**And** social profiles (LinkedIn, GitHub) with icons
|
||||
**And** all links are clickable (mailto:, tel:, https://)
|
||||
|
||||
### AC7: Skills Section
|
||||
**Given** the skills component renders
|
||||
**When** I view the sidebar
|
||||
**Then** I see "Skills" section header
|
||||
**And** skill categories as subheaders
|
||||
**And** keywords displayed as tags or comma-separated list
|
||||
|
||||
### AC8: Education Section
|
||||
**Given** the education component renders
|
||||
**When** I view the sidebar
|
||||
**Then** I see degree type and field (e.g., "B.Sc. Computer Science")
|
||||
**And** institution name
|
||||
**And** date range formatted (e.g., "2018 - 2022")
|
||||
|
||||
### AC9: Languages Section
|
||||
**Given** the languages component renders
|
||||
**When** I view the sidebar
|
||||
**Then** I see "Languages" section header
|
||||
**And** each language with fluency level (e.g., "English - Fluent")
|
||||
|
||||
### AC10: Summary Section
|
||||
**Given** the summary component renders
|
||||
**When** I view the main content
|
||||
**Then** I see "Profile" or "Summary" section header
|
||||
**And** professional summary paragraph
|
||||
### AC6: Summary Section
|
||||
**Given** the summary component renders
|
||||
**When** I view after the header
|
||||
**Then** I see "SUMMARY" section header (blue, uppercase, underlined)
|
||||
**And** professional summary paragraph
|
||||
**And** line height is 1.6 for readability
|
||||
|
||||
### AC11: Experience Section
|
||||
**Given** the experience component renders
|
||||
**When** I view the main content
|
||||
**Then** I see for each job:
|
||||
- Position title (bold)
|
||||
- Company name
|
||||
- Date range (formatted: "Jan 2022 - Present")
|
||||
- Bullet points for highlights (• character)
|
||||
**And** jobs are sorted by date (most recent first)
|
||||
### AC7: Experience Section
|
||||
**Given** the experience component renders
|
||||
**When** I view the work history
|
||||
**Then** I see "WORK EXPERIENCE" section header (blue, uppercase, underlined)
|
||||
**And** for each job:
|
||||
- Position title (bold, left-aligned)
|
||||
- Company name (normal weight)
|
||||
- Date range (right-aligned: "Jan 2022 - Present")
|
||||
- Bullet points for highlights (• character)
|
||||
**And** jobs are sorted by date (most recent first)
|
||||
**And** "Present" is shown for current jobs (no endDate)
|
||||
|
||||
### AC12: Download Button
|
||||
**Given** I'm on the resume page
|
||||
**When** I view the page
|
||||
**Then** I see a floating action button in bottom-right corner
|
||||
**And** it has download icon (i-heroicons-arrow-down-tray)
|
||||
**And** it has "Download PDF" text
|
||||
**And** it has blue background color
|
||||
**And** it has fixed position (doesn't scroll)
|
||||
**And** it has shadow for elevation
|
||||
### AC8: Education Section
|
||||
**Given** the education component renders
|
||||
**When** I view the education history
|
||||
**Then** I see "EDUCATION" section header (blue, uppercase, underlined)
|
||||
**And** for each degree:
|
||||
- Degree type and field (e.g., "Bachelor of Mechatronics Engineering with Honours")
|
||||
- Institution name
|
||||
- Date range (right-aligned: "Aug 2016 - Oct 2019")
|
||||
- Optional bullet points for achievements or coursework
|
||||
|
||||
### AC9: Additional Information Section
|
||||
**Given** the additional info component renders
|
||||
**When** I view the bottom section
|
||||
**Then** I see "ADDITIONAL INFORMATION" section header (blue, uppercase, underlined)
|
||||
**And** "Technical Skills:" with comma-separated or categorized list
|
||||
**And** "Languages:" with fluency levels (e.g., "English, Malay, Japan")
|
||||
**And** "Certifications:" (if present) with name and issuer
|
||||
**And** "Awards/Activities:" (if present) with brief descriptions
|
||||
|
||||
### AC10: Download Button
|
||||
**Given** I'm on the resume page
|
||||
**When** I view the page
|
||||
**Then** I see a floating action button in bottom-right corner
|
||||
**And** it has download icon (i-heroicons-arrow-down-tray)
|
||||
**And** it has "Download PDF" text
|
||||
**And** it has blue background color (#2563eb)
|
||||
**And** it has fixed position (doesn't scroll)
|
||||
**And** it has shadow for elevation
|
||||
**And** it has `.no-print` class
|
||||
|
||||
### AC13: Print Mode
|
||||
**Given** I navigate to `/resume?print=true`
|
||||
**When** the page loads
|
||||
**Then** the download button is not visible
|
||||
### AC11: Print Mode
|
||||
**Given** I navigate to `/resume?print=true`
|
||||
**When** the page loads
|
||||
**Then** the download button is not visible
|
||||
**And** all other content renders normally
|
||||
|
||||
### AC14: Print Styles
|
||||
**Given** the page is printed or captured by Puppeteer
|
||||
**When** print media query is active
|
||||
**Then** `.no-print` elements are hidden
|
||||
**And** colors print correctly (printBackground: true)
|
||||
**And** page breaks are controlled
|
||||
### AC12: Print Styles
|
||||
**Given** the page is printed or captured by Puppeteer
|
||||
**When** print media query is active
|
||||
**Then** `.no-print` elements are hidden
|
||||
**And** colors print correctly (printBackground: true)
|
||||
**And** page breaks are controlled
|
||||
**And** A4 dimensions are maintained
|
||||
|
||||
### AC15: ATS Compatibility
|
||||
**Given** the HTML structure is inspected
|
||||
**When** I check semantic elements
|
||||
**Then** name uses `<h1>` tag
|
||||
**And** section headers use `<h2>` tags
|
||||
**And** no layout tables are used (CSS Grid/Flexbox only)
|
||||
### AC13: Section Header Styling
|
||||
**Given** any section header is rendered
|
||||
**When** I inspect its styling
|
||||
**Then** text is blue (#2563eb)
|
||||
**And** text is UPPERCASE
|
||||
**And** text is bold (font-weight: 700)
|
||||
**And** has a blue bottom border (border-bottom: 2px solid #2563eb)
|
||||
**And** has appropriate margin/padding for visual separation
|
||||
|
||||
### AC14: ATS Compatibility
|
||||
**Given** the HTML structure is inspected
|
||||
**When** I check semantic elements
|
||||
**Then** name uses `<h1>` tag
|
||||
**And** section headers use `<h2>` tags
|
||||
**And** no layout tables are used (CSS Grid/Flexbox only)
|
||||
**And** all text is real HTML text (not images)
|
||||
|
||||
## Traceability Mapping
|
||||
@@ -298,20 +338,19 @@ interface Props {
|
||||
| AC | Spec Section | Components | Test Idea |
|
||||
|----|--------------|------------|-----------|
|
||||
| AC1 | Standalone Route | `pages/resume.vue` | Navigate to /resume, verify no nav, check meta tags |
|
||||
| AC2 | Two-Column Layout | `ResumePreview.vue` | Inspect grid, measure column widths, verify A4 ratio |
|
||||
| AC3 | Responsive | `ResumePreview.vue` | Resize viewport, verify single column on mobile |
|
||||
| AC2 | Single-Column Layout | `ResumePreview.vue` | Verify vertical stack order, measure container, verify A4 ratio |
|
||||
| AC3 | Responsive | `ResumePreview.vue` | Resize viewport, verify mobile adaptations |
|
||||
| AC4 | Color/Typography | All components | Inspect computed styles, verify colors and fonts |
|
||||
| AC5 | Header | `ResumeHeader.vue` | Check h1 text, font size, job title styling |
|
||||
| AC6 | Contact | `ResumeContact.vue` | Verify icons, links, click mailto/tel links |
|
||||
| AC7 | Skills | `ResumeSkills.vue` | Check section header, categories, keywords |
|
||||
| AC5 | Header | `ResumeHeader.vue` | Check photo, name, title, contact info, click links |
|
||||
| AC6 | Summary | `ResumeSummary.vue` | Verify section header, paragraph, line height |
|
||||
| AC7 | Experience | `ResumeExperience.vue` | Check job entries, date formatting, bullet points |
|
||||
| AC8 | Education | `ResumeEducation.vue` | Verify degree, institution, date formatting |
|
||||
| AC9 | Languages | `ResumeLanguages.vue` | Check language list, fluency levels |
|
||||
| AC10 | Summary | `ResumeSummary.vue` | Verify section header, paragraph, line height |
|
||||
| AC11 | Experience | `ResumeExperience.vue` | Check job entries, date formatting, bullet points |
|
||||
| AC12 | Download Button | `ResumeDownloadButton.vue` | Verify FAB position, icon, styling, shadow |
|
||||
| AC13 | Print Mode | `pages/resume.vue` | Add ?print=true, verify button hidden |
|
||||
| AC14 | Print Styles | CSS | Trigger print preview, verify styles |
|
||||
| AC15 | ATS Compatibility | HTML structure | Inspect DOM, verify semantic tags, no tables |
|
||||
| AC9 | Additional Info | `ResumeAdditionalInfo.vue` | Check skills, languages, certs structure |
|
||||
| AC10 | Download Button | `ResumeDownloadButton.vue` | Verify FAB position, icon, styling, shadow |
|
||||
| AC11 | Print Mode | `pages/resume.vue` | Add ?print=true, verify button hidden |
|
||||
| AC12 | Print Styles | CSS | Trigger print preview, verify styles |
|
||||
| AC13 | Section Headers | All section components | Inspect header styling, verify blue, uppercase, border |
|
||||
| AC14 | ATS Compatibility | HTML structure | Inspect DOM, verify semantic tags, no tables |
|
||||
|
||||
## Risks, Assumptions, Open Questions
|
||||
|
||||
@@ -322,12 +361,14 @@ interface Props {
|
||||
- **Mitigation:** Test in Chrome, Firefox, Safari; use Puppeteer for PDF (consistent)
|
||||
- **Risk:** Long content causes page overflow
|
||||
- **Mitigation:** Test with max content length, add overflow handling
|
||||
- **Risk:** Profile image not provided or fails to load
|
||||
- **Mitigation:** Implement graceful fallback (hide image container, expand text area)
|
||||
|
||||
**Assumptions:**
|
||||
- **Assumption:** Inter font is ATS-compatible
|
||||
- **Validation:** Inter is a standard web font, widely supported
|
||||
- **Assumption:** Two-column layout works for all content lengths
|
||||
- **Validation:** Test with varying experience entries (2-5 jobs)
|
||||
- **Assumption:** Single-column layout works for all content lengths
|
||||
- **Validation:** Test with varying experience entries (2-5 jobs), education (1-3 degrees)
|
||||
- **Assumption:** Blue (#2563eb) has sufficient contrast
|
||||
- **Validation:** WCAG AA contrast ratio verified (4.5:1 minimum)
|
||||
|
||||
@@ -336,8 +377,8 @@ interface Props {
|
||||
- **Answer:** Yes, but consider smaller size or icon-only
|
||||
- **Question:** How to handle very long job titles or company names?
|
||||
- **Answer:** Use text truncation with ellipsis, test with max lengths
|
||||
- **Question:** Should we add a "Print" button in addition to "Download PDF"?
|
||||
- **Answer:** No for MVP, browser print (Ctrl+P) is sufficient
|
||||
- **Question:** Profile photo aspect ratio and size?
|
||||
- **Answer:** Square or portrait (3:4), max 150px width, left-aligned in header
|
||||
|
||||
## Test Strategy Summary
|
||||
|
||||
@@ -345,23 +386,25 @@ interface Props {
|
||||
- Each component renders with sample data
|
||||
- Date formatting helper works correctly
|
||||
- Composable returns expected data structure
|
||||
- Profile image fallback works when image unavailable
|
||||
|
||||
**Integration Tests:**
|
||||
- Full page renders with all components
|
||||
- Full page renders with all components in correct order
|
||||
- Print mode hides download button
|
||||
- Responsive layout switches at breakpoint
|
||||
- Responsive layout adapts spacing on mobile
|
||||
|
||||
**Visual Regression Tests:**
|
||||
- Screenshot comparison: web preview vs expected design
|
||||
- Screenshot comparison: web preview vs design template
|
||||
- Print preview matches web preview
|
||||
- Mobile layout matches design
|
||||
- Mobile layout matches design intent
|
||||
|
||||
**Manual Tests:**
|
||||
- Navigate to /resume, verify all sections
|
||||
- Click all links (email, phone, social profiles)
|
||||
- Navigate to /resume, verify all sections in correct order
|
||||
- Click all links (email, phone, website)
|
||||
- Test print preview (Ctrl+P)
|
||||
- Test on mobile device
|
||||
- Verify ATS compatibility (copy/paste text from PDF)
|
||||
- Test with and without profile photo
|
||||
|
||||
**Performance Tests:**
|
||||
- Measure page load time (< 1s target)
|
||||
@@ -369,12 +412,21 @@ interface Props {
|
||||
- Verify font loading doesn't block render
|
||||
|
||||
**Acceptance Tests:**
|
||||
- Run through all 15 ACs with real data
|
||||
- Run through all 14 ACs with real data
|
||||
- Verify pixel-perfect match to design template
|
||||
- Confirm Epic 3 can generate PDF from this page
|
||||
|
||||
---
|
||||
|
||||
**Epic 2 Tech Spec Complete**
|
||||
**Status:** Ready for Story Creation
|
||||
**Next Step:** SM creates Story 2.1 draft
|
||||
## Revision History
|
||||
|
||||
| Date | Author | Changes |
|
||||
|------|--------|---------|
|
||||
| 2025-11-30 | mahdi (initial) | Created initial tech spec (two-column layout) |
|
||||
| 2025-11-30 | Winston (Architect) | **MAJOR REVISION:** Changed to single-column layout per UX validation report. Refactored component architecture (merged Contact into Header, created AdditionalInfo, removed standalone Skills/Languages). Updated all ACs to match design template. |
|
||||
|
||||
---
|
||||
|
||||
**Epic 2 Tech Spec - REVISED**
|
||||
**Status:** Ready for Review → Implementation
|
||||
**Next Step:** SM reviews revised spec, creates updated Story 2.1 draft
|
||||
|
||||
Reference in New Issue
Block a user