From 67585300cb02aa053b547ac81e524ea13ed07547 Mon Sep 17 00:00:00 2001 From: DanielCliftonGuardian <110032454+DanielCliftonGuardian@users.noreply.github.com> Date: Thu, 7 May 2026 15:22:17 +0100 Subject: [PATCH 1/5] World cup sub nav articles WIP --- .../src/components/DirectoryPageNav.tsx | 113 ++++++++++++++++++ dotcom-rendering/src/layouts/AudioLayout.tsx | 6 +- .../src/layouts/CommentLayout.tsx | 6 +- dotcom-rendering/src/layouts/LiveLayout.tsx | 6 +- .../src/layouts/PictureLayout.tsx | 6 +- .../src/layouts/ShowcaseLayout.tsx | 8 +- .../src/layouts/StandardLayout.tsx | 6 +- 7 files changed, 144 insertions(+), 7 deletions(-) diff --git a/dotcom-rendering/src/components/DirectoryPageNav.tsx b/dotcom-rendering/src/components/DirectoryPageNav.tsx index f2304e76043..cb2519b9bb7 100644 --- a/dotcom-rendering/src/components/DirectoryPageNav.tsx +++ b/dotcom-rendering/src/components/DirectoryPageNav.tsx @@ -10,9 +10,13 @@ import { headlineMedium15Object, headlineMedium17Object, palette, + space, + textSans14Object, + textSansBold14Object, } from '@guardian/source/foundations'; import { grid } from '../grid'; import { generateImageURL } from '../lib/image'; +import { palette as themePalette } from '../palette'; import type { TagType } from '../types/tag'; type Props = { @@ -23,6 +27,7 @@ type Props = { interface DirectoryPageNavConfig { pageIds: string[]; tagIds: string[]; + variant?: 'subnav'; textColor: string; backgroundColor: string; title: { label: string; id: string }; @@ -120,6 +125,40 @@ const configs = [ 'https://uploads.guim.co.uk/2026/03/03/winter-paralympics-980px.jpg', }, }, + // World Cup 2026 + { + pageIds: [] as string[], + tagIds: ['football/world-cup-2026'], + variant: 'subnav', + textColor: palette.neutral[7], + backgroundColor: palette.brand[400], + title: { + label: 'World Cup 2026', + id: 'football/world-cup-2026', + }, + links: [ + { + label: 'Match centre', + id: 'football/world-cup-2026/overview', + }, + { + label: 'Player guide', + id: '', + }, + { + label: 'Bracketology', + id: '', + }, + { + label: 'Golden boot', + id: '', + }, + { + label: 'More football', + id: 'football', + }, + ], + }, ] satisfies DirectoryPageNavConfig[]; export const DirectoryPageNav = ({ pageId, pageTags }: Props) => { @@ -135,6 +174,80 @@ export const DirectoryPageNav = ({ pageId, pageTags }: Props) => { return null; } + if (config.variant === 'subnav') { + const subnavWrapperStyles = css({ + backgroundColor: config.backgroundColor, + }); + + const subnavListStyles = css({ + ...textSans14Object, + display: 'flex', + columnGap: space[2], + color: 'inherit', + minHeight: 28, + width: '100%', + paddingTop: space[2], + [from.mobileMedium]: { + paddingTop: space[3], + }, + [from.tablet]: { + minHeight: 30, + }, + [from.leftCol]: { + paddingTop: 14, + }, + overflowX: 'scroll', + scrollbarWidth: 'none', + '&::-webkit-scrollbar': { + display: 'none', + }, + listStyle: 'none', + padding: 0, + }); + + const subnavListItemStyles = css({ + whiteSpace: 'nowrap', + }); + + const subnavLinkStyles = css({ + color: themePalette('--masthead-nav-link-text'), + textDecoration: 'none', + paddingRight: space[1], + '&:hover': { + textDecoration: 'underline', + color: themePalette('--masthead-nav-link-text-hover'), + }, + }); + + const subnavSelectedLinkStyles = css({ + ...textSansBold14Object, + }); + + return ( + + ); + } + const { textColor, backgroundColor } = config; const nav = css({ diff --git a/dotcom-rendering/src/layouts/AudioLayout.tsx b/dotcom-rendering/src/layouts/AudioLayout.tsx index d3b68d8c2ba..f1a938e7e12 100644 --- a/dotcom-rendering/src/layouts/AudioLayout.tsx +++ b/dotcom-rendering/src/layouts/AudioLayout.tsx @@ -164,6 +164,10 @@ export const AudioLayout = (props: WebProps | AppProps) => { const isLabs = format.theme === ArticleSpecial.Labs; + const isWorldCup2026 = article.tags.some( + (tag) => tag.id === 'football/world-cup-2026', + ); + const renderAds = canRenderAds(article); return ( @@ -191,7 +195,7 @@ export const AudioLayout = (props: WebProps | AppProps) => { discussionApiUrl={article.config.discussionApiUrl} idApiUrl={article.config.idApiUrl} contributionsServiceUrl={contributionsServiceUrl} - showSubNav={!isLabs} + showSubNav={!isLabs && !isWorldCup2026} showSlimNav={false} hasPageSkinContentSelfConstrain={true} pageId={article.pageId} diff --git a/dotcom-rendering/src/layouts/CommentLayout.tsx b/dotcom-rendering/src/layouts/CommentLayout.tsx index 57571eda45a..c12fbddf3b6 100644 --- a/dotcom-rendering/src/layouts/CommentLayout.tsx +++ b/dotcom-rendering/src/layouts/CommentLayout.tsx @@ -300,6 +300,10 @@ export const CommentLayout = (props: WebProps | AppsProps) => { const renderAds = canRenderAds(article); + const isWorldCup2026 = article.tags.some( + (tag) => tag.id === 'football/world-cup-2026', + ); + return ( <> {isWeb && ( @@ -325,7 +329,7 @@ export const CommentLayout = (props: WebProps | AppsProps) => { discussionApiUrl={article.config.discussionApiUrl} idApiUrl={article.config.idApiUrl} contributionsServiceUrl={contributionsServiceUrl} - showSubNav={true} + showSubNav={!isWorldCup2026} showSlimNav={false} hasPageSkin={false} hasPageSkinContentSelfConstrain={false} diff --git a/dotcom-rendering/src/layouts/LiveLayout.tsx b/dotcom-rendering/src/layouts/LiveLayout.tsx index ff64da7bd6f..f534cc7e753 100644 --- a/dotcom-rendering/src/layouts/LiveLayout.tsx +++ b/dotcom-rendering/src/layouts/LiveLayout.tsx @@ -310,6 +310,10 @@ export const LiveLayout = (props: WebProps | AppsProps) => { const showComments = article.isCommentable && !isPaidContent; + const isWorldCup2026 = article.tags.some( + (tag) => tag.id === 'football/world-cup-2026', + ); + return ( <> {isWeb && ( @@ -337,7 +341,7 @@ export const LiveLayout = (props: WebProps | AppsProps) => { discussionApiUrl={article.config.discussionApiUrl} idApiUrl={article.config.idApiUrl} contributionsServiceUrl={contributionsServiceUrl} - showSubNav={true} + showSubNav={!isWorldCup2026} showSlimNav={false} hasPageSkin={false} hasPageSkinContentSelfConstrain={false} diff --git a/dotcom-rendering/src/layouts/PictureLayout.tsx b/dotcom-rendering/src/layouts/PictureLayout.tsx index 01f2e5b5df6..f9ae6e0165e 100644 --- a/dotcom-rendering/src/layouts/PictureLayout.tsx +++ b/dotcom-rendering/src/layouts/PictureLayout.tsx @@ -273,6 +273,10 @@ export const PictureLayout = (props: WebProps | AppsProps) => { const renderAds = canRenderAds(article); + const isWorldCup2026 = article.tags.some( + (tag) => tag.id === 'football/world-cup-2026', + ); + const avatarUrl = getSoleContributor(article.tags, article.byline) ?.bylineLargeImageUrl; @@ -303,7 +307,7 @@ export const PictureLayout = (props: WebProps | AppsProps) => { discussionApiUrl={article.config.discussionApiUrl} idApiUrl={article.config.idApiUrl} contributionsServiceUrl={contributionsServiceUrl} - showSubNav={true} + showSubNav={!isWorldCup2026} showSlimNav={false} hasPageSkin={false} hasPageSkinContentSelfConstrain={false} diff --git a/dotcom-rendering/src/layouts/ShowcaseLayout.tsx b/dotcom-rendering/src/layouts/ShowcaseLayout.tsx index 8f0c2b67894..1c1cf592ca1 100644 --- a/dotcom-rendering/src/layouts/ShowcaseLayout.tsx +++ b/dotcom-rendering/src/layouts/ShowcaseLayout.tsx @@ -250,6 +250,10 @@ export const ShowcaseLayout = (props: WebProps | AppsProps) => { const isLabs = format.theme === ArticleSpecial.Labs; + const isWorldCup2026 = article.tags.some( + (tag) => tag.id === 'football/world-cup-2026', + ); + return ( <> {isWeb && ( @@ -282,7 +286,7 @@ export const ShowcaseLayout = (props: WebProps | AppsProps) => { contributionsServiceUrl={ contributionsServiceUrl } - showSubNav={true} + showSubNav={!isWorldCup2026} showSlimNav={false} hasPageSkin={false} hasPageSkinContentSelfConstrain={false} @@ -321,7 +325,7 @@ export const ShowcaseLayout = (props: WebProps | AppsProps) => { contributionsServiceUrl={ contributionsServiceUrl } - showSubNav={true} + showSubNav={!isWorldCup2026} showSlimNav={true} hasPageSkin={false} hasPageSkinContentSelfConstrain={false} diff --git a/dotcom-rendering/src/layouts/StandardLayout.tsx b/dotcom-rendering/src/layouts/StandardLayout.tsx index 65ebfbc5994..aa3093cac27 100644 --- a/dotcom-rendering/src/layouts/StandardLayout.tsx +++ b/dotcom-rendering/src/layouts/StandardLayout.tsx @@ -383,6 +383,10 @@ export const StandardLayout = (props: WebProps | AppProps) => { const isLabs = format.theme === ArticleSpecial.Labs; + const isWorldCup2026 = article.tags.some( + (tag) => tag.id === 'football/world-cup-2026', + ); + const renderAds = canRenderAds(article); return ( @@ -410,7 +414,7 @@ export const StandardLayout = (props: WebProps | AppProps) => { discussionApiUrl={article.config.discussionApiUrl} idApiUrl={article.config.idApiUrl} contributionsServiceUrl={contributionsServiceUrl} - showSubNav={!isLabs} + showSubNav={!isLabs && !isWorldCup2026} showSlimNav={false} hasPageSkinContentSelfConstrain={true} pageId={article.pageId} From 5a1de65fcbac1dc4a2af547dae7232b816187e61 Mon Sep 17 00:00:00 2001 From: DanielCliftonGuardian <110032454+DanielCliftonGuardian@users.noreply.github.com> Date: Thu, 7 May 2026 15:50:52 +0100 Subject: [PATCH 2/5] Fix spacing styles and add article border --- .../src/components/DirectoryPageNav.tsx | 98 ++++++++++++++----- 1 file changed, 71 insertions(+), 27 deletions(-) diff --git a/dotcom-rendering/src/components/DirectoryPageNav.tsx b/dotcom-rendering/src/components/DirectoryPageNav.tsx index cb2519b9bb7..deffe707640 100644 --- a/dotcom-rendering/src/components/DirectoryPageNav.tsx +++ b/dotcom-rendering/src/components/DirectoryPageNav.tsx @@ -137,6 +137,10 @@ const configs = [ id: 'football/world-cup-2026', }, links: [ + { + label: 'World Cup', + id: 'football/world-cup-2026', + }, { label: 'Match centre', id: 'football/world-cup-2026/overview', @@ -161,6 +165,32 @@ const configs = [ }, ] satisfies DirectoryPageNavConfig[]; +/** + * Mirrors the centering of the Masthead Titlepiece's content area at each + * breakpoint (matching Section/ElementContainer's margin-auto + max-width + * pattern), with side padding matching ElementContainer's sidePadding. + */ +const subnavInnerStyles = css({ + position: 'relative', + margin: 'auto', + padding: '0 10px', + [from.mobileLandscape]: { + padding: '0 20px', + }, + [from.tablet]: { + maxWidth: 740, + }, + [from.desktop]: { + maxWidth: 980, + }, + [from.leftCol]: { + maxWidth: 1140, + }, + [from.wide]: { + maxWidth: 1300, + }, +}); + export const DirectoryPageNav = ({ pageId, pageTags }: Props) => { const config = configs.find( (cfg) => @@ -177,25 +207,19 @@ export const DirectoryPageNav = ({ pageId, pageTags }: Props) => { if (config.variant === 'subnav') { const subnavWrapperStyles = css({ backgroundColor: config.backgroundColor, + // paddingBottom: space[1], }); const subnavListStyles = css({ ...textSans14Object, display: 'flex', + alignItems: 'center', columnGap: space[2], - color: 'inherit', minHeight: 28, width: '100%', - paddingTop: space[2], - [from.mobileMedium]: { - paddingTop: space[3], - }, [from.tablet]: { minHeight: 30, }, - [from.leftCol]: { - paddingTop: 14, - }, overflowX: 'scroll', scrollbarWidth: 'none', '&::-webkit-scrollbar': { @@ -207,6 +231,8 @@ export const DirectoryPageNav = ({ pageId, pageTags }: Props) => { const subnavListItemStyles = css({ whiteSpace: 'nowrap', + display: 'flex', + alignItems: 'center', }); const subnavLinkStyles = css({ @@ -223,27 +249,45 @@ export const DirectoryPageNav = ({ pageId, pageTags }: Props) => { ...textSansBold14Object, }); + const subnavInnerWithBorderStyles = css(subnavInnerStyles, { + paddingTop: space[2], + [from.tablet]: { + '&::before': { + content: '""', + borderLeft: `1px solid ${themePalette( + '--masthead-nav-lines', + )}`, + position: 'absolute', + left: 0, + top: 0, + bottom: 0, + }, + }, + }); + return ( ); } From df79c127dff50c1b64e1eec3703c7046e8da80a6 Mon Sep 17 00:00:00 2001 From: DanielCliftonGuardian <110032454+DanielCliftonGuardian@users.noreply.github.com> Date: Tue, 12 May 2026 16:28:17 +0100 Subject: [PATCH 3/5] Update DirectoryPageNav.tsx --- dotcom-rendering/src/components/DirectoryPageNav.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/dotcom-rendering/src/components/DirectoryPageNav.tsx b/dotcom-rendering/src/components/DirectoryPageNav.tsx index deffe707640..85cc85c1968 100644 --- a/dotcom-rendering/src/components/DirectoryPageNav.tsx +++ b/dotcom-rendering/src/components/DirectoryPageNav.tsx @@ -262,6 +262,16 @@ export const DirectoryPageNav = ({ pageId, pageTags }: Props) => { top: 0, bottom: 0, }, + '&::after': { + content: '""', + borderRight: `1px solid ${themePalette( + '--masthead-nav-lines', + )}`, + position: 'absolute', + right: 0, + top: 0, + bottom: 0, + }, }, }); From 52613ccbdba62e36a305a8bdcec2f09470f79015 Mon Sep 17 00:00:00 2001 From: DanielCliftonGuardian <110032454+DanielCliftonGuardian@users.noreply.github.com> Date: Thu, 14 May 2026 15:27:10 +0100 Subject: [PATCH 4/5] Fix padding --- dotcom-rendering/src/components/DirectoryPageNav.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/dotcom-rendering/src/components/DirectoryPageNav.tsx b/dotcom-rendering/src/components/DirectoryPageNav.tsx index 85cc85c1968..7ba6f5dff43 100644 --- a/dotcom-rendering/src/components/DirectoryPageNav.tsx +++ b/dotcom-rendering/src/components/DirectoryPageNav.tsx @@ -173,9 +173,11 @@ const configs = [ const subnavInnerStyles = css({ position: 'relative', margin: 'auto', - padding: '0 10px', + paddingLeft: 10, + paddingRight: 10, [from.mobileLandscape]: { - padding: '0 20px', + paddingLeft: 20, + paddingRight: 20, }, [from.tablet]: { maxWidth: 740, @@ -251,6 +253,7 @@ export const DirectoryPageNav = ({ pageId, pageTags }: Props) => { const subnavInnerWithBorderStyles = css(subnavInnerStyles, { paddingTop: space[2], + paddingBottom: space[2], [from.tablet]: { '&::before': { content: '""', From 0c395c158d7ecd85106b6b3c7406218bd0acdb1a Mon Sep 17 00:00:00 2001 From: Harry Fischer Date: Wed, 20 May 2026 16:51:33 +0100 Subject: [PATCH 5/5] Hf/slim nav styles (#15913) --- .../src/components/DirectoryPageNav.tsx | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/dotcom-rendering/src/components/DirectoryPageNav.tsx b/dotcom-rendering/src/components/DirectoryPageNav.tsx index 7ba6f5dff43..8730e33613d 100644 --- a/dotcom-rendering/src/components/DirectoryPageNav.tsx +++ b/dotcom-rendering/src/components/DirectoryPageNav.tsx @@ -28,6 +28,7 @@ interface DirectoryPageNavConfig { pageIds: string[]; tagIds: string[]; variant?: 'subnav'; + subLinkBadge?: string; textColor: string; backgroundColor: string; title: { label: string; id: string }; @@ -136,6 +137,8 @@ const configs = [ label: 'World Cup 2026', id: 'football/world-cup-2026', }, + subLinkBadge: + 'data:image/svg+xml,%3Csvg%20width%3D%2216%22%20height%3D%2217%22%20viewBox%3D%220%200%2016%2017%22%20fill%3D%22none%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%3E%3Crect%20width%3D%224.39184%22%20height%3D%2211.5286%22%20fill%3D%22%2390DCFF%22/%3E%3Crect%20x%3D%225.80347%22%20y%3D%225%22%20width%3D%224.39184%22%20height%3D%2211.5286%22%20fill%3D%22%2390DCFF%22/%3E%3Crect%20x%3D%2211.6084%22%20width%3D%224.39184%22%20height%3D%2211.5286%22%20fill%3D%22%2390DCFF%22/%3E%3Ccircle%20cx%3D%227.99939%22%20cy%3D%222.19592%22%20r%3D%222.19592%22%20fill%3D%22%2390DCFF%22/%3E%3C/svg%3E', links: [ { label: 'World Cup', @@ -235,11 +238,23 @@ export const DirectoryPageNav = ({ pageId, pageTags }: Props) => { whiteSpace: 'nowrap', display: 'flex', alignItems: 'center', + '&:first-of-type': { + borderRight: `1px solid ${themePalette( + '--masthead-nav-lines', + )}`, + paddingRight: space[1], + 'a:not(:hover)': { + color: palette.sport[600], + }, + }, }); const subnavLinkStyles = css({ color: themePalette('--masthead-nav-link-text'), textDecoration: 'none', + display: 'inline-flex', + alignItems: 'center', + columnGap: space[1], paddingRight: space[1], '&:hover': { textDecoration: 'underline', @@ -247,6 +262,12 @@ export const DirectoryPageNav = ({ pageId, pageTags }: Props) => { }, }); + const subLinkBadgeStyles = css({ + width: 16, + height: 17, + flexShrink: 0, + }); + const subnavSelectedLinkStyles = css({ ...textSansBold14Object, }); @@ -295,6 +316,15 @@ export const DirectoryPageNav = ({ pageId, pageTags }: Props) => { : undefined, ]} > + {config.subLinkBadge && + link.label === 'World Cup' ? ( + + ) : null} {link.label}