diff --git a/src/assets/icons/photos.svg b/src/assets/icons/photos.svg
new file mode 100644
index 0000000..8c3b4fd
--- /dev/null
+++ b/src/assets/icons/photos.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/src/lib/components/PhotoGrid.svelte b/src/lib/components/PhotoGrid.svelte
new file mode 100644
index 0000000..172f341
--- /dev/null
+++ b/src/lib/components/PhotoGrid.svelte
@@ -0,0 +1,88 @@
+
+
+
+
+ {#each photoItems as item}
+
+ {/each}
+
+
+
+{#if lightboxPhoto}
+
+{/if}
+
+
\ No newline at end of file
diff --git a/src/lib/components/PhotoItem.svelte b/src/lib/components/PhotoItem.svelte
new file mode 100644
index 0000000..e21a2f9
--- /dev/null
+++ b/src/lib/components/PhotoItem.svelte
@@ -0,0 +1,255 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/lib/components/PhotoLightbox.svelte b/src/lib/components/PhotoLightbox.svelte
new file mode 100644
index 0000000..7716655
--- /dev/null
+++ b/src/lib/components/PhotoLightbox.svelte
@@ -0,0 +1,364 @@
+
+
+
+
+
+
+
+
+
+
+ {#if hasNavigation}
+
+
+ {/if}
+
+
+
+

+ {#if !imageLoaded}
+
+ {/if}
+
+
+
+
+ {#if photo.caption}
+
{photo.caption}
+ {/if}
+
+ {#if hasExifData}
+
+
Camera Settings
+
+ {#each Object.entries(photo.exif) as [key, value]}
+
+
- {getExifLabel(key)}
+ - {formatExifValue(key, value)}
+
+ {/each}
+
+
+ {/if}
+
+ {#if hasNavigation}
+
+ {currentIndex + 1} of {albumPhotos.length}
+
+ {/if}
+
+
+
+
+
\ No newline at end of file
diff --git a/src/lib/components/SegmentedController.svelte b/src/lib/components/SegmentedController.svelte
index e2dd8e3..38c1c06 100644
--- a/src/lib/components/SegmentedController.svelte
+++ b/src/lib/components/SegmentedController.svelte
@@ -2,6 +2,7 @@
import WorkIcon from '$icons/work.svg'
import LabsIcon from '$icons/labs.svg'
import UniverseIcon from '$icons/universe.svg'
+ import PhotosIcon from '$icons/photos.svg'
import { page } from '$app/stores'
const currentPath = $derived($page.url.pathname)
@@ -10,11 +11,12 @@
icon: typeof WorkIcon
text: string
href: string
- variant: 'work' | 'universe' | 'labs'
+ variant: 'work' | 'universe' | 'labs' | 'photos'
}
const navItems: NavItem[] = [
{ icon: WorkIcon, text: 'Work', href: '/', variant: 'work' },
+ { icon: PhotosIcon, text: 'Photos', href: '/photos', variant: 'photos' },
{ icon: LabsIcon, text: 'Labs', href: '#', variant: 'labs' },
{ icon: UniverseIcon, text: 'Universe', href: '/universe', variant: 'universe' }
]
@@ -25,7 +27,8 @@
// Calculate active index based on current path
const activeIndex = $derived(
currentPath === '/' ? 0 :
- currentPath.startsWith('/universe') ? 2 :
+ currentPath.startsWith('/photos') ? 1 :
+ currentPath.startsWith('/universe') ? 3 :
-1
)
@@ -64,6 +67,7 @@
function getBgColor(variant: string): string {
switch (variant) {
case 'work': return '#ffcdc5' // $work-bg
+ case 'photos': return '#e8c5ff' // $photos-bg (purple)
case 'universe': return '#ffebc5' // $universe-bg
case 'labs': return '#c5eaff' // $labs-bg
default: return '#c5eaff'
@@ -74,6 +78,7 @@
function getTextColor(variant: string): string {
switch (variant) {
case 'work': return '#d0290d' // $work-color
+ case 'photos': return '#7c3aed' // $photos-color (purple)
case 'universe': return '#b97d14' // $universe-color
case 'labs': return '#1482c1' // $labs-color
default: return '#1482c1'
@@ -157,18 +162,23 @@
}
// Different animations for each nav item
- // First item after the sliding pill is Work (index 1)
+ // First item is Work (index 1)
.nav-item:nth-of-type(1) :global(svg.animate) {
animation: cursorWiggle 0.6s ease;
}
- // Second item is Labs (index 2)
+ // Second item is Photos (index 2)
.nav-item:nth-of-type(2) :global(svg.animate) {
+ animation: photoFlash 0.6s ease;
+ }
+
+ // Third item is Labs (index 3)
+ .nav-item:nth-of-type(3) :global(svg.animate) {
animation: tubeBubble 0.6s ease;
}
- // Third item is Universe (index 3)
- .nav-item:nth-of-type(3) :global(svg.animate) {
+ // Fourth item is Universe (index 4)
+ .nav-item:nth-of-type(4) :global(svg.animate) {
animation: starSpin 0.6s ease;
}
@@ -178,6 +188,11 @@
75% { transform: rotate(8deg) scale(1.05); }
}
+ @keyframes photoFlash {
+ 0%, 100% { transform: scale(1); filter: brightness(1); }
+ 50% { transform: scale(1.1); filter: brightness(1.3); }
+ }
+
@keyframes tubeBubble {
0%, 100% { transform: translateY(0) scale(1); }
50% { transform: translateY(-2px) scale(1.1); }
diff --git a/src/lib/types/photos.ts b/src/lib/types/photos.ts
new file mode 100644
index 0000000..45c21e4
--- /dev/null
+++ b/src/lib/types/photos.ts
@@ -0,0 +1,35 @@
+export interface ExifData {
+ camera?: string
+ lens?: string
+ focalLength?: string
+ aperture?: string
+ shutterSpeed?: string
+ iso?: string
+ dateTaken?: string
+ location?: string
+}
+
+export interface Photo {
+ id: string
+ src: string
+ alt: string
+ caption?: string
+ width: number
+ height: number
+ exif?: ExifData
+}
+
+export interface PhotoAlbum {
+ id: string
+ title: string
+ description?: string
+ coverPhoto: Photo
+ photos: Photo[]
+ createdAt: string
+}
+
+export type PhotoItem = Photo | PhotoAlbum
+
+export function isAlbum(item: PhotoItem): item is PhotoAlbum {
+ return 'photos' in item && Array.isArray(item.photos)
+}
\ No newline at end of file
diff --git a/src/routes/photos/+page.svelte b/src/routes/photos/+page.svelte
new file mode 100644
index 0000000..e64eab4
--- /dev/null
+++ b/src/routes/photos/+page.svelte
@@ -0,0 +1,16 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/src/routes/photos/+page.ts b/src/routes/photos/+page.ts
new file mode 100644
index 0000000..f08f442
--- /dev/null
+++ b/src/routes/photos/+page.ts
@@ -0,0 +1,413 @@
+import type { PageLoad } from './$types'
+import type { PhotoItem } from '$lib/types/photos'
+
+export const load: PageLoad = async () => {
+ // Mock data for now - in a real app this would come from an API or CMS
+ const photoItems: PhotoItem[] = [
+ {
+ id: 'photo-1',
+ src: 'https://picsum.photos/400/600?random=1',
+ alt: 'Mountain landscape at sunset',
+ caption: 'A beautiful landscape captured during golden hour',
+ width: 400,
+ height: 600,
+ exif: {
+ camera: 'Canon EOS R5',
+ lens: '24-70mm f/2.8',
+ focalLength: '35mm',
+ aperture: 'f/5.6',
+ shutterSpeed: '1/250s',
+ iso: '100',
+ dateTaken: '2024-01-15',
+ location: 'Yosemite National Park'
+ }
+ },
+ {
+ id: 'album-1',
+ title: 'Tokyo Street Photography',
+ description: 'A collection of street photography from Tokyo',
+ coverPhoto: {
+ id: 'album-1-cover',
+ src: 'https://picsum.photos/500/400?random=2',
+ alt: 'Tokyo street scene',
+ width: 500,
+ height: 400
+ },
+ photos: [
+ {
+ id: 'album-1-1',
+ src: 'https://picsum.photos/500/400?random=2',
+ alt: 'Tokyo street scene',
+ caption: 'Busy intersection in Shibuya',
+ width: 500,
+ height: 400,
+ exif: {
+ camera: 'Fujifilm X-T4',
+ lens: '23mm f/1.4',
+ focalLength: '23mm',
+ aperture: 'f/2.8',
+ shutterSpeed: '1/60s',
+ iso: '800',
+ dateTaken: '2024-02-10'
+ }
+ },
+ {
+ id: 'album-1-2',
+ src: 'https://picsum.photos/600/400?random=25',
+ alt: 'Tokyo alley',
+ caption: 'Quiet alley in Shinjuku',
+ width: 600,
+ height: 400
+ },
+ {
+ id: 'album-1-3',
+ src: 'https://picsum.photos/500/700?random=26',
+ alt: 'Neon signs',
+ caption: 'Colorful neon signs in Harajuku',
+ width: 500,
+ height: 700,
+ exif: {
+ camera: 'Fujifilm X-T4',
+ lens: '56mm f/1.2',
+ focalLength: '56mm',
+ aperture: 'f/1.8',
+ shutterSpeed: '1/30s',
+ iso: '1600',
+ dateTaken: '2024-02-11'
+ }
+ }
+ ],
+ createdAt: '2024-02-10'
+ },
+ {
+ id: 'photo-2',
+ src: 'https://picsum.photos/300/500?random=4',
+ alt: 'Modern building',
+ caption: 'Urban architecture study',
+ width: 300,
+ height: 500,
+ exif: {
+ camera: 'Sony A7IV',
+ lens: '85mm f/1.8',
+ focalLength: '85mm',
+ aperture: 'f/2.8',
+ shutterSpeed: '1/125s',
+ iso: '200',
+ dateTaken: '2024-01-20'
+ }
+ },
+ {
+ id: 'photo-3',
+ src: 'https://picsum.photos/600/300?random=5',
+ alt: 'Ocean waves',
+ caption: 'Minimalist seascape composition',
+ width: 600,
+ height: 300
+ },
+ {
+ id: 'photo-4',
+ src: 'https://picsum.photos/400/500?random=6',
+ alt: 'Forest path',
+ caption: 'Morning light through the trees',
+ width: 400,
+ height: 500,
+ exif: {
+ camera: 'Nikon Z6II',
+ lens: '24-120mm f/4',
+ focalLength: '50mm',
+ aperture: 'f/8',
+ shutterSpeed: '1/60s',
+ iso: '400',
+ dateTaken: '2024-03-05',
+ location: 'Redwood National Park'
+ }
+ },
+ {
+ id: 'album-2',
+ title: 'Portrait Series',
+ description: 'A collection of environmental portraits',
+ coverPhoto: {
+ id: 'album-2-cover',
+ src: 'https://picsum.photos/400/600?random=7',
+ alt: 'Portrait of a musician',
+ width: 400,
+ height: 600
+ },
+ photos: [
+ {
+ id: 'album-2-1',
+ src: 'https://picsum.photos/400/600?random=7',
+ alt: 'Portrait of a musician',
+ caption: 'Jazz musician in his studio',
+ width: 400,
+ height: 600,
+ exif: {
+ camera: 'Canon EOS R6',
+ lens: '85mm f/1.2',
+ focalLength: '85mm',
+ aperture: 'f/1.8',
+ shutterSpeed: '1/125s',
+ iso: '640',
+ dateTaken: '2024-02-20'
+ }
+ },
+ {
+ id: 'album-2-2',
+ src: 'https://picsum.photos/500/650?random=27',
+ alt: 'Artist in gallery',
+ caption: 'Painter surrounded by her work',
+ width: 500,
+ height: 650
+ },
+ {
+ id: 'album-2-3',
+ src: 'https://picsum.photos/450/600?random=28',
+ alt: 'Chef in kitchen',
+ caption: 'Chef preparing for evening service',
+ width: 450,
+ height: 600
+ }
+ ],
+ createdAt: '2024-02-20'
+ },
+ {
+ id: 'photo-5',
+ src: 'https://picsum.photos/500/350?random=8',
+ alt: 'City skyline',
+ caption: 'Downtown at blue hour',
+ width: 500,
+ height: 350,
+ exif: {
+ camera: 'Sony A7R V',
+ lens: '16-35mm f/2.8',
+ focalLength: '24mm',
+ aperture: 'f/11',
+ shutterSpeed: '8s',
+ iso: '100',
+ dateTaken: '2024-01-30',
+ location: 'San Francisco'
+ }
+ },
+ {
+ id: 'photo-6',
+ src: 'https://picsum.photos/350/550?random=9',
+ alt: 'Vintage motorcycle',
+ caption: 'Classic bike restoration project',
+ width: 350,
+ height: 550
+ },
+ {
+ id: 'photo-7',
+ src: 'https://picsum.photos/450/300?random=10',
+ alt: 'Coffee and books',
+ caption: 'Quiet morning ritual',
+ width: 450,
+ height: 300,
+ exif: {
+ camera: 'Fujifilm X100V',
+ lens: '23mm f/2',
+ focalLength: '23mm',
+ aperture: 'f/2.8',
+ shutterSpeed: '1/60s',
+ iso: '320',
+ dateTaken: '2024-03-01'
+ }
+ },
+ {
+ id: 'album-3',
+ title: 'Nature Macro',
+ description: 'Close-up studies of natural details',
+ coverPhoto: {
+ id: 'album-3-cover',
+ src: 'https://picsum.photos/400/400?random=11',
+ alt: 'Dewdrop on leaf',
+ width: 400,
+ height: 400
+ },
+ photos: [
+ {
+ id: 'album-3-1',
+ src: 'https://picsum.photos/400/400?random=11',
+ alt: 'Dewdrop on leaf',
+ caption: 'Morning dew captured with macro lens',
+ width: 400,
+ height: 400,
+ exif: {
+ camera: 'Canon EOS R5',
+ lens: '100mm f/2.8 Macro',
+ focalLength: '100mm',
+ aperture: 'f/5.6',
+ shutterSpeed: '1/250s',
+ iso: '200',
+ dateTaken: '2024-03-15'
+ }
+ },
+ {
+ id: 'album-3-2',
+ src: 'https://picsum.photos/500/500?random=29',
+ alt: 'Butterfly wing detail',
+ caption: 'Intricate patterns on butterfly wing',
+ width: 500,
+ height: 500
+ },
+ {
+ id: 'album-3-3',
+ src: 'https://picsum.photos/400/600?random=30',
+ alt: 'Tree bark texture',
+ caption: 'Ancient oak bark patterns',
+ width: 400,
+ height: 600
+ },
+ {
+ id: 'album-3-4',
+ src: 'https://picsum.photos/350/500?random=31',
+ alt: 'Flower stamen',
+ caption: 'Lily stamen in soft light',
+ width: 350,
+ height: 500
+ }
+ ],
+ createdAt: '2024-03-15'
+ },
+ {
+ id: 'photo-8',
+ src: 'https://picsum.photos/600/400?random=12',
+ alt: 'Desert landscape',
+ caption: 'Vast desert under starry sky',
+ width: 600,
+ height: 400,
+ exif: {
+ camera: 'Nikon D850',
+ lens: '14-24mm f/2.8',
+ focalLength: '14mm',
+ aperture: 'f/2.8',
+ shutterSpeed: '25s',
+ iso: '3200',
+ dateTaken: '2024-02-25',
+ location: 'Death Valley'
+ }
+ },
+ {
+ id: 'photo-9',
+ src: 'https://picsum.photos/300/450?random=13',
+ alt: 'Vintage camera',
+ caption: 'My grandfather\'s Leica',
+ width: 300,
+ height: 450
+ },
+ {
+ id: 'photo-10',
+ src: 'https://picsum.photos/550/350?random=14',
+ alt: 'Market scene',
+ caption: 'Colorful spices at local market',
+ width: 550,
+ height: 350,
+ exif: {
+ camera: 'Fujifilm X-T5',
+ lens: '18-55mm f/2.8-4',
+ focalLength: '35mm',
+ aperture: 'f/4',
+ shutterSpeed: '1/125s',
+ iso: '800',
+ dateTaken: '2024-03-10',
+ location: 'Marrakech, Morocco'
+ }
+ },
+ {
+ id: 'photo-11',
+ src: 'https://picsum.photos/400/600?random=15',
+ alt: 'Lighthouse at dawn',
+ caption: 'Coastal beacon in morning mist',
+ width: 400,
+ height: 600,
+ exif: {
+ camera: 'Sony A7III',
+ lens: '70-200mm f/2.8',
+ focalLength: '135mm',
+ aperture: 'f/8',
+ shutterSpeed: '1/200s',
+ iso: '400',
+ dateTaken: '2024-02-28',
+ location: 'Big Sur, California'
+ }
+ },
+ {
+ id: 'photo-12',
+ src: 'https://picsum.photos/500/300?random=16',
+ alt: 'Train station',
+ caption: 'Rush hour commuters',
+ width: 500,
+ height: 300
+ },
+ {
+ id: 'album-4',
+ title: 'Black & White',
+ description: 'Monochrome photography collection',
+ coverPhoto: {
+ id: 'album-4-cover',
+ src: 'https://picsum.photos/450/600?random=17',
+ alt: 'Urban shadows',
+ width: 450,
+ height: 600
+ },
+ photos: [
+ {
+ id: 'album-4-1',
+ src: 'https://picsum.photos/450/600?random=17',
+ alt: 'Urban shadows',
+ caption: 'Dramatic shadows in the financial district',
+ width: 450,
+ height: 600,
+ exif: {
+ camera: 'Leica M11 Monochrom',
+ lens: '35mm f/1.4',
+ focalLength: '35mm',
+ aperture: 'f/5.6',
+ shutterSpeed: '1/250s',
+ iso: '200',
+ dateTaken: '2024-03-20'
+ }
+ },
+ {
+ id: 'album-4-2',
+ src: 'https://picsum.photos/600/400?random=32',
+ alt: 'Elderly man reading',
+ caption: 'Contemplation in the park',
+ width: 600,
+ height: 400
+ },
+ {
+ id: 'album-4-3',
+ src: 'https://picsum.photos/400/500?random=33',
+ alt: 'Rain on window',
+ caption: 'Storm patterns on glass',
+ width: 400,
+ height: 500
+ }
+ ],
+ createdAt: '2024-03-20'
+ },
+ {
+ id: 'photo-13',
+ src: 'https://picsum.photos/350/500?random=18',
+ alt: 'Street art mural',
+ caption: 'Vibrant wall art in the Mission District',
+ width: 350,
+ height: 500,
+ exif: {
+ camera: 'iPhone 15 Pro',
+ lens: '24mm f/1.8',
+ focalLength: '24mm',
+ aperture: 'f/1.8',
+ shutterSpeed: '1/120s',
+ iso: '64',
+ dateTaken: '2024-03-25',
+ location: 'San Francisco'
+ }
+ }
+ ]
+
+ return {
+ photoItems
+ }
+}
\ No newline at end of file