chore(website): share page metadata, model screenshot devices as records, honor reduced motion

Home, privacy, and scan each repeated the same 13 title/canonical/Open
Graph/Twitter tags; PageMetadata now emits them once with route-specific
title, description, url, and an optional noindex. Screenshots keeps one
record per device instead of four parallel arrays plus a lookup table.

FAQ hash jumps and screenshot arrows forced behavior: 'smooth' from JS,
which bypasses the prefers-reduced-motion override in layout.css; both
now defer to CSS scroll-behavior. Removes the unused adapter-auto
dependency.
This commit is contained in:
edde746
2026-09-03 11:36:29 +02:00
parent 1373c299df
commit 17c877c973
8 changed files with 92 additions and 123 deletions
-3
View File
@@ -9,7 +9,6 @@
},
"devDependencies": {
"@iconify/json": "^2.2.474",
"@sveltejs/adapter-auto": "^7.0.0",
"@sveltejs/enhanced-img": "^0.10.4",
"@sveltejs/kit": "^2.70.2",
"@sveltejs/vite-plugin-svelte": "^6.2.4",
@@ -215,8 +214,6 @@
"@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.11", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-LFuZUkjJ9iF7JZye/aG5XM0SFcQ5VyL0oVX4WJ9dc0Va3R3s0OauX1BESVCb+YN/ol8TAfqGDDAQsTG627Y5kw=="],
"@sveltejs/adapter-auto": ["@sveltejs/adapter-auto@7.0.0", "", { "peerDependencies": { "@sveltejs/kit": "^2.0.0" } }, "sha512-ImDWaErTOCkRS4Gt+5gZuymKFBobnhChXUZ9lhUZLahUgvA4OOvRzi3sahzYgbxGj5nkA6OV0GAW378+dl/gyw=="],
"@sveltejs/adapter-static": ["@sveltejs/adapter-static@3.0.10", "", { "peerDependencies": { "@sveltejs/kit": "^2.0.0" } }, "sha512-7D9lYFWJmB7zxZyTE/qxjksvMqzMuYrrsyh1f4AlZqeZeACPRySjbC3aFiY55wb1tWUaKOQG9PVbm74JcN2Iew=="],
"@sveltejs/enhanced-img": ["@sveltejs/enhanced-img@0.10.4", "", { "dependencies": { "magic-string": "^0.30.5", "sharp": "^0.34.1", "svelte-parse-markup": "^0.1.5", "vite-imagetools": "^9.0.3", "zimmerframe": "^1.1.2" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^6.0.0 || ^7.0.0", "svelte": "^5.0.0", "vite": "^6.3.0 || >=7.0.0" } }, "sha512-Am5nmAKUo7Nboqq7Dhtfn7dcXA087d7gIz6Vecn1opB41aJ680+0q9U9KvEcMgduOyeiwckTIOQOx4Mmq9GcvA=="],
-1
View File
@@ -15,7 +15,6 @@
},
"devDependencies": {
"@iconify/json": "^2.2.474",
"@sveltejs/adapter-auto": "^7.0.0",
"@sveltejs/enhanced-img": "^0.10.4",
"@sveltejs/kit": "^2.70.2",
"@sveltejs/vite-plugin-svelte": "^6.2.4",
+1 -1
View File
@@ -15,7 +15,7 @@
if (hashIndex !== -1) {
openIndex = hashIndex;
requestAnimationFrame(() => {
document.getElementById(hash)?.scrollIntoView({ behavior: "smooth", block: "center" });
document.getElementById(hash)?.scrollIntoView({ block: "center" });
});
}
});
@@ -0,0 +1,31 @@
<script lang="ts">
const image = 'https://plezy.app/og/plezy-social.png';
let {
title,
description,
url,
noindex = false,
}: { title: string; description: string; url: string; noindex?: boolean } = $props();
</script>
<svelte:head>
<title>{title}</title>
<meta name="description" content={description} />
{#if noindex}
<meta name="robots" content="noindex, nofollow" />
{/if}
<link rel="canonical" href={url} />
<meta property="og:type" content="website" />
<meta property="og:site_name" content="Plezy" />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:url" content={url} />
<meta property="og:image" content={image} />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={title} />
<meta name="twitter:description" content={description} />
<meta name="twitter:image" content={image} />
</svelte:head>
+53 -66
View File
@@ -27,75 +27,62 @@
type DeviceType = 'phone' | 'tablet' | 'desktop' | 'tv';
type DeviceIconComponent = typeof DevicePhoneIcon | typeof DeviceTabletIcon | typeof DesktopIcon | typeof TvIcon;
const devices: { id: DeviceType; icon: DeviceIconComponent; label: string }[] = [
{ id: 'phone', icon: DevicePhoneIcon, label: 'Phone' },
{ id: 'tablet', icon: DeviceTabletIcon, label: 'Tablet' },
{ id: 'desktop', icon: DesktopIcon, label: 'Desktop' },
{ id: 'tv', icon: TvIcon, label: 'TV' },
];
const phoneShots = [
{ image: phoneHomeImage, alt: 'Plezy home screen' },
{ image: phoneLibraryImage, alt: 'Plezy library view' },
{ image: phoneMdImage, alt: 'Plezy media details' },
{ image: phoneSearchImage, alt: 'Plezy search' },
];
const tabletShots = [
{ image: tabletHomeImage, alt: 'Plezy on tablet - home' },
{ image: tabletLibraryImage, alt: 'Plezy on tablet - library' },
{ image: tabletMdImage, alt: 'Plezy on tablet - media details' },
{ image: tabletPlayerImage, alt: 'Plezy on tablet - video player' },
];
const desktopShots = [
{ image: desktopHomeImage, alt: 'Plezy on desktop - home' },
{ image: desktopLibraryImage, alt: 'Plezy on desktop - library' },
{ image: desktopMdImage, alt: 'Plezy on desktop - media details' },
{ image: desktopPlayerImage, alt: 'Plezy on desktop - video player' },
];
const tvShots = [
{ image: tvHomeImage, alt: 'Plezy on TV - home' },
{ image: tvLibraryImage, alt: 'Plezy on TV - library' },
{ image: tvMdImage, alt: 'Plezy on TV - media details' },
{ image: tvPlayerImage, alt: 'Plezy on TV - video player' },
];
const screenshots: Record<
DeviceType,
const devices: {
id: DeviceType;
icon: DeviceIconComponent;
label: string;
sizes: string;
shots: { image: typeof phoneHomeImage; alt: string }[];
}[] = [
{
shots: typeof phoneShots;
frameClass: string;
sizes: string;
ariaLabel: string;
}
> = {
phone: {
shots: phoneShots,
frameClass: 'phone-frame',
id: 'phone',
icon: DevicePhoneIcon,
label: 'Phone',
sizes: '(min-width: 1024px) 214px, 187px',
ariaLabel: 'Phone screenshots',
shots: [
{ image: phoneHomeImage, alt: 'Plezy home screen' },
{ image: phoneLibraryImage, alt: 'Plezy library view' },
{ image: phoneMdImage, alt: 'Plezy media details' },
{ image: phoneSearchImage, alt: 'Plezy search' },
],
},
tablet: {
shots: tabletShots,
frameClass: 'tablet-frame',
{
id: 'tablet',
icon: DeviceTabletIcon,
label: 'Tablet',
sizes: '(min-width: 1024px) 768px, 672px',
ariaLabel: 'Tablet screenshots',
shots: [
{ image: tabletHomeImage, alt: 'Plezy on tablet - home' },
{ image: tabletLibraryImage, alt: 'Plezy on tablet - library' },
{ image: tabletMdImage, alt: 'Plezy on tablet - media details' },
{ image: tabletPlayerImage, alt: 'Plezy on tablet - video player' },
],
},
desktop: {
shots: desktopShots,
frameClass: 'desktop-frame',
{
id: 'desktop',
icon: DesktopIcon,
label: 'Desktop',
sizes: '(min-width: 1024px) 768px, 672px',
ariaLabel: 'Desktop screenshots',
shots: [
{ image: desktopHomeImage, alt: 'Plezy on desktop - home' },
{ image: desktopLibraryImage, alt: 'Plezy on desktop - library' },
{ image: desktopMdImage, alt: 'Plezy on desktop - media details' },
{ image: desktopPlayerImage, alt: 'Plezy on desktop - video player' },
],
},
tv: {
shots: tvShots,
frameClass: 'tv-frame',
{
id: 'tv',
icon: TvIcon,
label: 'TV',
sizes: '(min-width: 1024px) 854px, 747px',
ariaLabel: 'TV screenshots',
shots: [
{ image: tvHomeImage, alt: 'Plezy on TV - home' },
{ image: tvLibraryImage, alt: 'Plezy on TV - library' },
{ image: tvMdImage, alt: 'Plezy on TV - media details' },
{ image: tvPlayerImage, alt: 'Plezy on TV - video player' },
],
},
};
];
let active: DeviceType = $state('phone');
let loaded: Record<DeviceType, boolean> = $state({
@@ -144,7 +131,7 @@
intendedScrollLeft = target;
canScrollLeft = target > 10;
canScrollRight = target < maxScroll - 10;
scrollContainer.scrollTo({ left: target, behavior: 'smooth' });
scrollContainer.scrollTo({ left: target });
}
$effect(() => {
@@ -225,11 +212,10 @@
<div class="screenshot-panels">
{#each devices as device (device.id)}
{@const screenshot = screenshots[device.id]}
<div
id={`screenshots-${device.id}-panel`}
role="region"
aria-label={screenshot.ariaLabel}
aria-label={`${device.label} screenshots`}
aria-hidden={active !== device.id}
class="screenshot-strip scrollbar-hide content-pad"
class:panel-active={active === device.id}
@@ -238,15 +224,15 @@
}}
>
{#if loaded[device.id]}
{#each screenshot.shots as shot}
{#each device.shots as shot}
<div class="screenshot-item">
<div class={`screenshot-frame ${screenshot.frameClass}`}>
<div class={`screenshot-frame ${device.id}-frame`}>
<enhanced:img
src={shot.image}
alt={shot.alt}
loading="lazy"
class="screenshot-image"
sizes={screenshot.sizes}
sizes={device.sizes}
/>
</div>
</div>
@@ -377,6 +363,7 @@
width: 100%;
gap: 1.25rem;
overflow-x: auto;
scroll-behavior: smooth;
padding-bottom: 1rem;
opacity: 0;
pointer-events: none;
+3 -17
View File
@@ -5,6 +5,7 @@
import Reviews from '$lib/components/Reviews.svelte';
import FAQ from '$lib/components/FAQ.svelte';
import Footer from '$lib/components/Footer.svelte';
import PageMetadata from '$lib/components/PageMetadata.svelte';
import { faqSchemaMainEntity } from '$lib/content/faqs';
import { buildSoftwareApplicationOffers } from '$lib/content/software_app_offers';
@@ -13,7 +14,6 @@
const title = "Plezy - A Beautiful Plex & Jellyfin Client";
const description = "Plezy is a beautiful client for Plex and Jellyfin, available on iOS, Android, Android TV, tvOS, Windows, macOS, and Linux. HDR, Dolby Vision, offline downloads, and more.";
const url = "https://plezy.app/";
const image = "https://plezy.app/og/plezy-social.png";
const softwareAppSchema = $derived.by(() => {
const schema: Record<string, unknown> = {
@@ -50,23 +50,9 @@
};
</script>
<PageMetadata {title} {description} {url} />
<svelte:head>
<title>{title}</title>
<meta name="description" content={description} />
<link rel="canonical" href={url} />
<meta property="og:type" content="website" />
<meta property="og:site_name" content="Plezy" />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:url" content={url} />
<meta property="og:image" content={image} />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={title} />
<meta name="twitter:description" content={description} />
<meta name="twitter:image" content={image} />
{@html `<script type="application/ld+json">${JSON.stringify(softwareAppSchema)}</script>`}
{@html `<script type="application/ld+json">${JSON.stringify(faqSchema)}</script>`}
</svelte:head>
+2 -17
View File
@@ -1,28 +1,13 @@
<script lang="ts">
import Logo from '$lib/components/Logo.svelte';
import PageMetadata from '$lib/components/PageMetadata.svelte';
const title = 'Privacy Policy - Plezy';
const description = 'How Plezy stores data on your device and shares data when you use connected services.';
const url = 'https://plezy.app/privacy';
</script>
<svelte:head>
<title>{title}</title>
<meta name="description" content={description} />
<link rel="canonical" href={url} />
<meta property="og:type" content="website" />
<meta property="og:site_name" content="Plezy" />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:url" content={url} />
<meta property="og:image" content="https://plezy.app/og/plezy-social.png" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={title} />
<meta name="twitter:description" content={description} />
<meta name="twitter:image" content="https://plezy.app/og/plezy-social.png" />
</svelte:head>
<PageMetadata {title} {description} {url} />
<article class="privacy-article">
<a href="/" class="back-link">
+2 -18
View File
@@ -1,6 +1,7 @@
<script lang="ts">
import { onMount } from "svelte";
import Logo from "$lib/components/Logo.svelte";
import PageMetadata from "$lib/components/PageMetadata.svelte";
import AppleIcon from "~icons/simple-icons/apple";
import GooglePlayIcon from "~icons/simple-icons/googleplay";
import {
@@ -21,24 +22,7 @@
});
</script>
<svelte:head>
<title>Open in Plezy</title>
<meta name="description" content="Open this QR code with the Plezy app." />
<meta name="robots" content="noindex, nofollow" />
<link rel="canonical" href="https://plezy.app/scan" />
<meta property="og:type" content="website" />
<meta property="og:site_name" content="Plezy" />
<meta property="og:title" content="Open in Plezy" />
<meta property="og:description" content="Open this QR code with the Plezy app." />
<meta property="og:url" content="https://plezy.app/scan" />
<meta property="og:image" content="https://plezy.app/og/plezy-social.png" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="Open in Plezy" />
<meta name="twitter:description" content="Open this QR code with the Plezy app." />
<meta name="twitter:image" content="https://plezy.app/og/plezy-social.png" />
</svelte:head>
<PageMetadata title="Open in Plezy" description="Open this QR code with the Plezy app." url="https://plezy.app/scan" noindex />
<div class="centered-page">
<div class="centered-card">