Skip to content

Commit e5a1a27

Browse files
Adds reel
1 parent 9cae788 commit e5a1a27

2 files changed

Lines changed: 171 additions & 12 deletions

File tree

‎src/components/YouTubeEmbed.astro‎

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
---
2+
/**
3+
* Click-to-play YouTube embed. Renders a lightweight thumbnail and only loads
4+
* the (privacy-enhanced) YouTube player after the visitor clicks, which keeps
5+
* the page fast and avoids setting third-party cookies before consent.
6+
*/
7+
interface Props {
8+
videoId: string;
9+
title: string;
10+
duration?: string;
11+
playLabel?: string;
12+
loading?: 'lazy' | 'eager';
13+
class?: string;
14+
}
15+
16+
const {
17+
videoId,
18+
title,
19+
duration,
20+
playLabel = 'Play video',
21+
loading = 'lazy',
22+
class: className = '',
23+
} = Astro.props;
24+
25+
const thumbnail = `https://i.ytimg.com/vi/${videoId}/maxresdefault.jpg`;
26+
const thumbnailSmall = `https://i.ytimg.com/vi/${videoId}/hqdefault.jpg`;
27+
---
28+
29+
<div
30+
class={`yt-embed group relative rounded-2xl border overflow-hidden aspect-video ${className}`}
31+
style="background: var(--bg-secondary); border-color: var(--border-primary);"
32+
data-yt-embed
33+
data-video-id={videoId}
34+
data-title={title}
35+
>
36+
<button
37+
type="button"
38+
class="yt-embed-trigger absolute inset-0 w-full h-full cursor-pointer focus:outline-none focus-visible:ring-4 focus-visible:ring-purple-500/60"
39+
aria-label={`${playLabel}: ${title}${duration ? ` (${duration})` : ''}`}
40+
>
41+
<img
42+
src={thumbnail}
43+
srcset={`${thumbnailSmall} 480w, ${thumbnail} 1280w`}
44+
sizes="(min-width: 1024px) 896px, 100vw"
45+
alt=""
46+
width="1280"
47+
height="720"
48+
loading={loading}
49+
decoding="async"
50+
class="absolute inset-0 w-full h-full object-cover transition-transform duration-500 group-hover:scale-[1.02]"
51+
/>
52+
<span class="absolute inset-0 bg-black/20 group-hover:bg-black/10 transition-colors duration-300" aria-hidden="true"></span>
53+
<span class="absolute inset-0 flex items-center justify-center" aria-hidden="true">
54+
<span class="flex items-center gap-3 pl-5 pr-6 py-3 sm:pl-6 sm:pr-7 sm:py-4 rounded-full bg-purple-600 group-hover:bg-purple-500 text-white font-semibold shadow-[0_0_40px_rgba(168,85,247,0.45)] transition-all duration-300 group-hover:scale-105">
55+
<svg class="w-5 h-5 sm:w-6 sm:h-6" viewBox="0 0 24 24" fill="currentColor"><path d="M8 5.14v13.72a1 1 0 0 0 1.52.85l11.01-6.86a1 1 0 0 0 0-1.7L9.52 4.29A1 1 0 0 0 8 5.14Z" /></svg>
56+
<span class="text-sm sm:text-base">{playLabel}</span>
57+
</span>
58+
</span>
59+
{duration && (
60+
<span class="absolute bottom-3 right-3 px-2 py-1 rounded-md bg-black/75 text-white text-xs font-mono" aria-hidden="true">{duration}</span>
61+
)}
62+
</button>
63+
</div>
64+
65+
<script>
66+
type ClarityWindow = Window & { clarity?: (...args: unknown[]) => void };
67+
68+
function play(container: HTMLElement) {
69+
if (container.dataset.loaded) return;
70+
container.dataset.loaded = 'true';
71+
72+
const { videoId, title } = container.dataset;
73+
const iframe = document.createElement('iframe');
74+
iframe.src = `https://www.youtube-nocookie.com/embed/${videoId}?autoplay=1&rel=0&modestbranding=1&playsinline=1`;
75+
iframe.title = title ?? 'YouTube video';
76+
iframe.allow = 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share';
77+
iframe.referrerPolicy = 'strict-origin-when-cross-origin';
78+
iframe.allowFullscreen = true;
79+
iframe.className = 'absolute inset-0 w-full h-full border-0';
80+
container.replaceChildren(iframe);
81+
iframe.focus();
82+
83+
// Only fires if the visitor consented to analytics (Clarity is loaded).
84+
(window as ClarityWindow).clarity?.('event', `video_play_${videoId}`);
85+
}
86+
87+
document.querySelectorAll<HTMLElement>('[data-yt-embed]').forEach((container) => {
88+
container.querySelector('.yt-embed-trigger')?.addEventListener('click', () => play(container), { once: true });
89+
});
90+
91+
// Links like <a href="#intro-video" data-yt-play="VIDEO_ID"> scroll to and start the video.
92+
document.querySelectorAll<HTMLAnchorElement>('[data-yt-play]').forEach((link) => {
93+
link.addEventListener('click', () => {
94+
const container = document.querySelector<HTMLElement>(`[data-yt-embed][data-video-id="${link.dataset.ytPlay}"]`);
95+
if (container) play(container);
96+
});
97+
});
98+
</script>

