diff --git a/ab-testing/config/abTests.ts b/ab-testing/config/abTests.ts index 3afc5da2228..0f3781e5c78 100644 --- a/ab-testing/config/abTests.ts +++ b/ab-testing/config/abTests.ts @@ -223,6 +223,17 @@ const ABTests: ABTest[] = [ groups: ["control", "variant"], shouldForceMetricsCollection: true, }, + { + name: "articles-and-publishing-revamped-immersive-layout", + description: "New grid-based immersive layout for all articles", + owners: ["articles.and.publishing@guardian.co.uk "], + status: "ON", + expirationDate: "2027-08-31", + type: "server", + audienceSize: 0 / 100, + groups: ["enable"], + shouldForceMetricsCollection: false, + }, ]; const activeABtests = ABTests.filter((test) => test.status === "ON"); diff --git a/ab-testing/config/types.ts b/ab-testing/config/types.ts index be7d8b3d4d8..f64107a4156 100644 --- a/ab-testing/config/types.ts +++ b/ab-testing/config/types.ts @@ -15,7 +15,8 @@ type Team = | "feast" | "martech" | "identity-and-trust" - | "puzzles"; + | "puzzles" + | "articles-and-publishing"; type TestName = `${Team}-${string}`; diff --git a/dotcom-rendering/src/components/ArticleHeadline.tsx b/dotcom-rendering/src/components/ArticleHeadline.tsx index 67e3cc50970..ba48d62bac3 100644 --- a/dotcom-rendering/src/components/ArticleHeadline.tsx +++ b/dotcom-rendering/src/components/ArticleHeadline.tsx @@ -321,7 +321,7 @@ const immersiveHeadlineStyles = ( ]; } - if (layoutType === 'immersiveLandscapeDefault') { + if (layoutType === 'immersiveLandscape') { return [invertedText, darkBackground]; } @@ -523,7 +523,7 @@ export const ArticleHeadline = ({ isMatch, starRating, }: Props) => { - const isInverted = layoutType === 'immersiveLandscapeDefault'; + const isInverted = layoutType === 'immersiveLandscape'; const isLegacyImmersive = layoutType == null; switch (format.display) { case ArticleDisplay.Immersive: { diff --git a/dotcom-rendering/src/components/Figure.tsx b/dotcom-rendering/src/components/Figure.tsx index 83a444ac608..ccb113fb4b1 100644 --- a/dotcom-rendering/src/components/Figure.tsx +++ b/dotcom-rendering/src/components/Figure.tsx @@ -1,15 +1,6 @@ import { css } from '@emotion/react'; -import { - breakpoints, - from, - palette as sourcePalette, - space, - until, -} from '@guardian/source/foundations'; -import type { LayoutType } from '../layouts/lib/articleArrangements'; +import { breakpoints, from, space, until } from '@guardian/source/foundations'; import { ArticleDesign, type ArticleFormat } from '../lib/articleFormat'; -import { transparentColour } from '../lib/transparentColour'; -import { palette } from '../palette'; import type { FEElement, RoleType } from '../types/content'; type Props = { @@ -21,35 +12,8 @@ type Props = { className?: string; type?: FEElement['_type']; isTimeline?: boolean; - articleArrangement?: LayoutType; }; -const overlayMaskGradientStyles = (angle: string) => css` - mask-image: linear-gradient( - ${angle}, - rgb(0, 0, 0) 0%, - rgba(0, 0, 0, 0.9619) 12.5%, - rgba(0, 0, 0, 0.8536) 25%, - rgba(0, 0, 0, 0.6913) 37.5%, - rgba(0, 0, 0, 0.5) 50%, - rgba(0, 0, 0, 0.3087) 62.5%, - rgba(0, 0, 0, 0.1464) 75%, - rgba(0, 0, 0, 0.0381) 87.5%, - transparent 100% - ); -`; - -const blurStyles = css` - position: absolute; - inset: 0; - background-color: ${palette('--article-background')}; - backdrop-filter: blur(12px) brightness(0.5); - @supports not (backdrop-filter: blur(12px)) { - background-color: ${transparentColour(sourcePalette.neutral[10], 0.7)}; - } - ${overlayMaskGradientStyles('0deg')}; -`; - const roleCss = { inline: css` margin-top: ${space[3]}px; @@ -299,7 +263,6 @@ export const Figure = ({ className = '', type, isTimeline = false, - articleArrangement = 'standard', }: Props) => { if (isMainMedia && !isTimeline) { // Don't add in-body styles for main media elements @@ -310,17 +273,6 @@ export const Figure = ({ return (
{children} - {articleArrangement === 'immersiveLandscapeFeature' && ( -
- )}
); } diff --git a/dotcom-rendering/src/components/SeriesSectionLink.tsx b/dotcom-rendering/src/components/SeriesSectionLink.tsx index 5b077df0093..0443b19417b 100644 --- a/dotcom-rendering/src/components/SeriesSectionLink.tsx +++ b/dotcom-rendering/src/components/SeriesSectionLink.tsx @@ -408,10 +408,8 @@ export const SeriesSectionLink = ({ ); } if ( - layoutType === 'immersivePortraitDefault' || - layoutType === 'immersivePortraitFeature' || - layoutType === 'immersiveLandscapeDefault' || - layoutType === 'immersiveLandscapeFeature' + layoutType === 'immersivePortrait' || + layoutType === 'immersiveLandscape' ) { return ( <> diff --git a/dotcom-rendering/src/layouts/DecideLayout.tsx b/dotcom-rendering/src/layouts/DecideLayout.tsx index fa41ad65ca6..ae47531e140 100644 --- a/dotcom-rendering/src/layouts/DecideLayout.tsx +++ b/dotcom-rendering/src/layouts/DecideLayout.tsx @@ -36,6 +36,18 @@ interface WebProps extends BaseProps { export type Props = WebProps | AppProps; +/** + * Guards the new grid-based immersive layout for all Guardian articles + * behind a 0% a/b test + */ +export const REVAMPED_IMMERSIVE_LAYOUT_AB_TEST = + 'articles-and-publishing-revamped-immersive-layout'; + +const isInRevampedImmersiveLayoutTest = (article: Article): boolean => + article.frontendData.config.serverSideABTests[ + REVAMPED_IMMERSIVE_LAYOUT_AB_TEST + ] === 'enable'; + const DecideLayoutApps = ({ article, renderingTarget }: AppProps) => { const format = { design: article.design, @@ -58,8 +70,9 @@ const DecideLayoutApps = ({ article, renderingTarget }: AppProps) => { ); } default: { - return article.theme === ArticleSpecial.Labs && - article.design === ArticleDesign.Standard ? ( + return isInRevampedImmersiveLayoutTest(article) || + (article.theme === ArticleSpecial.Labs && + article.design === ArticleDesign.Standard) ? ( { ); } default: { - return article.theme === ArticleSpecial.Labs && - article.design === ArticleDesign.Standard ? ( + return isInRevampedImmersiveLayoutTest(article) || + (article.theme === ArticleSpecial.Labs && + article.design === ArticleDesign.Standard) ? ( { - switch (layoutType) { - case 'immersivePortraitDefault': - case 'immersivePortraitFeature': - case 'immersiveLandscapeDefault': - return 4; - default: - return 3; - } - })(); - const ageWarning = getAgeWarning( article.tags, article.webPublicationDateDeprecated, @@ -237,8 +220,7 @@ export const StandardLayoutArticleGrid = ({ `, grid.container, grid.outerRules(), - isLabs && - isImmersive && + isImmersive && css` &::before, &::after { @@ -248,29 +230,24 @@ export const StandardLayoutArticleGrid = ({ !isLabs && css` ${from.leftCol} { - ${grid.centreRule(centreRuleColumn)} + ${grid.centreRule(isImmersive ? 4 : 3)} } `, - isImmersivePortrait && + layoutType === 'immersivePortrait' && css` ${from.desktop} { grid-template-rows: 0.25fr 1fr auto; } `, - isImmersiveLandscape && + layoutType === 'immersiveLandscape' && css` ${from.desktop} { grid-template-rows: auto auto ${ageWarning != null ? '130px' : '90px'} auto auto auto auto auto; - ${grid.centreRule( - layoutType === 'immersiveLandscapeFeature' - ? 3 - : 4, - )} } `, - (isImmersivePortrait || isImmersiveLandscape) && + isImmersive && css` ${until.desktop} { /* Anchor the title consistently while wrapped text extends the media below it. */ @@ -292,14 +269,12 @@ export const StandardLayoutArticleGrid = ({ align-self: start; ${mainMediaAspectRatio != null && `aspect-ratio: ${mainMediaAspectRatio.replace(':', ' / ')};`} - ${isImmersiveLandscape && + ${layoutType === 'immersiveLandscape' && `margin-left: -20px; margin-right: -20px;`} } - ${(isImmersivePortrait || - isImmersiveLandscape) && - immersiveMediaBelowDesktop( + ${immersiveMediaBelowDesktop( headlineBackground, isMainMediaImage, )} @@ -347,7 +322,7 @@ export const StandardLayoutArticleGrid = ({ padding-bottom: ${space[2]}px; } `, - isImmersivePortrait && + layoutType === 'immersivePortrait' && css` align-self: end; margin-bottom: 0; @@ -383,8 +358,7 @@ export const StandardLayoutArticleGrid = ({ padding-bottom: ${space[8]}px; } `, - (layoutType === 'immersivePortraitDefault' || - layoutType === 'immersivePortraitFeature') && + layoutType === 'immersivePortrait' && css` ${from.desktop} { border-bottom: 1px solid @@ -393,7 +367,7 @@ export const StandardLayoutArticleGrid = ({ ${themePalette('--article-border')}; } `, - isImmersiveLandscape && + layoutType === 'immersiveLandscape' && css` ${from.desktop} { padding-bottom: ${space[8]}px; @@ -423,7 +397,7 @@ export const StandardLayoutArticleGrid = ({ padding-top: ${space[2]}px; } `, - isImmersiveLandscape && + layoutType === 'immersiveLandscape' && css` ${from.desktop} { padding-bottom: ${space[8]}px; @@ -470,7 +444,7 @@ export const StandardLayoutArticleGrid = ({ padding-top: ${space[3]}px; } `, - layoutType === 'immersivePortraitDefault' + layoutType === 'immersivePortrait' ? css` ${from.leftCol} { margin-right: -10px; @@ -481,7 +455,7 @@ export const StandardLayoutArticleGrid = ({ > {format.display !== ArticleDisplay.Immersive && format.design !== ArticleDesign.Audio && - layoutType !== 'immersivePortraitDefault' && ( + layoutType !== 'immersivePortrait' && (
{isWeb && format.theme === ArticleSpecial.Labs && diff --git a/dotcom-rendering/src/layouts/lib/articleArrangements.ts b/dotcom-rendering/src/layouts/lib/articleArrangements.ts index 25481511bb6..da0a4b45226 100644 --- a/dotcom-rendering/src/layouts/lib/articleArrangements.ts +++ b/dotcom-rendering/src/layouts/lib/articleArrangements.ts @@ -6,10 +6,8 @@ export type LayoutType = | 'standard' | 'showcase' | 'media' - | 'immersiveLandscapeDefault' - | 'immersiveLandscapeFeature' - | 'immersivePortraitDefault' - | 'immersivePortraitFeature'; + | 'immersiveLandscape' + | 'immersivePortrait'; export type Area = | 'title' @@ -165,7 +163,7 @@ const mediaCss: LayoutCssMap = { }, }; -const immersivePortraitDefaultCss: LayoutCssMap = { +const immersivePortraitCss: LayoutCssMap = { title: { belowDesktop: `${grid.column.all} grid-row: 2;`, desktop: `grid-row: 1; ${grid.between('centre-column-start', 8)};`, @@ -208,50 +206,7 @@ const immersivePortraitDefaultCss: LayoutCssMap = { }, }; -const immersivePortraitFeatureCss: LayoutCssMap = { - title: { - belowDesktop: `${grid.column.all} grid-row: 2;`, - desktop: `grid-row: 1; ${grid.between('centre-column-start', 8)};`, - leftCol: `grid-row: 1; ${grid.between('left-column-start', 9)};`, - }, - headline: { - belowDesktop: `${grid.column.all} grid-row: 3;`, - desktop: `grid-row: 2; ${grid.between('centre-column-start', 8)};`, - leftCol: `grid-row: 2; ${grid.between('left-column-start', 9)};`, - wide: `grid-row: 2; ${grid.between('left-column-start', 10)};`, - }, - media: { - belowDesktop: `${grid.column.all} grid-row: 1 / span 2;`, - desktop: `grid-row: 1 / span 4; ${grid.between(8, 'right-column-end')};`, - leftCol: `grid-row: 1 / span 3; ${grid.between(9, 'right-column-end')};`, - wide: `grid-row: 1 / span 3; ${grid.between(10, 'right-column-end')};`, - }, - standfirst: { - belowDesktop: 'grid-row: 4;', - desktop: `grid-row: 3; ${grid.between('centre-column-start', 7)};`, - leftCol: `grid-row: 3; ${grid.between('centre-column-start', 8)};`, - wide: `grid-row: 3; ${grid.between('centre-column-start', 9)};`, - }, - caption: { - belowDesktop: 'grid-row: 5;', - desktop: `grid-row: 5;`, - }, - meta: { - belowDesktop: 'grid-row: 6;', - desktop: `grid-row: 4; ${grid.between('centre-column-start', 8)};`, - leftCol: `grid-row: 3 / span 2; ${grid.column.left};`, - }, - body: { - belowDesktop: 'grid-row: 7;', - leftCol: 'grid-row: 4;', - }, - 'right-column': { - desktop: `grid-row: 5 / span 2; ${grid.column.right};`, - leftCol: `grid-row: 4; ${grid.column.right};`, - }, -}; - -const immersiveLandscapeDefaultCss: LayoutCssMap = { +const immersiveLandscapeCss: LayoutCssMap = { title: { belowDesktop: `${grid.column.all} grid-row: 2; align-self: end;`, desktop: 'grid-row: 2;', @@ -288,50 +243,12 @@ const immersiveLandscapeDefaultCss: LayoutCssMap = { }, }; -const immersiveLandscapeFeatureCss: LayoutCssMap = { - title: { - belowDesktop: `${grid.column.all} grid-row: 2; align-self: end;`, - desktop: 'grid-row: 2;', - }, - headline: { - belowDesktop: `${grid.column.all} grid-row: 3;`, - desktop: 'grid-row: 3 / span 2;', - }, - media: { - belowDesktop: `${grid.column.all} grid-row: 1 / span 2;`, - desktop: `grid-row: 1 / span 3; ${grid.between('centre-column-start', 'right-column-end')};`, - leftCol: `grid-row: 1 / span 3; ${grid.between('left-column-start', 'right-column-end')};`, - }, - standfirst: { - belowDesktop: 'grid-row: 4;', - desktop: 'grid-row: 5;', - }, - caption: { - belowDesktop: 'grid-row: 5;', - desktop: 'grid-row: 6;', - }, - meta: { - belowDesktop: 'grid-row: 6;', - desktop: `grid-row: 7;`, - leftCol: `grid-row: 5 / span 2; ${grid.column.left};`, - }, - body: { - belowDesktop: 'grid-row: 7;', - leftCol: 'grid-row: 6;', - }, - 'right-column': { - desktop: `grid-row: 6 / span 3; ${grid.column.right};`, - }, -}; - const layoutCssMaps: Record = { standard: standardCss, showcase: showcaseCss, media: mediaCss, - immersiveLandscapeDefault: immersiveLandscapeDefaultCss, - immersiveLandscapeFeature: immersiveLandscapeFeatureCss, - immersivePortraitDefault: immersivePortraitDefaultCss, - immersivePortraitFeature: immersivePortraitFeatureCss, + immersiveLandscape: immersiveLandscapeCss, + immersivePortrait: immersivePortraitCss, }; /** @@ -380,7 +297,6 @@ export const gridItemCss = ( */ export const getLayoutType = ({ isImmersive, - isFeature, orientation, isMedia, isShowcase, @@ -393,14 +309,10 @@ export const getLayoutType = ({ }): LayoutType => { if (isImmersive) { if (orientation === 'portrait') { - return isFeature - ? 'immersivePortraitFeature' - : 'immersivePortraitDefault'; + return 'immersivePortrait'; } // Square images are treated the same as landscape for immersive layouts. - return isFeature - ? 'immersiveLandscapeFeature' - : 'immersiveLandscapeDefault'; + return 'immersiveLandscape'; } if (isMedia) return 'media'; if (isShowcase) return 'showcase'; diff --git a/dotcom-rendering/src/lib/getZIndex.ts b/dotcom-rendering/src/lib/getZIndex.ts index 2b92726f8da..22fa2676144 100644 --- a/dotcom-rendering/src/lib/getZIndex.ts +++ b/dotcom-rendering/src/lib/getZIndex.ts @@ -82,14 +82,14 @@ const indices = [ // Sticky table of contents element 'tableOfContents', - // Article headline (should be above main media) - 'articleHeadline', - 'immersiveBlackBox', - // Body 'bodyArea', 'rightColumnArea', + // Article headline (should be above main media) + 'articleHeadline', + 'immersiveBlackBox', + // Media overlay 'mediaOverlay', diff --git a/dotcom-rendering/src/lib/renderElement.tsx b/dotcom-rendering/src/lib/renderElement.tsx index 70cecbba728..68bb7b4f231 100644 --- a/dotcom-rendering/src/lib/renderElement.tsx +++ b/dotcom-rendering/src/lib/renderElement.tsx @@ -1104,7 +1104,6 @@ export const RenderArticleElement = ({ type={element._type} format={format} isTimeline={isTimeline} - articleArrangement={articleArrangement} > {el}