Skip to content

Commit 306cd47

Browse files
authored
Add shared Video component for YouTube embeds (#5136)
Replaces hand-rolled YouTube iframes with a click-to-play facade that defers loading until clicked, uses the youtube-nocookie.com domain, and enforces a consistent centered/max-width appearance. Migrates the 5 single-video embeds; playlist embeds and the local mp4 in design-patterns/approval.mdx are left as-is.
1 parent a4b2119 commit 306cd47

13 files changed

Lines changed: 258 additions & 73 deletions

File tree

docs/encyclopedia/activities/activities.mdx

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,17 @@ tags:
1717
- Durable Execution
1818
---
1919

20+
import { Video } from '@site/src/components';
21+
2022
This guide provides a comprehensive overview of Temporal Activities including
2123
[Activity Definition](/activity-definition), [Activity Type](/activity-definition#activity-type),
2224
[Activity Execution](/activity-execution), [Local Activity](/local-activity), and [Standalone Activity](/standalone-activity).
2325

2426
:::tip
2527

26-
Watch [What is an Activity in Temporal?](https://www.youtube.com/watch?v=rtWrzjnKlSQ) for a short overview.
28+
Watch a short overview of what an Activity is in Temporal:
2729

28-
<div style={{ display: 'flex', justifyContent: 'center' }}>
29-
<iframe
30-
width="560"
31-
height="315"
32-
src="https://www.youtube.com/embed/rtWrzjnKlSQ"
33-
title="What is an Activity in Temporal?"
34-
frameBorder="0"
35-
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
36-
referrerPolicy="strict-origin-when-cross-origin"
37-
allowFullScreen
38-
></iframe>
39-
</div>
30+
<Video videoId="rtWrzjnKlSQ" title="What is an Activity in Temporal?" />
4031

4132
:::
4233

docs/encyclopedia/temporal.mdx

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ tags:
1616
- Concepts
1717
---
1818

19-
import { CaptionedImage, QuickstartCards } from '@site/src/components';
19+
import { CaptionedImage, QuickstartCards, Video } from '@site/src/components';
2020

2121
Temporal is a scalable and reliable runtime for durable function executions called [Temporal Workflow Executions](/workflow-execution).
2222

@@ -41,20 +41,9 @@ If a failure occurs, the Workflow Execution can resume from the last recorded ev
4141

4242
:::tip
4343

44-
Watch [What is the Temporal Platform?](https://www.youtube.com/watch?v=EwweiH2rd7M) for a short overview.
45-
46-
<div style={{ display: 'flex', justifyContent: 'center' }}>
47-
<iframe
48-
width="560"
49-
height="315"
50-
src="https://www.youtube.com/embed/EwweiH2rd7M"
51-
title="What is the Temporal Platform?"
52-
frameBorder="0"
53-
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
54-
referrerPolicy="strict-origin-when-cross-origin"
55-
allowFullScreen
56-
></iframe>
57-
</div>
44+
Watch a short overview of the Temporal Platform:
45+
46+
<Video videoId="EwweiH2rd7M" title="What is the Temporal Platform?" />
5847

5948
:::
6049

docs/encyclopedia/workflow/workflow-overview.mdx

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ tags:
1313
- Workflows
1414
---
1515

16+
import { Video } from '@site/src/components';
17+
1618
This guide provides a comprehensive overview of Temporal Workflows and covers the following:
1719

1820
- [Workflow Definition](/workflow-definition)
@@ -25,20 +27,9 @@ This guide provides a comprehensive overview of Temporal Workflows and covers th
2527

2628
:::tip
2729

28-
Watch [What Is a Workflow in Temporal?](https://www.youtube.com/watch?v=zLjhNrOKphE) for a short overview.
29-
30-
<div style={{ display: 'flex', justifyContent: 'center' }}>
31-
<iframe
32-
width="560"
33-
height="315"
34-
src="https://www.youtube.com/embed/zLjhNrOKphE"
35-
title="What Is a Workflow in Temporal?"
36-
frameBorder="0"
37-
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
38-
referrerPolicy="strict-origin-when-cross-origin"
39-
allowFullScreen
40-
></iframe>
41-
</div>
30+
Watch a short overview of what a Workflow is in Temporal:
31+
32+
<Video videoId="zLjhNrOKphE" title="What Is a Workflow in Temporal?" />
4233

4334
:::
4435

docs/evaluate/why-temporal.mdx

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ tags:
1313
- Durable Execution
1414
---
1515

16+
import { Video } from '@site/src/components';
17+
1618
# Why Temporal?
1719

1820
Temporal solves many problems that developers face while building distributed applications.
@@ -27,14 +29,7 @@ You can check out a list of [use cases for Temporal](/evaluate/use-cases-design-
2729
:::tip See Temporal in action
2830
Watch the following video to see how Temporal ensures an order-fulfillment system can recover from various failures, from process crashes to unreachable APIs.
2931

30-
<div style={{ display: 'flex', justifyContent: 'center' }}>
31-
<iframe width="560" height="315"
32-
src="https://www.youtube.com/embed/dNVmRfWsNkM?si=cfwAJgr2zaoro97P"
33-
title="YouTube video player"
34-
frameBorder="0"
35-
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
36-
referrerPolicy="strict-origin-when-cross-origin" allowFullScreen></iframe>
37-
</div>
32+
<Video videoId="dNVmRfWsNkM" title="Recovering an order-fulfillment system from failures with Temporal" />
3833
:::
3934

4035
## Reliable execution

docs/production-deployment/worker-deployments/worker-versioning/index.mdx

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ tags:
1616
- Durable Execution
1717
---
1818

19+
import { Video } from '@site/src/components';
20+
1921
Worker Versioning is a Temporal feature that allows you to confidently deploy new changes to the Workflows running on
2022
your Workers without breaking them. Temporal enables this by helping you manage different builds or versions, formally
2123
called [Worker Deployment Versions](/worker-versioning#deployment-versions).
@@ -40,18 +42,7 @@ To use Workflow Pinning, we recommend using [rainbow deployments](#deployment-sy
4042

4143
Watch this Temporal Replay 2025 talk to learn more about Worker Versioning and see a demo.
4244

43-
<div style={{ display: 'flex', justifyContent: 'center' }}>
44-
<iframe
45-
width="560"
46-
height="315"
47-
src="https://www.youtube.com/embed/rm4BlD9WXqc"
48-
title="YouTube video player"
49-
frameBorder="0"
50-
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
51-
referrerPolicy="strict-origin-when-cross-origin"
52-
allowFullScreen
53-
></iframe>
54-
</div>
45+
<Video videoId="rm4BlD9WXqc" title="Worker Versioning: a Temporal Replay 2025 talk and demo" />
5546

5647
:::
5748

readme/COMPONENTS.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Whether you’re using core components or experimenting with new ones, this guid
2020
- [Using Components in MDX Source Files](#using-components-in-mdx-source-files)
2121
- [Using IntegrationsGrid](#using-integrationsgrid)
2222
- [Using CaptionedImage](#using-captionedimage)
23+
- [Using Video](#using-video)
2324
- [Using DocsTable](#using-docstable)
2425
- [Using RelatedRead](#using-relatedread)
2526
- [Using ToolTipTerm](#using-tooltipterm)
@@ -251,6 +252,26 @@ One click will zoom out, another will zoom back.
251252
Most rendering will allow readers to pull the image around to view all parts of the image.
252253
On non-mobile systems, cursors will change to interactive elements when hovered above zoomable items to indicate interaction.
253254

255+
## Using Video
256+
257+
Video embeds a YouTube video behind a click-to-play facade. It enforces a consistent, centered, width-constrained appearance across the site, always points at the `youtube-nocookie.com` embed domain, and defers loading the real iframe (and YouTube's tracking scripts) until the reader clicks the thumbnail.
258+
259+
Usage:
260+
261+
```
262+
<Video videoId="EwweiH2rd7M" title="What is the Temporal Platform?" />
263+
```
264+
265+
| Prop | Type | Required | Description |
266+
| --- | --- | --- | --- |
267+
| `videoId` | `string` | Yes | The YouTube video ID (the `v=` value from a `youtube.com/watch` URL). |
268+
| `title` | `string` | Yes | A descriptive title. Used as the iframe's accessible title, the play button's `aria-label`, and the "Watch" link generated for the LLM Markdown pipeline (see [MARKDOWN_PIPELINE.md](./MARKDOWN_PIPELINE.md)). |
269+
| `maxWidth` | `string` | No | Escape hatch to override the default width for a documented one-off exception. Most pages should omit this and use the standard width. |
270+
271+
Because `Video` generates the "Watch: {title}" link for the LLM Markdown feed automatically, you no longer need to hand-write a separate `Watch [Title](url)` sentence purely for that purpose. You can still add a short lead-in sentence for human readers (for example, "Watch a short overview:") above the component.
272+
273+
**Not yet supported:** embedding a YouTube playlist (`videoseries?list=...`). The two pages that still embed a playlist ([`docs/develop/python/workflows/versioning.mdx`](../docs/develop/python/workflows/versioning.mdx) and [`docs/develop/dotnet/workflows/versioning.mdx`](../docs/develop/dotnet/workflows/versioning.mdx)) continue to use a raw `<iframe>` until playlist support is added.
274+
254275
## Using DocsTable
255276

256277
In certain rare instances, standard Markdown tables won't work for our needs.

readme/COMPONENT_REGISTRY.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,26 @@
22

33
Generated by `scripts/audit-components.mjs`. Run to update.
44

5-
**Coverage: 40/48 (83%)**
5+
**Coverage: 41/49 (84%)**
66

77
| Component | File Count | Strategy | Covered |
88
|-----------|------------|----------|---------|
9-
| `TabItem` | 82 | `tabitem` ||
10-
| `Tabs` | 81 | `tabs` ||
11-
| `ReleaseNoteHeader` | 71 | `release-note-header` ||
9+
| `TabItem` | 84 | `tabitem` ||
10+
| `Tabs` | 83 | `tabs` ||
11+
| `ReleaseNoteHeader` | 76 | `release-note-header` ||
1212
| `NoZoom` | 60 | `UNKNOWN` ||
1313
| `RelatedReadContainer` | 23 | `related-read-container` ||
1414
| `RelatedReadItem` | 20 | `related-read-item` ||
15-
| `SetupSteps` | 14 | `setup-steps` ||
16-
| `SetupStep` | 14 | `setup-step` ||
17-
| `CodeSnippet` | 14 | `code-snippet` ||
15+
| `SetupSteps` | 20 | `setup-steps` ||
16+
| `SetupStep` | 20 | `setup-step` ||
17+
| `CodeSnippet` | 20 | `code-snippet` ||
1818
| `PatternCards` | 13 | `cards` ||
19-
| `SdkTabs` | 9 | `sdk-tabs` ||
20-
| `CallToAction` | 8 | `call-to-action` ||
19+
| `SdkTabs` | 12 | `sdk-tabs` ||
20+
| `CallToAction` | 9 | `call-to-action` ||
2121
| `CaptionedImage` | 7 | `captioned-image` ||
2222
| `ViewSourceCodeNotice` | 6 | `view-source-code-notice` ||
2323
| `IntegrationsGrid` | 6 | `integrations-grid` ||
24+
| `Video` | 5 | `video` ||
2425
| `CodeToCommandsDemo` | 5 | `strip-block` ||
2526
| `CommandsToEventsDemo` | 5 | `strip-block` ||
2627
| `HistoryReplayDemo` | 5 | `strip-block` ||

scripts/mdx-to-md.mjs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ export const COMPONENT_REGISTRY = {
6161
SdkGuideLinks: "sdk-guide-links",
6262
CaptionedImage: "captioned-image",
6363
EnlargeImage: "captioned-image",
64+
Video: "video",
6465
CodeSnippet: "code-snippet",
6566
SdkTabs: "sdk-tabs",
6667
ToolTipTerm: "tooltip-term",
@@ -953,6 +954,21 @@ export function transformMdx(mdxContent, options = {}) {
953954
i = embedEnd;
954955
continue;
955956
}
957+
// <Video videoId="..." title="..." /> inside a tip → Watch link, same
958+
// as the Video strategy used outside admonitions.
959+
if (/^\s*<Video\b/.test(line)) {
960+
let tag = line;
961+
while (!/\/>/.test(tag) && i + 1 < lines.length) {
962+
i++;
963+
tag += " " + lines[i].trim();
964+
}
965+
const videoId = extractProp(tag, "videoId");
966+
const title = extractProp(tag, "title");
967+
if (videoId && title) {
968+
admonitionLines.push(`[Watch: ${title}](https://www.youtube.com/watch?v=${videoId})`);
969+
}
970+
continue;
971+
}
956972
admonitionLines.push(line);
957973
}
958974
continue;
@@ -1209,6 +1225,23 @@ export function transformMdx(mdxContent, options = {}) {
12091225
continue;
12101226
}
12111227

1228+
// --- Video (self-closing, may span lines) ---
1229+
// <Video videoId="..." title="..." /> → [Watch: Title](https://www.youtube.com/watch?v=ID)
1230+
if (state === State.NORMAL && /^\s*<Video\b/.test(line)) {
1231+
let tag = line;
1232+
while (!/\/>/.test(tag) && i + 1 < lines.length) {
1233+
i++;
1234+
tag += " " + lines[i].trim();
1235+
}
1236+
const videoId = extractProp(tag, "videoId");
1237+
const title = extractProp(tag, "title");
1238+
if (videoId && title) {
1239+
outputLines.push(`[Watch: ${title}](https://www.youtube.com/watch?v=${videoId})`);
1240+
outputLines.push("");
1241+
}
1242+
continue;
1243+
}
1244+
12121245
// --- JsonTable (self-closing data component) ---
12131246
if (state === State.NORMAL && /^\s*<JsonTable\b/.test(line)) {
12141247
let tag = line;
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
.container {
2+
--video-max-width: 750px;
3+
max-width: var(--video-max-width);
4+
width: 100%;
5+
margin: 2rem auto;
6+
}
7+
8+
.aspectBox {
9+
position: relative;
10+
width: 100%;
11+
aspect-ratio: 16 / 9;
12+
border-radius: 8px;
13+
overflow: hidden;
14+
background: #000;
15+
}
16+
17+
.facade {
18+
position: absolute;
19+
inset: 0;
20+
width: 100%;
21+
height: 100%;
22+
padding: 0;
23+
margin: 0;
24+
border: 0;
25+
background: none;
26+
cursor: pointer;
27+
display: block;
28+
}
29+
30+
.thumbnail {
31+
width: 100%;
32+
height: 100%;
33+
object-fit: cover;
34+
display: block;
35+
}
36+
37+
.playButton {
38+
position: absolute;
39+
top: 50%;
40+
left: 50%;
41+
transform: translate(-50%, -50%);
42+
width: 68px;
43+
height: 48px;
44+
display: flex;
45+
align-items: center;
46+
justify-content: center;
47+
background: rgba(0, 0, 0, 0.7);
48+
border-radius: 14px;
49+
transition: background-color 0.15s ease, transform 0.15s ease;
50+
}
51+
52+
.facade:hover .playButton {
53+
background: rgba(210, 30, 30, 0.85);
54+
transform: translate(-50%, -50%) scale(1.05);
55+
}
56+
57+
.facade:focus-visible {
58+
outline: 3px solid var(--ifm-color-primary);
59+
outline-offset: 3px;
60+
}
61+
62+
.playIcon {
63+
width: 22px;
64+
height: 22px;
65+
fill: #fff;
66+
margin-left: 3px;
67+
}
68+
69+
.iframe {
70+
position: absolute;
71+
inset: 0;
72+
width: 100%;
73+
height: 100%;
74+
border: 0;
75+
}
76+
77+
@media (max-width: 480px) {
78+
.playButton {
79+
width: 52px;
80+
height: 36px;
81+
border-radius: 10px;
82+
}
83+
84+
.playIcon {
85+
width: 16px;
86+
height: 16px;
87+
}
88+
}

0 commit comments

Comments
 (0)