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 (
+