11import { css } from '@emotion/react' ;
2- import { log , storage } from '@guardian/libs' ;
2+ import { isUndefined , log , storage } from '@guardian/libs' ;
33import { from , space } from '@guardian/source/foundations' ;
44import { SvgAudio , SvgAudioMute } from '@guardian/source/react-components' ;
55import { useCallback , useEffect , useRef , useState } from 'react' ;
@@ -30,22 +30,44 @@ import type {
3030import { SelfHostedVideoPlayer } from './SelfHostedVideoPlayer' ;
3131import { ophanTrackerWeb } from './YoutubeAtom/eventEmitters' ;
3232
33- const videoAndBackgroundStyles = ( isCinemagraph : boolean ) => css `
33+ const videoContainerStyles = (
34+ isCinemagraph : boolean ,
35+ aspectRatio : number ,
36+ containerAspectRatio ?: number , // The aspect ratio of the container
37+ ) => css `
3438 position : relative;
3539 display : flex;
3640 justify-content : space-around;
3741 background-color : ${ palette ( '--video-background' ) } ;
3842 ${ ! isCinemagraph && `z-index: ${ getZIndex ( 'video-container' ) } ` } ;
43+
44+ /**
45+ * If the video and its containing slot have different dimensions, the slot will use the aspect
46+ * ratio of the video on mobile, so that the video can take up the full width of the screen.
47+ *
48+ * From tablet breakpoints, the aspect ratio of the slot is maintained, for consistency with other content.
49+ * This will result in grey bars on either side of the video if the video is narrower than the slot.
50+ */
51+ aspect- ratio: ${ aspectRatio } ;
52+ ${ from . tablet } {
53+ ${ ! isUndefined ( containerAspectRatio ) &&
54+ `aspect-ratio: ${ containerAspectRatio } ;` }
55+ }
3956` ;
4057
41- const videoContainerStyles = ( width : number , height : number ) => css `
58+ const figureStyles = ( aspectRatio : number ) => css `
4259 position : relative;
60+ aspect-ratio : ${ aspectRatio } ;
4361 height : 100% ;
4462 max-height : 100vh ;
4563 max-height : 100svh ;
4664 max-width : 100% ;
4765 ${ from . tablet } {
48- max-width: ${ ( width / height ) * 80 } %;
66+ /**
67+ * The value "80" is derived from the aspect ratio of the 5:4 slot.
68+ * When other slots are used for self-hosted videos, this will need to be adjusted.
69+ */
70+ max-width: ${ aspectRatio * 80 } %;
4971 }
5072` ;
5173
@@ -113,6 +135,12 @@ type Props = {
113135 width : number ;
114136 videoStyle : VideoPlayerFormat ;
115137 posterImage : string ;
138+ /**
139+ * The desired aspect ratio of the container of the video, which can differ from the
140+ * aspect ratio of the video itself. Only applied from the tablet breakpoint, as below this
141+ * breakpoint, the video always takes up the full width of the screen.
142+ */
143+ containerAspectRatio ?: number ;
116144 fallbackImage : CardPictureProps [ 'mainImage' ] ;
117145 fallbackImageSize : CardPictureProps [ 'imageSize' ] ;
118146 fallbackImageLoading : CardPictureProps [ 'loading' ] ;
@@ -132,6 +160,7 @@ export const SelfHostedVideo = ({
132160 width : expectedWidth ,
133161 videoStyle,
134162 posterImage,
163+ containerAspectRatio,
135164 fallbackImage,
136165 fallbackImageSize,
137166 fallbackImageLoading,
@@ -656,17 +685,25 @@ export const SelfHostedVideo = ({
656685 }
657686 } ;
658687
688+ const aspectRatio = width / height ;
689+
659690 const AudioIcon = isMuted ? SvgAudioMute : SvgAudio ;
660691
661692 const optimisedPosterImage = showPosterImage
662693 ? getOptimisedPosterImage ( posterImage )
663694 : undefined ;
664695
665696 return (
666- < div css = { videoAndBackgroundStyles ( isCinemagraph ) } >
697+ < div
698+ css = { videoContainerStyles (
699+ isCinemagraph ,
700+ aspectRatio ,
701+ containerAspectRatio ,
702+ ) }
703+ >
667704 < figure
668705 ref = { setNode }
669- css = { videoContainerStyles ( width , height ) }
706+ css = { figureStyles ( aspectRatio ) }
670707 className = { `video-container ${ videoStyle . toLocaleLowerCase ( ) } ` }
671708 data-component = "gu-video-loop"
672709 >
0 commit comments