From e2139b9d2c80765cd27e36780152097a6ee97aa0 Mon Sep 17 00:00:00 2001 From: Frederick O'Brien Date: Thu, 22 Oct 2026 17:49:27 +0100 Subject: [PATCH 1/2] Breathtakingly rough proof of concept --- .../src/components/SeriesSectionLink.tsx | 23 +++++-- .../src/layouts/StandardLayoutArticleGrid.tsx | 66 +++++++++++++++++-- .../src/layouts/lib/articleArrangements.ts | 34 ++++++++++ 3 files changed, 112 insertions(+), 11 deletions(-) diff --git a/dotcom-rendering/src/components/SeriesSectionLink.tsx b/dotcom-rendering/src/components/SeriesSectionLink.tsx index 3af813da98b..b4a13be3885 100644 --- a/dotcom-rendering/src/components/SeriesSectionLink.tsx +++ b/dotcom-rendering/src/components/SeriesSectionLink.tsx @@ -470,13 +470,22 @@ export const SeriesSectionLink = ({ marginRight, fontStyles(format), breakWord, - css` - box-shadow: - -6px 0 0 0 - ${themePalette('--series-title-background')}, - 6px 0 0 0 - ${themePalette('--series-title-background')}; - `, + layoutType !== 'interactiveTopHat' && + css` + box-shadow: + -6px 0 0 0 + ${themePalette( + '--series-title-background', + )}, + 6px 0 0 0 + ${themePalette( + '--series-title-background', + )}; + `, + layoutType === 'interactiveTopHat' && + css` + padding: 0 4px; + `, ]} data-component="series" data-link-name="article series" diff --git a/dotcom-rendering/src/layouts/StandardLayoutArticleGrid.tsx b/dotcom-rendering/src/layouts/StandardLayoutArticleGrid.tsx index 2fbb6d13567..164f1f3f46e 100644 --- a/dotcom-rendering/src/layouts/StandardLayoutArticleGrid.tsx +++ b/dotcom-rendering/src/layouts/StandardLayoutArticleGrid.tsx @@ -1,6 +1,11 @@ import { css } from '@emotion/react'; import { log } from '@guardian/libs'; -import { from, space, until } from '@guardian/source/foundations'; +import { + from, + headlineMedium17, + space, + until, +} from '@guardian/source/foundations'; import { Hide } from '@guardian/source/react-components'; import { StraightLines } from '@guardian/source-development-kitchen/react-components'; import { AffiliateDisclaimer } from '../components/AffiliateProducts/AffiliateDisclaimer'; @@ -259,7 +264,10 @@ export const StandardLayoutArticleGrid = ({ } `; const hideCaptionBeneathMainMedia = - layoutType === 'media' || isImmersiveLandscape || isImmersivePortrait; + layoutType === 'media' || + layoutType === 'interactiveTopHat' || + isImmersiveLandscape || + isImmersivePortrait; return (
@@ -595,6 +620,9 @@ export const StandardLayoutArticleGrid = ({ layoutType={layoutType} element="aside" css={[ + css` + position: relative; + `, isImmersive && branding != null && captionText != null && @@ -725,6 +753,36 @@ export const StandardLayoutArticleGrid = ({ /> )} +
+ + + + +
Scroll to continue
+
= { standard: standardCss, showcase: showcaseCss, @@ -337,6 +369,7 @@ const layoutCssMaps: Record = { picture: pictureCss, comment: commentCss, interactive: standardCss, + interactiveTopHat: interactiveTopHatCss, immersiveLandscape: immersiveLandscapeCss, immersivePortrait: immersivePortraitCss, }; @@ -389,6 +422,7 @@ export const getLayoutType = ( format: ArticleFormat, mainMediaOrientation: 'portrait' | 'landscape' | 'square', ): LayoutType => { + // return 'interactiveTopHat'; if (format.display === ArticleDisplay.Immersive) { if ( mainMediaOrientation === 'portrait' || From 0c1064fbd0556134e99b5c288dae7184b60ff50c Mon Sep 17 00:00:00 2001 From: Frederick O'Brien Date: Sat, 24 Oct 2026 22:43:12 +0100 Subject: [PATCH 2/2] Rough implementation of both top hat variants --- .../InteractiveLayoutAtom.stories.tsx | 2 +- .../components/InteractiveLayoutAtom.test.tsx | 2 +- .../src/components/SeriesSectionLink.tsx | 4 +- .../src/layouts/StandardLayoutArticleGrid.tsx | 152 +++++++++++------- .../src/layouts/lib/articleArrangements.ts | 71 +++++--- .../src/lib/interactiveAtoms.test.tsx | 2 +- 6 files changed, 153 insertions(+), 80 deletions(-) diff --git a/dotcom-rendering/src/components/InteractiveLayoutAtom.stories.tsx b/dotcom-rendering/src/components/InteractiveLayoutAtom.stories.tsx index 42e17066399..27fd4e99906 100644 --- a/dotcom-rendering/src/components/InteractiveLayoutAtom.stories.tsx +++ b/dotcom-rendering/src/components/InteractiveLayoutAtom.stories.tsx @@ -21,7 +21,7 @@ export const InteractiveLayoutAtom = meta.story({ design: 0, theme: 0, }, - articleArrangement: 'interactive', + articleArrangement: 'interactiveStandard', }, decorators: (StoryComponent) => (
{ design: 0, theme: 0, }} - articleArrangement="interactive" + articleArrangement="interactiveStandard" />, ); diff --git a/dotcom-rendering/src/components/SeriesSectionLink.tsx b/dotcom-rendering/src/components/SeriesSectionLink.tsx index b4a13be3885..c632a50e2b3 100644 --- a/dotcom-rendering/src/components/SeriesSectionLink.tsx +++ b/dotcom-rendering/src/components/SeriesSectionLink.tsx @@ -470,7 +470,7 @@ export const SeriesSectionLink = ({ marginRight, fontStyles(format), breakWord, - layoutType !== 'interactiveTopHat' && + layoutType !== 'interactiveTopHatFull' && css` box-shadow: -6px 0 0 0 @@ -482,7 +482,7 @@ export const SeriesSectionLink = ({ '--series-title-background', )}; `, - layoutType === 'interactiveTopHat' && + layoutType === 'interactiveTopHatFull' && css` padding: 0 4px; `, diff --git a/dotcom-rendering/src/layouts/StandardLayoutArticleGrid.tsx b/dotcom-rendering/src/layouts/StandardLayoutArticleGrid.tsx index 164f1f3f46e..c0cb2948728 100644 --- a/dotcom-rendering/src/layouts/StandardLayoutArticleGrid.tsx +++ b/dotcom-rendering/src/layouts/StandardLayoutArticleGrid.tsx @@ -166,6 +166,41 @@ const GridItem = ({ ); +const TopHatScrollPrompt = ({ topGap }: { topGap: number }) => ( +
+ + + + +
Scroll to continue
+
+); + export const StandardLayoutArticleGrid = ({ article, format, @@ -234,11 +269,38 @@ export const StandardLayoutArticleGrid = ({ const layoutType = getLayoutType(format, mainMediaOrientation); + const setCentreRule = (gridLayout: LayoutType) => { + if (isLabs || gridLayout === 'interactiveStandard') return undefined; + + const getColumnCount = () => { + switch (gridLayout) { + case 'immersivePortrait': + case 'immersiveLandscape': + return 4; + case 'interactiveTopHatFull': + return 6; + case 'interactiveTopHatLite': + return 5; + default: + return 3; + } + }; + + return css` + ${from.leftCol} { + ${grid.centreRule(getColumnCount())} + } + `; + }; + const contentLayoutName = `${ArticleDisplay[format.display]}Layout`; const isImmersivePortrait = layoutType === 'immersivePortrait'; const isImmersiveLandscape = layoutType === 'immersiveLandscape'; const isImmersive = isImmersivePortrait || isImmersiveLandscape; + const isInteractiveTopHat = + layoutType === 'interactiveTopHatLite' || + layoutType === 'interactiveTopHatFull'; const hasMinimumImageHeight = isLabs && isImmersivePortrait && isMainMediaImage; @@ -265,10 +327,16 @@ export const StandardLayoutArticleGrid = ({ `; const hideCaptionBeneathMainMedia = layoutType === 'media' || - layoutType === 'interactiveTopHat' || + isInteractiveTopHat || isImmersiveLandscape || isImmersivePortrait; + const topHatFurnitureBackgroundStyles = css` + z-index: ${getZIndex('mediaOverlay')}; + background: rgba(255, 255, 255, 0.9); + padding: 0 10px; + `; + return (
@@ -450,6 +499,10 @@ export const StandardLayoutArticleGrid = ({ flex-direction: column; justify-content: space-between; `, + isInteractiveTopHat && + css` + z-index: ${getZIndex('mediaOverlay')}; + `, ]} > {layoutType === 'picture' || layoutType === 'comment' ? ( @@ -560,6 +614,9 @@ export const StandardLayoutArticleGrid = ({ area="standfirst" layoutType={layoutType} css={[ + css` + position: relative; + `, isImmersive && css` ${until.tablet} { @@ -576,6 +633,7 @@ export const StandardLayoutArticleGrid = ({ padding-bottom: 14px; } `, + isInteractiveTopHat && topHatFurnitureBackgroundStyles, ]} > + {layoutType === 'interactiveTopHatLite' && ( + + )} {isImmersive && ( {format.display !== ArticleDisplay.Immersive && format.design !== ArticleDesign.Audio && - layoutType !== 'immersivePortrait' && ( + layoutType !== 'immersivePortrait' && + !isInteractiveTopHat && (
)} -
- - - - -
Scroll to continue
-
+ {layoutType === 'interactiveTopHatFull' && ( + + )} )} - {layoutType === 'interactive' && ( + {layoutType === 'interactiveStandard' && (