‎src/pages/index.astro‎

Lines changed: 73 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,32 @@
11
---
22
import Layout from '../layouts/Layout.astro';
3+
import YouTubeEmbed from '../components/YouTubeEmbed.astro';
4+
5+
const introVideo = {
6+
id: 'kbTK0A4Psv0',
7+
title: 'Dev Proxy: Test API Errors, Rate Limits & AI Without Code',
8+
description: 'Your app works on the happy path. What happens when the API returns 500s, throttles you with 429s, or times out? Dev Proxy lets you find out in minutes, without changing a single line of code.',
9+
uploadDate: '2026-09-28T02:29:23-07:00',
10+
duration: 'PT40S',
11+
};
12+
13+
const structuredData = {
14+
'@context': 'https://schema.org',
15+
'@type': 'VideoObject',
16+
name: introVideo.title,
17+
description: introVideo.description,
18+
thumbnailUrl: [
19+
`https://i.ytimg.com/vi/${introVideo.id}/maxresdefault.jpg`,
20+
`https://i.ytimg.com/vi/${introVideo.id}/hqdefault.jpg`,
21+
],
22+
uploadDate: introVideo.uploadDate,
23+
duration: introVideo.duration,
24+
embedUrl: `https://www.youtube-nocookie.com/embed/${introVideo.id}`,
25+
contentUrl: `https://www.youtube.com/watch?v=${introVideo.id}`,
26+
};
327
---
428

5-
<Layout title="Home">
29+
<Layout title="Home" structuredData={structuredData}>
630

731
<!-- Hero — full viewport, cinematic intro -->
832
<section class="hero-section relative flex items-center justify-center overflow-hidden" style="min-height: calc(100vh - 4rem);">
@@ -39,6 +63,19 @@ import Layout from '../layouts/Layout.astro';
3963
Browse Samples
4064
</a>
4165
</div>
66+
<div class="hero-fade hero-fade-4 mt-6">
67+
<a
68+
href="#intro-video"
69+
data-yt-play={introVideo.id}
70+
class="group inline-flex items-center gap-2 text-sm font-medium transition-colors hover:text-purple-400"
71+
style="color: var(--text-muted);"
72+
>
73+
<span class="flex items-center justify-center w-7 h-7 rounded-full border transition-colors group-hover:border-purple-500" style="border-color: var(--border-secondary);" aria-hidden="true">
74+
<svg class="w-3 h-3 ml-0.5" viewBox="0 0 24 24" fill="currentColor"><path d="M8 5.14v13.72a1 1 0 0 0 1.52.85l11.01-6.86a1 1 0 0 0 0-1.7L9.52 4.29A1 1 0 0 0 8 5.14Z" /></svg>
75+
</span>
76+
Watch the 40-second intro
77+
</a>
78+
</div>
4279

