Svelte conversion — quick and dirty
Some checks failed
ci/woodpecker/pr/ci Pipeline failed

This commit is contained in:
2026-02-15 14:10:57 -03:00
parent e712e73902
commit 99cb89d1e8
198 changed files with 3192 additions and 1193 deletions

View File

@@ -0,0 +1,83 @@
<script lang="ts">
import type { MediaItem } from '$lib/media.js';
interface Props {
index?: number;
item: MediaItem;
}
let { item, index }: Props = $props();
</script>
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
<figure
class="gallery-item{item.type === 'video' ? ' video' : ''}"
tabindex="0"
data-name={item.name}
data-type={item.type}
data-caption={item.caption}
>
<picture>
{#each [
{ bp: 'desktop', minWidth: 1024 },
{ bp: 'tablet', minWidth: 768 },
{ bp: 'mobile', minWidth: 0 }
] as breakpoint}
<source
media="(min-width:{breakpoint.minWidth}px)"
srcset={item.type === 'image'
? `/assets/media/${breakpoint.bp}/${item.name}@1x.webp 1x, /assets/media/${breakpoint.bp}/${item.name}.webp 2x`
: `/assets/media/${breakpoint.bp}/${item.name}_still@1x.webp 1x, /assets/media/${breakpoint.bp}/${item.name}_still.webp 2x`}
/>
{/each}
<img
src="/assets/media/thumbnail/{item.name}.webp"
alt={item.alt.replace(/['']/g, '')}
height={item.height ?? undefined}
width={item.width ?? undefined}
loading={index && index > 2 ? item.loading ?? 'lazy' : undefined}
fetchpriority={item.fetchpriority ?? undefined}
/>
</picture>
<figcaption>{item.caption}</figcaption>
</figure>
<style>
.gallery-item {
aspect-ratio: 3/2;
margin: 0 0 1rem;
position: relative;
cursor: pointer;
& img {
height: auto;
width: 100%;
display: block;
border-radius: 8px;
}
& figcaption {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
padding: 0.5rem;
background: rgb(0 0 0 / 60%);
color: #fff;
font-size: 0.9rem;
opacity: 0;
transition: opacity 0.2s;
}
&:focus-visible {
outline: 2px solid var(--accent);
}
&:focus,
&:hover {
& figcaption {
opacity: 1;
}
}
}
</style>

View File

@@ -0,0 +1,30 @@
<header class="site-header">
<h1>64 Armandine St #3 Boston, Massachusetts</h1>
<div class="buttons">
<button id="show_video" class="emoji-button" aria-label="Show video tour">
🎥
</button>
<button id="theme-toggle" class="emoji-button" aria-label="Toggle light/dark theme">
🌓
</button>
</div>
</header>
<style>
.site-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem;
background: var(--bg);
border-bottom: 1px solid #ccc;
}
.emoji-button {
background: none;
border: none;
font-size: 1.5rem;
cursor: pointer;
color: var(--accent);
}
</style>

178
src/lib/media.ts Normal file
View File

