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) && ( + Book + )} ); 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}