4380
<!-- Terminal teaser -->
4481
<div class="hero-fade hero-fade-5 mt-16 max-w-xl mx-auto min-w-0">
@@ -66,6 +103,33 @@ import Layout from '../layouts/Layout.astro';
66103
</div>
67104
</section>
68105

106+
<!-- Intro video — 40s explainer, placed high with a CTA right after -->
107+
<section id="intro-video" class="pt-16 pb-8 px-6 sm:px-8 scroll-mt-20">
108+
<div class="max-w-4xl mx-auto text-center">
109+
<p class="section-reveal text-sm font-semibold tracking-widest uppercase text-purple-400 mb-4">Dev Proxy in 40 seconds</p>
110+
<h2 class="section-reveal text-3xl md:text-5xl font-bold mb-12">See what Dev Proxy can do for&nbsp;you.</h2>
111+
<div class="section-reveal">
112+
<YouTubeEmbed
113+
videoId={introVideo.id}
114+
title={introVideo.title}
115+
duration="0:40"
116+
playLabel="Watch the intro"
117+
/>
118+
</div>
119+
<div class="section-reveal mt-10 flex flex-col items-center gap-3">
120+
<a
121+
href="https://learn.microsoft.com/microsoft-cloud/dev/dev-proxy/get-started"
122+
target="_blank"
123+
rel="noopener"
124+
class="group px-8 py-4 bg-purple-600 hover:bg-purple-500 text-white font-semibold rounded-full transition-all duration-300 hover:shadow-[0_0_30px_rgba(168,85,247,0.4)]"
125+
>
126+
Try Dev Proxy now <span class="inline-block transition-transform duration-300 group-hover:translate-x-1">&rarr;</span>
127+
</a>
128+
<p class="text-sm" style="color: var(--text-faint);">Free &amp; open source &middot; Windows, macOS &amp; Linux</p>
129+
</div>
130+
</div>
131+
</section>
132+
69133
<!-- Problem statement — emotional hook -->
70134
<section class="py-32 px-6 sm:px-8">
71135
<div class="max-w-3xl mx-auto text-center">
@@ -275,21 +339,18 @@ import Layout from '../layouts/Layout.astro';
275339
<!-- Getting started video -->
276340
<section class="py-32 px-6 sm:px-8">
277341
<div class="max-w-4xl mx-auto text-center">
278-
<p class="section-reveal text-sm font-semibold tracking-widest uppercase text-purple-400 mb-4">See It In Action</p>
342+
<p class="section-reveal text-sm font-semibold tracking-widest uppercase text-purple-400 mb-4">Step-by-Step Walkthrough</p>
279343
<h2 class="section-reveal text-3xl md:text-5xl font-bold mb-8">Get started with Dev Proxy.</h2>
280344
<p class="section-reveal text-lg md:text-xl mb-12" style="color: var(--text-muted);">
281345
Watch how to set up Dev Proxy and start testing your app in minutes.
282346
</p>
283-
<div class="section-reveal rounded-2xl border overflow-hidden" style="border-color: var(--border-primary);">
284-
<div style="position: relative; padding-bottom: 56.25%; height: 0;">
285-
<iframe
286-
src="https://www.youtube-nocookie.com/embed/HVTJlGSxhcw"
287-
title="Get started with Dev Proxy"
288-
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: 0;"
289-
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
290-
allowfullscreen
291-
></iframe>
292-
</div>
347+
<div class="section-reveal">
348+
<YouTubeEmbed
349+
videoId="HVTJlGSxhcw"
350+
title="Get started with Dev Proxy"
351+
duration="8:49"
352+
playLabel="Watch the walkthrough"
353+
/>
293354
</div>
294355
</div>
295356
</section>

0 commit comments

Comments
 (0)