@@ -0,0 +1,178 @@
export interface MediaItem {
type: 'image' | 'video';
name: string;
caption: string;
alt: string;
height?: number;
width?: number;
loading?: 'lazy' | 'eager';
fetchpriority?: 'high' | 'low' | 'auto';
}
export const mediaItems: MediaItem[] = [
{
type: 'image',
name: 'living_room_1',
caption: 'An inviting blend of comfort and curated art—relaxation guaranteed.',
alt: 'Sunny living room with stylish seating and vibrant artwork.',
height: 200,
width: 300,
loading: 'eager',
fetchpriority: 'high'
},
{
type: 'image',
name: 'living_room_2',
caption: 'Relaxation elevated—your stylish living space awaits.',
alt: 'Spacious living area featuring elegant furniture and tasteful decor.',
height: 200,
width: 300,
fetchpriority: 'high'
},
{
type: 'image',
name: 'kitchen',
caption: 'The culinary stage is set—snacking encouraged, style required.',
alt: 'Modern kitchen showcasing sleek appliances and contemporary design.',
height: 200,
width: 300
},
{
type: 'image',
name: 'bedroom_suite_1',
caption: 'A bedroom suite designed to make snoozing irresistible.',
alt: 'Inviting bedroom suite with cozy bedding and warm lighting.',
height: 200,
width: 300
},
{
type: 'image',
name: 'bedroom_suite_2',
caption: 'Style meets comfort—sleeping in has never been easier.',
alt: 'Comfortable bedroom suite with elegant decor and soft tones.',
height: 200,
width: 300
},
{
type: 'image',
name: 'bedroom_suite_3',
caption: 'Where dreams get stylish—a bedroom that feels like home.',
alt: 'Welcoming bedroom with soothing colors and inviting ambiance.',
height: 200,
width: 300
},
{
type: 'image',
name: 'guest_bath',
caption: 'Your personal spa experience—right down the hall.',
alt: 'Sophisticated guest bathroom with modern fixtures and clean lines.',
height: 450,
width: 300
},
{
type: 'image',
name: 'onsuite_1',
caption: 'Luxury meets practicality—your private ensuite awaits.',
alt: 'Private ensuite bathroom featuring contemporary design and premium finishes.',
height: 450,
width: 300,
loading: 'eager',
fetchpriority: 'high'
},
{
type: 'image',
name: 'onsuite_2',
caption: 'Everyday luxury, right at home—your ensuite oasis.',
alt: 'Elegant ensuite with sleek fixtures and stylish decor.',
height: 200,
width: 300
},
{
type: 'image',
name: 'laundry',
caption: 'Laundry day reimagined—functional never looked so good.',
alt: 'Modern laundry room with washer, dryer, and organized storage.',
height: 450,
width: 300
},
{
type: 'image',
name: 'coat_closet',
caption: 'Organized and chic—your entryway\'s best friend.',
alt: 'Convenient coat closet with tidy storage solutions.',
height: 200,
width: 300
},
{
type: 'image',
name: 'deck_1',
caption: 'Outdoor comfort, just steps away—morning coffee optional.',
alt: 'Sunny deck with cozy seating and pleasant outdoor views.',
height: 450,
width: 300
},
{
type: 'image',
name: 'deck_2',
caption: 'Your fresh-air escape—ideal for relaxing evenings.',
alt: 'Comfortable deck area perfect for unwinding or entertaining.',
height: 200,
width: 300
},
{
type: 'image',
name: 'exterior',
caption: 'Curb appeal perfected—your new favorite place starts here.',
alt: 'Attractive home exterior with inviting architecture.',
height: 200,
width: 300
},
{
type: 'image',
name: 'backyard_parking',
caption: 'Convenience meets privacy—your personal backyard parking spot.',
alt: 'Private backyard parking area offering secure convenience.',
height: 200,
width: 300
},
{
type: 'image',
name: 'office_fitness_guest_1',
caption: 'Productivity zone meets fitness corner—multitasking done right.',
alt: 'Dual-purpose room featuring office setup and fitness equipment.',
height: 200,
width: 300
},
{
type: 'image',
name: 'office_fitness_guest_2',
caption: 'Work, workout, or unwind—the room of endless possibilities.',
alt: 'Versatile office and fitness area with modern amenities.',
height: 200,
width: 300
},
{
type: 'image',
name: 'office_fitness_guest_3',
caption: 'Stay focused or get fit—you decide.',
alt: 'Functional space combining a workspace and home fitness area.',
height: 200,
width: 300
},
{
type: 'image',
name: 'office_fitness_guest_4',
caption: 'Room for every routine—your workspace meets wellness.',
alt: 'Stylish office area seamlessly integrated with fitness features.',
height: 200,
width: 300
},
{
type: 'video',
name: 'tour',
caption: "Take the scenic route—explore your the home's highlights with a virtual walkthrough.",
alt: 'Video tour showcasing the property.',
height: 534,
width: 300
}
];