From a79f4a32c787179a4371bda064e4799cd237fcec Mon Sep 17 00:00:00 2001 From: Padreug Date: Wed, 27 May 2026 11:20:30 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20portfolio=20scaffolding=20=E2=80=94=20i?= =?UTF-8?q?ndex=20+=20Boulder=20+=20Asheville=20detail=20pages?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the data layer (src/data/projects.ts) holding curated narrative order, alt text, and feature tags ('hero'|'wide'|'narrow'|'paired') for both projects, plus the shared ProjectDetail + ProjectImage components that read from it. Each ProjectImage opens a Dialog lightbox on click; lazy-loaded by default. ProjectDetail's editorial scroll honors the feature tag — full-bleed hero, narrow centered, side-by-side pairs, etc — so the layout rhythm is driven by data, not template forks. The two project views are 4-line shims over the shared component. PortfolioView is a 2-up 4:5 grid of project covers leading to the detail pages; the cover ratios are deliberate (portrait crops echo the editorial spread feel). Router adds the 4 new routes plus a 404→home catch-all and reset-on-navigate scroll behavior. ContactView ships as a stub; the form lands in a follow-up commit alongside the Nostr submission helper. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/components/projects/ProjectDetail.vue | 87 ++++++++++++ src/components/projects/ProjectImage.vue | 38 ++++++ src/data/projects.ts | 155 ++++++++++++++++++++++ src/router/index.ts | 28 ++++ src/views/ContactView.vue | 11 ++ src/views/PortfolioView.vue | 48 +++++++ src/views/projects/AshevilleView.vue | 8 ++ src/views/projects/BoulderView.vue | 8 ++ 8 files changed, 383 insertions(+) create mode 100644 src/components/projects/ProjectDetail.vue create mode 100644 src/components/projects/ProjectImage.vue create mode 100644 src/data/projects.ts create mode 100644 src/views/ContactView.vue create mode 100644 src/views/PortfolioView.vue create mode 100644 src/views/projects/AshevilleView.vue create mode 100644 src/views/projects/BoulderView.vue diff --git a/src/components/projects/ProjectDetail.vue b/src/components/projects/ProjectDetail.vue new file mode 100644 index 0000000..b5ffd19 --- /dev/null +++ b/src/components/projects/ProjectDetail.vue @@ -0,0 +1,87 @@ + + + diff --git a/src/components/projects/ProjectImage.vue b/src/components/projects/ProjectImage.vue new file mode 100644 index 0000000..b47bebf --- /dev/null +++ b/src/components/projects/ProjectImage.vue @@ -0,0 +1,38 @@ + + + diff --git a/src/data/projects.ts b/src/data/projects.ts new file mode 100644 index 0000000..f33eff6 --- /dev/null +++ b/src/data/projects.ts @@ -0,0 +1,155 @@ +export type ImageOrientation = 'landscape' | 'portrait' + +export interface ProjectImage { + src: string + alt: string + orientation: ImageOrientation + /** Tag controlling layout slot in ProjectDetail's editorial scroll. */ + feature?: 'hero' | 'wide' | 'narrow' | 'paired' +} + +export interface Project { + slug: 'boulder' | 'asheville' + name: string + eyebrow: string + intro: string + cover: string + coverAlt: string + images: ProjectImage[] +} + +export const boulder: Project = { + slug: 'boulder', + name: 'Boulder', + eyebrow: 'Light-filled organic', + intro: + 'A mid-century ranch reimagined around warm woods, reclaimed barnwood walls, ' + + 'live-edge counters, and emerald glazed tile. The palette stays in conversation ' + + 'with the trees outside — sunlight does most of the work.', + cover: '/images/boulder/05.jpg', + coverAlt: 'Walnut kitchen with white embossed tile and warm pendant light', + images: [ + { + src: '/images/boulder/03.jpg', + alt: 'Kitchen with reclaimed barnwood walls and live-edge bar top', + orientation: 'landscape', + feature: 'hero', + }, + { + src: '/images/boulder/01.jpg', + alt: 'Oil-rubbed bronze faucet over a quartz counter, reclaimed wood backsplash', + orientation: 'portrait', + feature: 'narrow', + }, + { + src: '/images/boulder/07.jpg', + alt: 'Kitchen alternate angle showing live-edge counter and Asian carving', + orientation: 'landscape', + feature: 'wide', + }, + { + src: '/images/boulder/02.jpg', + alt: 'Dining room with reclaimed barnwood feature wall and rush-seat chairs', + orientation: 'landscape', + feature: 'wide', + }, + { + src: '/images/boulder/08.jpg', + alt: 'Living room with mid-century sofa and emerald tile fireplace surround', + orientation: 'landscape', + feature: 'wide', + }, + { + src: '/images/boulder/05.jpg', + alt: 'Walnut cabinets with white embossed tile and glass pendant', + orientation: 'portrait', + feature: 'narrow', + }, + { + src: '/images/boulder/09.jpg', + alt: 'Bathroom with emerald subway tile shower and walnut vanity', + orientation: 'portrait', + feature: 'paired', + }, + { + src: '/images/boulder/06.jpg', + alt: 'Emerald subway tile shower with bronze fittings and white niche', + orientation: 'landscape', + feature: 'paired', + }, + { + src: '/images/boulder/12.jpg', + alt: 'Walnut bath vanity with reclaimed wood mirror and white tile backsplash', + orientation: 'portrait', + feature: 'narrow', + }, + { + src: '/images/boulder/10.jpg', + alt: 'Rear deck and patio at golden hour with mountain view', + orientation: 'landscape', + feature: 'wide', + }, + { + src: '/images/boulder/11.jpg', + alt: 'Rear deck at dusk with warm lit windows', + orientation: 'landscape', + feature: 'wide', + }, + { + src: '/images/boulder/04.jpg', + alt: 'Front of the home under a winter moon at dusk', + orientation: 'landscape', + feature: 'wide', + }, + ], +} + +export const asheville: Project = { + slug: 'asheville', + name: 'Asheville', + eyebrow: 'Architectural and moody', + intro: + 'A counterpoint to Boulder — black vertical slats, matte black appliances, ' + + 'and large picture windows held in balance by warm wood floors and layered ' + + 'textiles. A space that is restrained but never cold.', + cover: '/images/asheville/01-living.jpg', + coverAlt: 'Living room with black vertical slat wall and picture window', + images: [ + { + src: '/images/asheville/01-living.jpg', + alt: 'Living room with black vertical slat wall and oversized window', + orientation: 'portrait', + feature: 'hero', + }, + { + src: '/images/asheville/02-kitchen.jpg', + alt: 'Galley kitchen with matte black appliances and layered Persian rug', + orientation: 'portrait', + feature: 'narrow', + }, + { + src: '/images/asheville/04-dining-wide.jpg', + alt: 'Dining nook with vertical slat wall and trailing monstera', + orientation: 'portrait', + feature: 'narrow', + }, + { + src: '/images/asheville/05-dining-detail.jpg', + alt: 'Dining table beneath a Nelson Saucer pendant against vertical slat wall', + orientation: 'portrait', + feature: 'narrow', + }, + { + src: '/images/asheville/03-bath.jpg', + alt: 'Powder bath with layered glazed tile and matte black accents', + orientation: 'portrait', + feature: 'narrow', + }, + ], +} + +export const projects: Project[] = [boulder, asheville] + +export function getProject(slug: string): Project | undefined { + return projects.find((p) => p.slug === slug) +} diff --git a/src/router/index.ts b/src/router/index.ts index 4f315f4..e30467c 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -6,11 +6,39 @@ const routes: RouteRecordRaw[] = [ name: 'home', component: () => import('@/views/HomeView.vue'), }, + { + path: '/portfolio', + name: 'portfolio', + component: () => import('@/views/PortfolioView.vue'), + }, + { + path: '/portfolio/boulder', + name: 'portfolio-boulder', + component: () => import('@/views/projects/BoulderView.vue'), + }, + { + path: '/portfolio/asheville', + name: 'portfolio-asheville', + component: () => import('@/views/projects/AshevilleView.vue'), + }, + { + path: '/contact', + name: 'contact', + component: () => import('@/views/ContactView.vue'), + }, + { + path: '/:pathMatch(.*)*', + redirect: '/', + }, ] const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes, + scrollBehavior(_to, _from, saved) { + if (saved) return saved + return { top: 0 } + }, }) export default router diff --git a/src/views/ContactView.vue b/src/views/ContactView.vue new file mode 100644 index 0000000..7148aae --- /dev/null +++ b/src/views/ContactView.vue @@ -0,0 +1,11 @@ + + + diff --git a/src/views/PortfolioView.vue b/src/views/PortfolioView.vue new file mode 100644 index 0000000..5143a92 --- /dev/null +++ b/src/views/PortfolioView.vue @@ -0,0 +1,48 @@ + + + diff --git a/src/views/projects/AshevilleView.vue b/src/views/projects/AshevilleView.vue new file mode 100644 index 0000000..af54897 --- /dev/null +++ b/src/views/projects/AshevilleView.vue @@ -0,0 +1,8 @@ + + + diff --git a/src/views/projects/BoulderView.vue b/src/views/projects/BoulderView.vue new file mode 100644 index 0000000..d9bfa0d --- /dev/null +++ b/src/views/projects/BoulderView.vue @@ -0,0 +1,8 @@ + + +