Skip to content

Commit ade99c9

Browse files
Merge branch 'main' into mob/scrollable-products
2 parents de9d97e + e7aeff7 commit ade99c9

File tree

3 files changed

+49
-9
lines changed

3 files changed

+49
-9
lines changed

dotcom-rendering/src/components/Card/Card.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1001,6 +1001,7 @@ export const Card = ({
10011001
width={media.mainMedia.width}
10021002
videoStyle={media.mainMedia.videoStyle}
10031003
posterImage={media.mainMedia.image ?? ''}
1004+
containerAspectRatio={5 / 4}
10041005
fallbackImage={media.mainMedia.image ?? ''}
10051006
fallbackImageSize={mediaSize}
10061007
fallbackImageLoading={imageLoading}

dotcom-rendering/src/components/SelfHostedVideo.importable.tsx

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { css } from '@emotion/react';
2-
import { log, storage } from '@guardian/libs';
2+
import { isUndefined, log, storage } from '@guardian/libs';
33
import { from, space } from '@guardian/source/foundations';
44
import { SvgAudio, SvgAudioMute } from '@guardian/source/react-components';
55
import { useCallback, useEffect, useRef, useState } from 'react';
@@ -30,22 +30,44 @@ import type {
3030
import { SelfHostedVideoPlayer } from './SelfHostedVideoPlayer';
3131
import { 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
>

dotcom-rendering/src/components/SelfHostedVideoPlayer.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import { VideoProgressBar } from './VideoProgressBar';
2323

2424
export type SubtitleSize = 'small' | 'medium' | 'large';
2525

26-
const videoStyles = (width: number, height: number) => css`
26+
const videoStyles = (aspectRatio: number) => css`
2727
position: relative;
2828
display: block;
2929
height: auto;
@@ -32,7 +32,7 @@ const videoStyles = (width: number, height: number) => css`
3232
max-height: 100svh;
3333
cursor: pointer;
3434
/* Prevents CLS by letting the browser know the space the video will take up. */
35-
aspect-ratio: ${width} / ${height};
35+
aspect-ratio: ${aspectRatio};
3636
`;
3737

3838
const subtitleStyles = (subtitleSize: SubtitleSize | undefined) => css`
@@ -191,13 +191,15 @@ export const SelfHostedVideoPlayer = forwardRef(
191191
? sources
192192
: filterOutHlsSources(sources);
193193

194+
const aspectRatio = width / height;
195+
194196
return (
195197
<>
196198
{/* eslint-disable-next-line jsx-a11y/media-has-caption -- Not all videos require captions. */}
197199
<video
198200
id={videoId}
199201
css={[
200-
videoStyles(width, height),
202+
videoStyles(aspectRatio),
201203
showSubtitles && subtitleStyles(subtitleSize),
202204
]}
203205
crossOrigin="anonymous"

0 commit comments

Comments
 (0)