diff --git a/dotcom-rendering/src/components/Masthead/HighlightsCard.tsx b/dotcom-rendering/src/components/Masthead/HighlightsCard.tsx index a696e4b4e9..9a2d20ca9a 100644 --- a/dotcom-rendering/src/components/Masthead/HighlightsCard.tsx +++ b/dotcom-rendering/src/components/Masthead/HighlightsCard.tsx @@ -1,12 +1,6 @@ import { css } from '@emotion/react'; import { isUndefined } from '@guardian/libs'; -import { - between, - from, - space, - textSansBold12, - until, -} from '@guardian/source/foundations'; +import { between, from, space, until } from '@guardian/source/foundations'; import { SvgCamera } from '@guardian/source/react-components'; import { ArticleDesign, type ArticleFormat } from '../../lib/articleFormat'; import { isMediaCard as isMedia } from '../../lib/cardHelpers'; @@ -14,6 +8,7 @@ import { palette } from '../../palette'; import type { StarRating as Rating } from '../../types/content'; import type { DCRFrontImage } from '../../types/front'; import type { MainMedia } from '../../types/mainMedia'; +import { PodcastSeriesImage } from '../../types/tag'; import { Avatar } from '../Avatar'; import { CardLink } from '../Card/components/CardLink'; import { CardHeadline } from '../CardHeadline'; @@ -41,6 +36,8 @@ export type HighlightsCardProps = { starRating?: Rating; galleryCount?: number; audioDuration?: string; + /** The square podcast series image, if it exists for a card */ + podcastImage?: PodcastSeriesImage; }; const gridContainer = css` @@ -49,11 +46,11 @@ const gridContainer = css` /** Relative positioning is required to absolutely position the card link overlay */ position: relative; - gap: 8px; + column-gap: ${space[2]}px; grid-template-areas: - 'headline headline' - 'rating rating' - 'media-icon image'; + 'headline headline' + 'media-icon media-icon' + '. image'; /* Applied word-break: break-word to prevent text overflow and ensure long words break onto the next line. @@ -83,65 +80,36 @@ const gridContainer = css` } `; -const mediaGrid = css` - grid-template-areas: - 'image' - 'headline' - 'media-icon'; - - ${from.desktop} { - width: 300px; - grid-template-areas: - 'image headline' - 'image media-icon'; - } -`; - const headline = css` grid-area: headline; + margin-bottom: ${space[1]}px; `; const mediaIcon = css` grid-area: media-icon; - align-self: end; display: flex; align-items: flex-end; `; -const audioPill = css` - display: flex; - align-items: center; - column-gap: 4px; -`; - -const audioPillIcon = css` - width: ${space[6]}px; - height: ${space[6]}px; - background-color: ${palette('--pill-background')}; - border-radius: 50%; - - > svg { - fill: ${palette('--highlights-container-background')}; - } -`; - -const audioPillText = css` - ${textSansBold12}; - color: ${palette('--highlight-card-audio-text')}; -`; - const imageArea = css` grid-area: image; - height: 106px; - width: 106px; + height: 112px; + width: 112px; align-self: end; position: relative; + ${until.desktop} { + margin-top: ${space[2]}px; + } ${from.desktop} { - height: 112px; - width: 112px; + align-self: start; } `; +/** Avatar alignment is an exception and should align with the bottom of the card *if* there is a gap.*/ +const avatarAlignmentStyles = css` + align-self: end; +`; + const hoverStyles = css` :hover .image-overlay { position: absolute; @@ -149,10 +117,13 @@ const hoverStyles = css` right: 0; height: 100%; width: 100%; - border-radius: 100%; background-color: ${palette('--card-background-hover')}; } + :hover .circular { + border-radius: 100%; + } + /* Only underline the headline element we want to target (not kickers/sublink headlines) */ :hover .card-headline .show-underline { text-decoration: underline; @@ -163,14 +134,64 @@ const starWrapper = css` background-color: ${palette('--star-rating-background')}; color: ${palette('--star-rating-fill')}; width: fit-content; - height: fit-content; - grid-area: rating; - ${from.desktop} { - grid-area: media-icon; - align-self: flex-end; - } + grid-area: media-icon; + align-self: flex-end; `; +const decideImage = ( + imageLoading: Loading, + format: ArticleFormat, + image?: DCRFrontImage, + podcastImage?: PodcastSeriesImage, + avatarUrl?: string, + byline?: string, +) => { + if (!image && !avatarUrl) { + return null; + } + if (avatarUrl) { + return ( + + ); + } + if (format.design === ArticleDesign.Audio && podcastImage?.src) { + return ( + <> + +
+ + ); + } + if (!image) { + return null; + } + return ( + <> + + {/* This image overlay is styled when the CardLink is hovered */} +
+ + ); +}; + export const HighlightsCard = ({ linkTo, format, @@ -187,24 +208,25 @@ export const HighlightsCard = ({ starRating, galleryCount, audioDuration, + podcastImage, }: HighlightsCardProps) => { const isMediaCard = isMedia(format); + const MediaPill = () => (
- {mainMedia?.type === 'Video' && ( + {mainMedia?.type === 'Video' && mainMedia.duration && ( } iconSize={'small'} /> )} - {mainMedia?.type === 'Audio' && ( -
-
- -
- {audioDuration} -
+ {mainMedia?.type === 'Audio' && audioDuration && ( + } + iconSize={'small'} + /> )} {mainMedia?.type === 'Gallery' && ( ); + return ( -
+
- {(avatarUrl && ( - - )) ?? - (image && ( - <> - - {/* This image overlay is styled when the CardLink is hovered */} -
- - ))} +
+ {decideImage( + imageLoading, + format, + image, + podcastImage, + avatarUrl, + byline, + )}
diff --git a/dotcom-rendering/src/components/ScrollableHighlights.importable.tsx b/dotcom-rendering/src/components/ScrollableHighlights.importable.tsx index 311a1cf24b..6a92a4e6b8 100644 --- a/dotcom-rendering/src/components/ScrollableHighlights.importable.tsx +++ b/dotcom-rendering/src/components/ScrollableHighlights.importable.tsx @@ -265,6 +265,7 @@ export const ScrollableHighlights = ({ trails }: Props) => { starRating={trail.starRating} galleryCount={trail.galleryCount} audioDuration={trail.audioDuration} + podcastImage={trail.podcastImage} /> ); diff --git a/dotcom-rendering/src/layouts/FrontLayout.tsx b/dotcom-rendering/src/layouts/FrontLayout.tsx index f393a55012..c06b8cdecb 100644 --- a/dotcom-rendering/src/layouts/FrontLayout.tsx +++ b/dotcom-rendering/src/layouts/FrontLayout.tsx @@ -137,7 +137,7 @@ export const FrontLayout = ({ front, NAV }: Props) => { const contributionsServiceUrl = getContributionsServiceUrl(front); - const { abTests, isPreview } = front.config; + // const { abTests, isPreview } = front.config; const { absoluteServerTimes = false } = front.config.switches; @@ -160,9 +160,9 @@ export const FrontLayout = ({ front, NAV }: Props) => { }; const Highlights = () => { - const showHighlights = - // Must be opted into the Europe beta test or in preview - abTests.europeBetaFrontVariant === 'variant' || isPreview; + const showHighlights = true; + // Must be opted into the Europe beta test or in preview + // abTests.europeBetaFrontVariant === 'variant' || isPreview; const highlightsCollection = front.pressedPage.collections.find(isHighlights);