From 614aad4a8bd35c513c728d96fe0627e1b36ac2e6 Mon Sep 17 00:00:00 2001 From: mifi Date: Sat, 14 Feb 2026 19:45:11 -0300 Subject: [PATCH] Some tweaks to improve CWV --- .prettierrc | 4 +- eslint.config.js | 10 ++- src/assets/js/ga-init.js | 20 +++--- src/assets/js/script.js | 144 ++++++++++++++++++++------------------- src/index.html | 88 ++++++++++++++++++------ 5 files changed, 163 insertions(+), 103 deletions(-) diff --git a/.prettierrc b/.prettierrc index fbb5b7a..81507a8 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,8 +1,8 @@ { - "semi": false, + "semi": true, "singleQuote": true, "tabWidth": 4, - "trailingComma": "none", + "trailingComma": "all", "overrides": [ { "files": "*.yml", diff --git a/eslint.config.js b/eslint.config.js index 88e0620..c7c357f 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -13,7 +13,15 @@ export default [ } }, rules: { - 'no-unused-vars': ['warn', { argsIgnorePattern: '^_' }] + 'no-undef': 'warn', + 'no-unused-vars': ['warn', { argsIgnorePattern: '^_' }], + 'no-var': 'warn', + 'prefer-arrow-callback': 'warn', + 'prefer-const': 'warn', + 'prefer-destructuring': 'warn', + 'prefer-rest-params': 'warn', + 'prefer-spread': 'warn', + 'prefer-template': 'warn', } }, prettierConfig diff --git a/src/assets/js/ga-init.js b/src/assets/js/ga-init.js index 3820ff4..e3038b3 100644 --- a/src/assets/js/ga-init.js +++ b/src/assets/js/ga-init.js @@ -1,11 +1,11 @@ -;(function () { - var script = document.currentScript - var id = script && script.getAttribute('data-ga-id') - if (!id) return - window.dataLayer = window.dataLayer || [] - function gtag() { - window.dataLayer.push(arguments) +(function () { + const script = document.currentScript; + const id = script && script.getAttribute('data-ga-id'); + if (!id) return; + window.dataLayer = window.dataLayer || []; + function gtag(...args) { + window.dataLayer.push(...args); } - gtag('js', new Date()) - gtag('config', id, { anonymize_ip: true }) -})() + gtag('js', new Date()); + gtag('config', id, { anonymize_ip: true }); +})(); diff --git a/src/assets/js/script.js b/src/assets/js/script.js index 4bdb6c2..615475b 100644 --- a/src/assets/js/script.js +++ b/src/assets/js/script.js @@ -1,115 +1,119 @@ // --- theme toggle --- -const toggle = document.getElementById('theme-toggle') -const root = document.documentElement -const saved = localStorage.getItem('dark-mode') -const sysDark = window.matchMedia('(prefers-color-scheme: dark)').matches +const toggle = document.getElementById('theme-toggle'); +const root = document.documentElement; +const saved = window?.localStorage?.getItem('dark-mode'); +const sysDark = window.matchMedia('(prefers-color-scheme: dark)').matches; if (saved === 'true') { - root.classList.add('dark') - root.classList.remove('light') + root.classList.add('dark'); + root.classList.remove('light'); } else if (saved === 'false') { - root.classList.add('light') - root.classList.remove('dark') + root.classList.add('light'); + root.classList.remove('dark'); } else { if (sysDark) { - root.classList.add('dark') - root.classList.remove('light') + root.classList.add('dark'); + root.classList.remove('light'); } else { - root.classList.add('light') - root.classList.remove('dark') + root.classList.add('light'); + root.classList.remove('dark'); } } toggle.addEventListener('click', () => { - const isDark = root.classList.contains('dark') - root.classList.toggle('dark', !isDark) - root.classList.toggle('light', isDark) - localStorage.setItem('dark-mode', !isDark) -}) + const isDark = root.classList.contains('dark'); + root.classList.toggle('dark', !isDark); + root.classList.toggle('light', isDark); + window?.localStorage?.setItem('dark-mode', !isDark); +}); -const body = document.body +const { body } = document; // --- lightbox base --- -const lb = document.getElementById('lightbox') -const lbCnt = document.getElementById('lb-content') -const lbCap = document.getElementById('lb-caption') +const lb = document.getElementById('lightbox'); +const lbCnt = document.getElementById('lb-content'); +const lbCap = document.getElementById('lb-caption'); document.getElementById('lb-close').addEventListener('click', () => { - lb.setAttribute('aria-hidden', 'true') - body.classList.remove('lightbox-open') - lbCnt.innerHTML = '' -}) + lb.setAttribute('aria-hidden', 'true'); + body.classList.remove('lightbox-open'); + lbCnt.innerHTML = ''; +}); // --- build gallery --- -const gallery = document.getElementById('gallery') -const mediaData = JSON.parse(document.getElementById('media-data').textContent) +const gallery = document.getElementById('gallery'); +const mediaData = JSON.parse(document.getElementById('media-data').textContent); const createPicture = (item) => { - const pic = document.createElement('picture') - ;['desktop', 'tablet', 'mobile'].forEach((bp) => { - const src = document.createElement('source') - const widthQuery = bp === 'desktop' ? 1024 : bp === 'tablet' ? 768 : 0 - src.media = `(min-width:${widthQuery}px)` + const pic = document.createElement('picture'); + ['desktop', 'tablet', 'mobile'].forEach((bp) => { + const src = document.createElement('source'); + const widthQuery = bp === 'desktop' ? 1024 : bp === 'tablet' ? 768 : 0; + src.media = `(min-width:${widthQuery}px)`; if (item.type === 'image') { src.srcset = `assets/media/${bp}/${item.name}@1x.webp 1x, ` + - `assets/media/${bp}/${item.name}.webp 2x` + `assets/media/${bp}/${item.name}.webp 2x`; } else { // video poster still src.srcset = `assets/media/${bp}/${item.name}_still@1x.webp 1x, ` + - `assets/media/${bp}/${item.name}_still.webp 2x` + `assets/media/${bp}/${item.name}_still.webp 2x`; } - pic.appendChild(src) - }) + pic.appendChild(src); + }); // thumbnail fallback (always 300px/2×) - const img = document.createElement('img') - img.src = `assets/media/thumbnail/${item.name}.webp` - img.alt = item.alt.replace(/[‘’]/g, '') - pic.appendChild(img) - return pic -} + const img = document.createElement('img'); + img.src = `assets/media/thumbnail/${item.name}.webp`; + img.alt = item.alt.replace(/[‘’]/g, ''); + img.height = item.height || 300; + img.width = item.width || 300; + img.loading = item.loading || 'lazy'; + pic.appendChild(img); + return pic; +}; mediaData.forEach((item) => { - const fig = document.createElement('figure') - fig.className = `gallery-item${item.type === 'video' ? ' video' : ''}` - fig.tabIndex = 0 - fig.dataset.name = item.name - fig.dataset.type = item.type - fig.dataset.caption = item.caption - fig.appendChild(createPicture(item)) + const fig = document.createElement('figure'); + fig.className = `gallery-item${item.type === 'video' ? ' video' : ''}`; + fig.tabIndex = 0; + fig.dataset.name = item.name; + fig.dataset.type = item.type; + fig.dataset.caption = item.caption; + fig.appendChild(createPicture(item)); // overlay caption - const cap = document.createElement('figcaption') - cap.textContent = item.caption - fig.appendChild(cap) + const cap = document.createElement('figcaption'); + cap.textContent = item.caption; + fig.appendChild(cap); // events - fig.addEventListener('click', () => openLightbox(item)) + fig.addEventListener('click', () => openLightbox(item)); fig.addEventListener( 'keypress', - (e) => e.key === 'Enter' && openLightbox(item) - ) + (e) => e.key === 'Enter' && openLightbox(item), + ); - gallery.appendChild(fig) -}) + gallery.appendChild(fig); +}); // --- video toggle --- -const videoTgl = document.getElementById('show_video') +const videoTgl = document.getElementById('show_video'); videoTgl.addEventListener('click', () => { - openLightbox(mediaData.find((i) => i.type === 'video')) -}) + openLightbox(mediaData.find((i) => i.type === 'video')); +}); function openLightbox(item) { - lbCnt.innerHTML = '' + lbCnt.innerHTML = ''; if (item.type === 'video') { - const v = document.createElement('video') - v.src = `assets/media/videos/${item.name}.mp4` - v.controls = true - v.autoplay = true - lbCnt.appendChild(v) + const v = document.createElement('video'); + v.src = `assets/media/videos/${item.name}.mp4`; + v.controls = true; + v.autoplay = true; + v.loading = item.loading || 'lazy'; + lbCnt.appendChild(v); } else { - lbCnt.appendChild(createPicture(item)) + lbCnt.appendChild(createPicture(item)); } - lbCap.textContent = item.caption - body.classList.add('lightbox-open') - lb.setAttribute('aria-hidden', 'false') + lbCap.textContent = item.caption; + body.classList.add('lightbox-open'); + lb.setAttribute('aria-hidden', 'false'); } diff --git a/src/index.html b/src/index.html index c0550c6..7e234da 100644 --- a/src/index.html +++ b/src/index.html @@ -15,6 +15,11 @@ 64 Armandine St #3 Boston, Massachusetts + + + @@ -71,121 +77,163 @@ "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." + "alt": "Sunny living room with stylish seating and vibrant artwork.", + "height": 200, + "width": 300, + "loading": "eager" }, { "type": "image", "name": "living_room_2", "caption": "Relaxation elevated—your stylish living space awaits.", - "alt": "Spacious living area featuring elegant furniture and tasteful decor." + "alt": "Spacious living area featuring elegant furniture and tasteful decor.", + "height": 200, + "width": 300 }, { "type": "image", "name": "kitchen", "caption": "The culinary stage is set—snacking encouraged, style required.", - "alt": "Modern kitchen showcasing sleek appliances and contemporary design." + "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." + "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." + "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." + "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." + "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." + "alt": "Private ensuite bathroom featuring contemporary design and premium finishes.", + "height": 450, + "width": 300, + "loading": "eager" }, { "type": "image", "name": "onsuite_2", "caption": "Everyday luxury, right at home—your ensuite oasis.", - "alt": "Elegant ensuite with sleek fixtures and stylish decor." + "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." + "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." + "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." + "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." + "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." + "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." + "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." + "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." + "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." + "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." + "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." + "alt": "Video tour showcasing the property.", + "height": 534, + "width": 300 } ]