feat(react): add captions styling to video skins (#582)

This commit is contained in:
Sam Potts
2026-02-20 11:53:46 +11:00
committed by GitHub
parent c1a069674a
commit b78c6ce7b1
4 changed files with 165 additions and 18 deletions
@@ -1,7 +1,3 @@
.media-minimal-skin * {
box-sizing: border-box;
}
.media-minimal-skin {
position: relative;
isolation: isolate;
@@ -15,6 +11,9 @@
-webkit-font-smoothing: auto;
-moz-osx-font-smoothing: auto;
}
.media-minimal-skin * {
box-sizing: border-box;
}
.media-minimal-skin::after {
content: "";
position: absolute;
@@ -155,7 +154,7 @@
text-shadow: 0 1px 0 oklab(0 0 0 / 0.2);
font-variant-numeric: tabular-nums;
}
@container media-controls (width > 480px) {
@container media-controls (width > 28rem) {
.media-minimal-skin .media-time-controls {
flex-direction: row;
}
@@ -175,7 +174,7 @@
align-items: center;
gap: 0.075rem;
}
@container media-root (width > 640px) {
@container media-root (width > 42rem) {
.media-minimal-skin .media-button-group {
gap: 0.125rem;
}
@@ -213,7 +212,7 @@
opacity: 0.5;
filter: grayscale(1);
}
@container media-controls (width < 480px) {
@container media-controls (width < 28rem) {
.media-minimal-skin .media-button--seek {
display: none;
}
@@ -375,6 +374,9 @@
}
/* Tooltip Component Styles */
.media-minimal-skin .media-tooltip {
white-space: nowrap;
}
.media-minimal-skin .media-tooltip-popup {
color: oklab(1 0 0);
padding: 0.25rem 0.5rem;
@@ -397,6 +399,68 @@
}
}
.media-minimal-skin .media-tooltip {
white-space: nowrap;
/* Captions */
.media-minimal-skin .media-captions {
position: absolute;
inset: auto 1rem 1.5rem 1rem;
z-index: 20;
transition: transform 150ms ease-out;
/* The delay must account for the controls delay/duration */
transition-delay: 600ms;
pointer-events: none;
text-wrap: balance;
font-size: 1rem;
}
@media (prefers-reduced-motion: reduce) {
.media-minimal-skin .media-captions {
transition-duration: 50ms;
}
}
.media-minimal-skin .media-captions__container {
max-width: 42ch;
margin: 0 auto;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
}
.media-minimal-skin .media-captions__text {
display: block;
padding: 0.125rem 0.5rem;
text-shadow:
0 0 1px oklab(0 0 0 / 0.7),
0 0 8px oklab(0 0 0 / 0.7);
color: #fff;
text-align: center;
white-space: pre-wrap;
line-height: 1.2;
}
@media (prefers-contrast: more) {
.media-minimal-skin .media-captions__text {
text-shadow: none;
box-decoration-break: clone;
background: oklab(0 0 0 / 0.7);
}
}
.media-minimal-skin .media-captions__text > * {
display: inline;
}
@container media-root (width > 20rem) {
.media-minimal-skin .media-captions {
font-size: 1.5rem;
}
}
@container media-root (width > 48rem) {
.media-minimal-skin .media-captions {
font-size: 2rem;
}
}
@container media-root (width > 80rem) {
.media-minimal-skin .media-captions {
font-size: 2.5rem;
}
}
.media-minimal-skin .media-controls[data-visible] ~ .media-captions {
transform: translateY(-2.5rem);
transition-delay: 25ms;
}
@@ -155,6 +155,15 @@ export function MinimalVideoSkin(props: MinimalVideoSkinProps): ReactNode {
</span>
</Controls.Root>
{/* <div className="media-captions">
<div className="media-captions__container">
<span className="media-captions__text">An example cue</span>
<span className="media-captions__text">
<p>Another example cue with HTML</p>
</span>
</div>
</div> */}
<div className="media-overlay" />
</Container>
);
+74 -9
View File
@@ -1,7 +1,3 @@
.media-default-skin * {
box-sizing: border-box;
}
.media-default-skin {
position: relative;
isolation: isolate;
@@ -15,6 +11,9 @@
-webkit-font-smoothing: auto;
-moz-osx-font-smoothing: auto;
}
.media-default-skin * {
box-sizing: border-box;
}
.media-default-skin::before,
.media-default-skin::after {
content: "";
@@ -156,12 +155,13 @@
}
@media (prefers-reduced-motion: reduce) {
.media-default-skin .media-controls:not([data-visible]) {
/* FIXME: Make this a custom property that we can share */
transition-duration: 100ms;
filter: blur(0);
scale: 1;
}
}
@container media-root (width > 640px) {
@container media-root (width > 42rem) {
.media-default-skin .media-controls {
padding: 0.25rem;
gap: 0.125rem;
@@ -184,7 +184,7 @@
.media-default-skin .media-time .media-time__value:first-child {
display: none;
}
@container media-time (width > 240px) {
@container media-time (width > 18rem) {
.media-default-skin .media-time .media-time__value:first-child {
display: block;
}
@@ -224,7 +224,7 @@
opacity: 0.5;
filter: grayscale(1);
}
@container media-controls (width < 480px) {
@container media-controls (width < 28rem) {
.media-default-skin .media-button--seek {
display: none;
}
@@ -388,6 +388,9 @@
}
/* Tooltip Component Styles */
.media-default-skin .media-tooltip {
white-space: nowrap;
}
.media-default-skin .media-tooltip-popup {
color: oklab(1 0 0);
padding: 0.25rem 0.625rem;
@@ -395,6 +398,68 @@
font-size: 0.75rem;
}
.media-default-skin .media-tooltip {
white-space: nowrap;
/* Captions */
.media-default-skin .media-captions {
position: absolute;
inset: auto 1rem 1.5rem 1rem;
z-index: 20;
transition: transform 150ms ease-out;
/* The delay must account for the controls delay/duration */
transition-delay: 600ms;
pointer-events: none;
text-wrap: balance;
font-size: 1rem;
}
@media (prefers-reduced-motion: reduce) {
.media-default-skin .media-captions {
transition-duration: 50ms;
}
}
.media-default-skin .media-captions__container {
max-width: 42ch;
margin: 0 auto;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
}
.media-default-skin .media-captions__text {
display: block;
padding: 0.125rem 0.5rem;
text-shadow:
0 0 1px oklab(0 0 0 / 0.7),
0 0 8px oklab(0 0 0 / 0.7);
color: #fff;
text-align: center;
white-space: pre-wrap;
line-height: 1.2;
}
@media (prefers-contrast: more) {
.media-default-skin .media-captions__text {
text-shadow: none;
box-decoration-break: clone;
background: oklab(0 0 0 / 0.7);
}
}
.media-default-skin .media-captions__text > * {
display: inline;
}
@container media-root (width > 20rem) {
.media-default-skin .media-captions {
font-size: 1.5rem;
}
}
@container media-root (width > 48rem) {
.media-default-skin .media-captions {
font-size: 2rem;
}
}
@container media-root (width > 80rem) {
.media-default-skin .media-captions {
font-size: 2.5rem;
}
}
.media-default-skin .media-controls[data-visible] ~ .media-captions {
transform: translateY(-3rem);
transition-delay: 25ms;
}
@@ -149,6 +149,15 @@ export function VideoSkin(props: VideoSkinProps): ReactNode {
/>
</Controls.Root>
{/*<div className="media-captions">
<div className="media-captions__container">
<span className="media-captions__text">An example cue</span>
<span className="media-captions__text">
<p>Another example cue with HTML</p>
</span>
</div>
</div>*/}
<div className="media-overlay" />
</Container>
);