Atelier Khelifi -- Interactive Showcase
Client
Yasmine Khelifi, owner and sole artisan at Atelier Khelifi. Handcrafted leather goods -- bags, wallets, belts, and custom pieces. Workshop in the Medina of Tunis, Tunisia. Fifteen years of craft using vegetable-tanned leather and traditional techniques passed down from her grandmother. The portfolio site from P1 is live. Yasmine wants it to become interactive: product detail views, category filtering, and a custom work section.
What you're building
An interactive product showcase that lets visitors explore Yasmine's leather goods. Three new capabilities: (1) click a product to see detail photos -- stitching, hardware, different angles; (2) filter products by category -- bags, wallets, belts, custom pieces; (3) a section explaining the custom work process for customers who keep asking. Built with React and TypeScript, replacing the static HTML from P1.
Tech stack
- React with TypeScript (component framework)
- Vite (build tool and dev server)
- Tailwind CSS (utility-first styling)
- Vitest (test runner)
- html-validate (HTML structural validation)
- Lighthouse (performance, accessibility, best practices, SEO)
- Vercel CLI (deployment to staging and production)
- Git and GitHub (version control, issues, project board)
File structure
src/
components/ # React components
ProductCard.tsx # Individual product display
CategoryFilter.tsx # Filter buttons for categories
ProductDetail.tsx # Detail view with multiple photos
CustomWork.tsx # Custom work process section
data/
products.ts # Product data (imports product-data.json)
types/
product.ts # TypeScript type definitions
App.tsx # Main application component
main.tsx # Application entry point
public/
photos/ # Product photography (placeholder for dev)
materials/
first-contact.md # Yasmine's follow-up email
product-data.json # Product catalog (8 items, 4 categories)
custom-work-brief.md # Custom work process reference
prd.md # PRD (student creates this)
design-decisions.md # Design decisions (student creates this)
templates/ # Pipeline templates for artifact creation
CLAUDE.md # This file -- project governance
Key material references
- PRD: Student creates from Yasmine's email using materials/templates/prd-template.md
- Design decisions: Student creates using materials/templates/design-decisions-template.md
- Product data: materials/product-data.json -- 8 products across 4 categories
- Custom work brief: materials/custom-work-brief.md -- reference for the custom work section content
- Yasmine's email: materials/first-contact.md -- the client request that starts the pipeline
Tickets
Work through tickets in order. Each ticket has acceptance criteria -- verify them before moving on.
T1: Project Setup and Scaffold
Initialize the React + TypeScript + Vite project, install dependencies, and configure the development environment.
- Initialize project with
npm create vite@latest(React + TypeScript template) - Install Tailwind CSS and configure
tailwind.config.ts - Install Vitest and add a test script to
package.json - Create the file structure:
src/components/,src/data/,src/types/ - Copy
product-data.jsoninto the project and create the TypeScript type definition - Verify:
npm run devstarts without errors,npm run testruns without errors
Acceptance criteria:
- Vite dev server starts and displays the default page
- Tailwind utility classes render correctly
- Vitest runs with zero tests (no failures)
- TypeScript compiles without errors
- File structure matches the plan
T2: Product Card Component
Build the ProductCard component that displays a single product with its main photo, name, category, and description.
- Create
ProductCard.tsxwith props: product data object - Display the main photo, product name, leather type, and price range
- Add descriptive alt text for the product image
- Style with Tailwind CSS -- warm tones consistent with Yasmine's brand
- Add keyboard and click handler to open the detail view
- Write Vitest tests: renders product name, renders alt text, calls onClick handler
Acceptance criteria:
- ProductCard renders all product fields (name, photo, leather type, price range)
- Image has descriptive alt text (not "image" or "photo")
- Component is keyboard accessible (focusable, activates on Enter/Space)
- At least 3 Vitest tests pass for this component
T3: Category Filter with State Management
Build the CategoryFilter component that lets visitors filter products by category.
- Create
CategoryFilter.tsxwith filter buttons: All, Bags, Wallets, Belts, Custom - Manage selected category with React state (
useState) - Filter the product list based on selected category
- Style active filter button differently from inactive buttons
- Add
aria-pressedattribute to filter buttons for screen readers - Write Vitest tests: renders all category buttons, filtering changes displayed products
Acceptance criteria:
- Filter buttons display for each category plus "All"
- Clicking a category shows only products in that category
- Active button is visually distinct from inactive buttons
- Filter buttons have
aria-pressedattribute - At least 3 Vitest tests pass for filtering behavior
T4: Product Detail View with Focus Trapping
Build the ProductDetail component that shows multiple photos and full product information when a product is selected.
- Create
ProductDetail.tsxas a modal/overlay showing detail photos, description, leather type, and price range - Display 2-3 detail photos per product alongside the main photo
- Implement keyboard navigation: Escape closes the detail view
- Trap focus inside the detail view when open (tab cycles within the modal)
- Add appropriate ARIA attributes (
role="dialog",aria-modal="true",aria-label) - Write Vitest tests: renders detail photos, Escape key closes view, ARIA attributes present
Acceptance criteria:
- Detail view shows main photo plus 2-3 detail photos
- All photos have descriptive alt text
- Escape key closes the detail view
- Focus is trapped inside the modal when open
- ARIA attributes are present and correct
- At least 3 Vitest tests pass for this component
T5: Custom Work Section and Security
Build the CustomWork component explaining Yasmine's custom work process, and review the full site for security.
- Create
CustomWork.tsxwith content from materials/custom-work-brief.md - Sections: types of custom work, process, materials, pricing approach, how to start
- Ensure no user input is rendered with
innerHTMLordangerouslySetInnerHTML - Review all components for XSS vectors -- no unsanitized interpolation
- Write Vitest tests: renders section headings, no dangerous HTML rendering
Acceptance criteria:
- Custom work section displays all five content areas
- Content matches the custom work brief
- No use of
innerHTMLordangerouslySetInnerHTMLanywhere in the codebase - At least 2 Vitest tests pass for this component
T6: Deployment to Vercel with Environment Variables
Deploy the interactive showcase to Vercel with staging and production environments.
- Install Vercel CLI (
npm i -g vercel) - Configure environment variables for staging and production (at minimum:
VITE_ENV=staging/VITE_ENV=production) - Deploy to staging (
vercel) and verify the site works - Run html-validate against the built HTML -- fix any errors
- Run Lighthouse in Chrome DevTools -- all four categories >= 90
- Run
npm run test-- all Vitest tests pass - Deploy to production (
vercel --prod)
Acceptance criteria:
- Site deployed to a live Vercel URL (staging and production)
- Environment variables configured for both environments
- html-validate reports 0 errors
- Lighthouse Performance >= 90
- Lighthouse Accessibility >= 90
- Lighthouse Best Practices >= 90
- Lighthouse SEO >= 90
- All Vitest tests pass
- Interactive features work: category filter, detail view open/close, keyboard navigation
Verification targets
| Check | Target |
|---|---|
| html-validate | 0 errors across all HTML output |
| Lighthouse Performance | >= 90 |
| Lighthouse Accessibility | >= 90 |
| Lighthouse Best Practices | >= 90 |
| Lighthouse SEO | >= 90 |
| Vitest | All tests passing |
| Keyboard accessibility | Filter buttons and detail view operable by keyboard |
Commit convention
Conventional commits, one ticket per commit. Reference the ticket ID in the message. Example: feat(T2): add ProductCard component with Vitest tests.