diff --git a/dotcom-rendering/src/components/ArticleHeadline.tsx b/dotcom-rendering/src/components/ArticleHeadline.tsx
index c2334546810..33701f17ef2 100644
--- a/dotcom-rendering/src/components/ArticleHeadline.tsx
+++ b/dotcom-rendering/src/components/ArticleHeadline.tsx
@@ -18,7 +18,10 @@ import {
until,
} from '@guardian/source/foundations';
import { grid } from '../../src/grid';
-import { type LayoutType } from '../layouts/lib/articleArrangements';
+import {
+ isWeekendRead,
+ type LayoutType,
+} from '../layouts/lib/articleArrangements';
import { interactiveLegacyClasses } from '../layouts/lib/interactiveLegacyStyling';
import { getAgeWarning } from '../lib/age-warning';
import {
@@ -140,6 +143,13 @@ const headlineFont = (format: ArticleFormat) => {
`;
};
+const weekendReadFont = css`
+ ${headlineMedium34}
+ ${from.leftCol} {
+ ${headlineMedium50}
+ }
+`;
+
const invertedFontLineHeight = css`
line-height: 2.1875rem;
@@ -532,6 +542,27 @@ export const ArticleHeadline = ({
}: Props) => {
const isInverted = layoutType === 'immersiveLandscape';
const isLegacyImmersive = layoutType == null;
+ if (layoutType === 'immersivePortrait' && isWeekendRead(tags)) {
+ return (
+
+
+ {headlineString}
+
+ {!!byline && (
+
+ )}
+
+ );
+ }
switch (format.display) {
case ArticleDisplay.Immersive: {
switch (format.design) {
diff --git a/dotcom-rendering/src/components/ArticleMeta.apps.tsx b/dotcom-rendering/src/components/ArticleMeta.apps.tsx
index 0a6fc60bbf9..05bc7a4249a 100644
--- a/dotcom-rendering/src/components/ArticleMeta.apps.tsx
+++ b/dotcom-rendering/src/components/ArticleMeta.apps.tsx
@@ -4,7 +4,10 @@ import { from, space, until } from '@guardian/source/foundations';
import { StraightLines } from '@guardian/source-development-kitchen/react-components';
import type { ReactNode } from 'react';
import { grid } from '../grid';
-import type { LayoutType } from '../layouts/lib/articleArrangements';
+import {
+ isWeekendRead,
+ type LayoutType,
+} from '../layouts/lib/articleArrangements';
import { interactiveLegacyClasses } from '../layouts/lib/interactiveLegacyStyling';
import {
ArticleDesign,
@@ -332,7 +335,12 @@ export const ArticleMetaApps = ({
)}
- {shouldShowContributor(format, isImmersiveGrid) &&
+ {shouldShowContributor(
+ format,
+ isImmersiveGrid,
+ layoutType === 'immersivePortrait' &&
+ isWeekendRead(tags),
+ ) &&
!!byline && (
)}
- {shouldShowContributor(format, isImmersiveGrid) && (
+ {shouldShowContributor(
+ format,
+ isImmersiveGrid,
+ layoutType === 'immersivePortrait' &&
+ isWeekendRead(tags),
+ ) && (
) : null}
- {isImmersive && mainMediaElements?.[0] && (
-
-
-
-
-
- )}
+ {layoutType === 'immersiveLandscape' &&
+ mainMediaElements?.[0] && (
+
+
+
+
+
+ )}
);
diff --git a/dotcom-rendering/src/components/ArticleTitle.tsx b/dotcom-rendering/src/components/ArticleTitle.tsx
index 7786b099158..5041ff1c378 100644
--- a/dotcom-rendering/src/components/ArticleTitle.tsx
+++ b/dotcom-rendering/src/components/ArticleTitle.tsx
@@ -1,7 +1,10 @@
import { css } from '@emotion/react';
import { from } from '@guardian/source/foundations';
import { grid } from '../../src/grid';
-import type { LayoutType } from '../layouts/lib/articleArrangements';
+import {
+ isWeekendRead,
+ type LayoutType,
+} from '../layouts/lib/articleArrangements';
import {
ArticleDesign,
ArticleDisplay,
@@ -79,6 +82,12 @@ export const ArticleTitle = ({
format.design,
) && galleryStyles,
sectionStyles,
+ layoutType === 'immersivePortrait' &&
+ isWeekendRead(tags) &&
+ css`
+ position: relative;
+ min-height: 71px;
+ `,
]}
>
+ {layoutType === 'immersivePortrait' && isWeekendRead(tags) && (
+
+ )}
);
diff --git a/dotcom-rendering/src/components/Figure.tsx b/dotcom-rendering/src/components/Figure.tsx
index 8aa12616efa..ebc473cc0f9 100644
--- a/dotcom-rendering/src/components/Figure.tsx
+++ b/dotcom-rendering/src/components/Figure.tsx
@@ -1,6 +1,9 @@
import { css } from '@emotion/react';
import { breakpoints, from, space, until } from '@guardian/source/foundations';
-import type { LayoutType } from '../layouts/lib/articleArrangements';
+import {
+ isWeekendRead,
+ type LayoutType,
+} from '../layouts/lib/articleArrangements';
import {
ArticleDesign,
type ArticleFormat,
@@ -8,6 +11,7 @@ import {
} from '../lib/articleFormat';
import { LABS_HEADER_HEIGHT } from '../lib/labs-constants';
import type { FEElement, RoleType } from '../types/content';
+import type { TagType } from '../types/tag';
import { minHeaderHeightPx } from './Masthead/Titlepiece/constants';
type Props = {
@@ -21,6 +25,7 @@ type Props = {
isTimeline?: boolean;
isInDeprecatedInteractiveLayout?: boolean;
articleArrangement?: LayoutType;
+ tags?: TagType[];
};
const roleCss = {
@@ -306,6 +311,7 @@ export const Figure = ({
isTimeline = false,
isInDeprecatedInteractiveLayout = false,
articleArrangement,
+ tags,
}: Props) => {
if (isMainMedia && !isTimeline) {
// Don't add in-body styles for main media elements
@@ -317,10 +323,33 @@ export const Figure = ({
{children}
diff --git a/dotcom-rendering/src/components/HeadlineByline.tsx b/dotcom-rendering/src/components/HeadlineByline.tsx
index 8a727337f3c..741ccf65511 100644
--- a/dotcom-rendering/src/components/HeadlineByline.tsx
+++ b/dotcom-rendering/src/components/HeadlineByline.tsx
@@ -5,6 +5,8 @@ import {
headlineLight34,
headlineMedium20,
headlineMedium24,
+ headlineMedium34,
+ headlineMedium50,
palette,
space,
textSans20,
@@ -12,6 +14,10 @@ import {
textSans34,
until,
} from '@guardian/source/foundations';
+import {
+ isWeekendRead,
+ type LayoutType,
+} from '../layouts/lib/articleArrangements';
import {
ArticleDesign,
ArticleDisplay,
@@ -74,6 +80,27 @@ const opinionStyles = (format: ArticleFormat) => css`
}
`;
+const weekendReadStyles = css`
+ ${headlineMedium34}
+ line-height: 38px;
+ color: ${schemedPalette('--byline-anchor')};
+ font-style: italic;
+
+ ${from.leftCol} {
+ ${headlineMedium50}
+ line-height: 54px;
+ }
+
+ a {
+ color: inherit;
+ text-decoration: none;
+ font-style: normal;
+ :hover {
+ text-decoration: underline;
+ }
+ }
+`;
+
const analysisStyles = css`
${headlineLight34};
line-height: 38px;
@@ -136,15 +163,36 @@ type Props = {
format: ArticleFormat;
byline: string;
tags: TagType[];
+ layoutType?: LayoutType;
};
-export const HeadlineByline = ({ format, byline, tags }: Props) => {
+export const HeadlineByline = ({ format, byline, tags, layoutType }: Props) => {
if (byline === '') {
return null;
}
const hasSingleContributor = !!getSoleContributor(tags, byline);
+ if (layoutType === 'immersivePortrait' && isWeekendRead(tags)) {
+ return (
+
+ );
+ }
+
switch (format.display) {
case ArticleDisplay.Immersive:
return (
diff --git a/dotcom-rendering/src/components/MainMedia.tsx b/dotcom-rendering/src/components/MainMedia.tsx
index 153ab6563dd..d159d9e0d64 100644
--- a/dotcom-rendering/src/components/MainMedia.tsx
+++ b/dotcom-rendering/src/components/MainMedia.tsx
@@ -11,6 +11,7 @@ import { getZIndex } from '../lib/getZIndex';
import { RenderArticleElement } from '../lib/renderElement';
import type { Switches } from '../types/config';
import type { FEElement } from '../types/content';
+import type { TagType } from '../types/tag';
const mainMedia = css`
position: relative;
@@ -105,6 +106,7 @@ type Props = {
contentType?: string;
contentLayout?: string;
articleArrangement?: LayoutType;
+ tags?: TagType[];
};
export const MainMedia = ({
@@ -123,6 +125,7 @@ export const MainMedia = ({
contentType,
contentLayout,
articleArrangement,
+ tags,
}: Props) => {
return (
@@ -146,6 +149,7 @@ export const MainMedia = ({
contentType={contentType}
contentLayout={contentLayout}
articleArrangement={articleArrangement}
+ tags={tags}
/>
))}
diff --git a/dotcom-rendering/src/layouts/StandardLayoutArticleGrid.tsx b/dotcom-rendering/src/layouts/StandardLayoutArticleGrid.tsx
index 6fed5f71084..452dae9dcdc 100644
--- a/dotcom-rendering/src/layouts/StandardLayoutArticleGrid.tsx
+++ b/dotcom-rendering/src/layouts/StandardLayoutArticleGrid.tsx
@@ -46,6 +46,7 @@ import {
type Area,
getLayoutType,
gridItemCss,
+ isWeekendRead,
type LayoutType,
} from './lib/articleArrangements';
@@ -64,6 +65,7 @@ const immersiveMediaBelowDesktop = (
headlineBackground: string,
isMainMediaImage: boolean,
hasMinimumImageHeight: boolean,
+ isLandscape: boolean,
) => css`
${until.tablet} {
aspect-ratio: 4 / 5;
@@ -77,31 +79,38 @@ const immersiveMediaBelowDesktop = (
${!isMainMediaImage && 'overflow: hidden;'}
- &::after {
- content: '';
- position: absolute;
- left: 0;
- right: 0;
- bottom: ${hasMinimumImageHeight ? '-1px' : '0'};
- height: ${hasMinimumImageHeight
- ? '180px'
- : isMainMediaImage
- ? 'min(20%, calc(200% - 120vw + 30px))'
- : 'min(60%, 144px)'};
- z-index: ${getZIndex('mediaOverlay')};
- background: linear-gradient(
- to bottom,
- rgba(0, 0, 0, ${hasMinimumImageHeight ? '0' : '0.08'}),
- ${headlineBackground} ${hasMinimumImageHeight ? '100%' : '72%'}
- );
- backdrop-filter: blur(12px);
- mask-image: linear-gradient(
- to bottom,
- transparent ${hasMinimumImageHeight ? '0%' : '40%'},
- black 60%
- );
- pointer-events: none;
- }
+ ${isLandscape &&
+ css`
+ ${until.tablet} {
+ aspect-ratio: 4 / 5;
+ }
+ &::after {
+ content: '';
+ position: absolute;
+ left: 0;
+ right: 0;
+ bottom: ${hasMinimumImageHeight ? '-1px' : '0'};
+ height: ${hasMinimumImageHeight
+ ? '180px'
+ : isMainMediaImage
+ ? 'min(60%, calc(200% - 120vw + 30px))'
+ : 'min(60%, 144px)'};
+ z-index: ${getZIndex('mediaOverlay')};
+ background: linear-gradient(
+ to bottom,
+ rgba(0, 0, 0, ${hasMinimumImageHeight ? '0' : '0.08'}),
+ ${headlineBackground}
+ ${hasMinimumImageHeight ? '100%' : '72%'}
+ );
+ backdrop-filter: blur(12px);
+ mask-image: linear-gradient(
+ to bottom,
+ transparent ${hasMinimumImageHeight ? '0%' : '40%'},
+ black 60%
+ );
+ pointer-events: none;
+ }
+ `}
}
`;
@@ -204,7 +213,6 @@ export const StandardLayoutArticleGrid = ({
: undefined;
const isLabs = format.theme === ArticleSpecial.Labs;
- const isImmersive = format.display === ArticleDisplay.Immersive;
const headlineBackgroundImmersive = themePalette(
'--headline-background-immersive',
@@ -224,8 +232,6 @@ export const StandardLayoutArticleGrid = ({
mainMediaType ===
'model.dotcomrendering.pageElements.MediaAtomBlockElement';
- const hasMinimumImageHeight = isLabs && isImmersive && isMainMediaImage;
-
const mainMediaAspectRatio = isMainMediaImage
? mainMedia.media.allImages[0]?.fields.aspectRatio
: isMainMediaAtom
@@ -242,9 +248,21 @@ export const StandardLayoutArticleGrid = ({
? 'auto'
: `max(calc(80vh - ${immersiveHeaderHeight}px), calc(25rem - ${immersiveHeaderHeight}px))`;
- const layoutType = getLayoutType(format, mainMediaOrientation);
+ // If tags includes news/series/the-sunday-read then use immersivePortrait layout, else
+ // use getLayoutType function to determine the layout type based on the format and main media orientation.
+ const layoutType = isWeekendRead(article.tags)
+ ? 'immersivePortrait'
+ : getLayoutType(format, mainMediaOrientation);
+
+ getLayoutType(format, mainMediaOrientation);
const contentLayoutName = `${ArticleDisplay[format.display]}Layout`;
+ const isImmersive =
+ layoutType === 'immersivePortrait' ||
+ layoutType === 'immersiveLandscape';
+
+ const hasMinimumImageHeight = isLabs && isImmersive && isMainMediaImage;
+
const ageWarning = getAgeWarning(
article.tags,
article.webPublicationDateDeprecated,
@@ -266,11 +284,18 @@ export const StandardLayoutArticleGrid = ({
}
`;
+ const hideCaptionBeneathMainMedia =
+ layoutType === 'media' ||
+ layoutType === 'immersiveLandscape' ||
+ layoutType === 'immersivePortrait';
+
return (
{article.affiliateLinksDisclaimerRequired && (
-
+ {layoutType === 'immersivePortrait' ? (
-
+ ) : (
+
+
+
+ )}
)}
+ tags.some((tag) =>
+ [
+ 'news/series/the-saturday-read',
+ 'news/series/the-sunday-read',
+ ].includes(tag.id),
+ );
diff --git a/dotcom-rendering/src/lib/articleMeta.ts b/dotcom-rendering/src/lib/articleMeta.ts
index 54c8494ffab..c1e1947de00 100644
--- a/dotcom-rendering/src/lib/articleMeta.ts
+++ b/dotcom-rendering/src/lib/articleMeta.ts
@@ -36,8 +36,12 @@ export const shouldShowAvatar = (
export const shouldShowContributor = (
format: ArticleFormat,
isImmersiveGrid = false,
+ isWeekendRead = false,
): boolean => {
- if (format.display === ArticleDisplay.Immersive && !isImmersiveGrid) {
+ if (
+ (format.display === ArticleDisplay.Immersive && !isImmersiveGrid) ||
+ (isWeekendRead && isImmersiveGrid)
+ ) {
return false;
}
diff --git a/dotcom-rendering/src/lib/renderElement.tsx b/dotcom-rendering/src/lib/renderElement.tsx
index 7b5a7447f87..198e95d85c3 100644
--- a/dotcom-rendering/src/lib/renderElement.tsx
+++ b/dotcom-rendering/src/lib/renderElement.tsx
@@ -75,6 +75,7 @@ import {
} from '../layouts/lib/interactiveLegacyStyling';
import type { Switches } from '../types/config';
import type { FEElement, RoleType, StarRating } from '../types/content';
+import type { TagType } from '../types/tag';
import {
ArticleDesign,
type ArticleFormat,
@@ -110,6 +111,7 @@ type Props = {
articleArrangement?: LayoutType;
idApiUrl?: string;
isInDeprecatedInteractiveLayout?: boolean;
+ tags?: TagType[];
};
// updateRole modifies the role of an element in a way appropriate for most
@@ -1057,6 +1059,7 @@ export const RenderArticleElement = ({
articleArrangement,
idApiUrl,
isInDeprecatedInteractiveLayout,
+ tags,
}: Props) => {
const withUpdatedRole = updateRole(element, format);
@@ -1112,6 +1115,7 @@ export const RenderArticleElement = ({
isTimeline={isTimeline}
isInDeprecatedInteractiveLayout={isInDeprecatedInteractiveLayout}
articleArrangement={articleArrangement}
+ tags={tags}
>
{el}