From 4424336e49160917fb75477a7ee74c3752049d8a Mon Sep 17 00:00:00 2001
From: DanielCliftonGuardian
<110032454+DanielCliftonGuardian@users.noreply.github.com>
Date: Wed, 15 Apr 2026 15:23:21 +0100
Subject: [PATCH 01/16] World cup 2026 Nav
---
.../components/DirectoryPageNav.stories.tsx | 12 +++++++
.../src/components/DirectoryPageNav.tsx | 32 +++++++++++++++++++
2 files changed, 44 insertions(+)
diff --git a/dotcom-rendering/src/components/DirectoryPageNav.stories.tsx b/dotcom-rendering/src/components/DirectoryPageNav.stories.tsx
index 74e0f21aedf..02c208cf193 100644
--- a/dotcom-rendering/src/components/DirectoryPageNav.stories.tsx
+++ b/dotcom-rendering/src/components/DirectoryPageNav.stories.tsx
@@ -22,6 +22,18 @@ export const WomensEuro2025 = meta.story({
},
});
+export const WorldCup2026 = meta.story({
+ args: {
+ pageId: 'football/world-cup-2026',
+ },
+});
+
+export const WorldCup2026Fixtures = meta.story({
+ args: {
+ pageId: 'football/world-cup-2026/fixtures',
+ },
+});
+
export const OtherCompetition = meta.story({
args: {
pageId: 'football/premierleague/table',
diff --git a/dotcom-rendering/src/components/DirectoryPageNav.tsx b/dotcom-rendering/src/components/DirectoryPageNav.tsx
index f2304e76043..f505971d0c6 100644
--- a/dotcom-rendering/src/components/DirectoryPageNav.tsx
+++ b/dotcom-rendering/src/components/DirectoryPageNav.tsx
@@ -37,6 +37,38 @@ interface DirectoryPageNavConfig {
}
const configs = [
+ // World Cup 2026
+ {
+ pageIds: [
+ 'football/world-cup-2026',
+ 'football/world-cup-2026/fixtures',
+ 'football/world-cup-2026/overview',
+ ],
+ tagIds: ['football/world-cup-2026'],
+ textColor: palette.neutral[100],
+ backgroundColor: palette.sport[400],
+ title: {
+ label: 'World Cup 2026',
+ id: 'football/world-cup-2026',
+ },
+ links: [
+ {
+ label: 'Fixtures',
+ id: 'football/world-cup-2026/fixtures',
+ },
+ {
+ label: 'Tables',
+ id: 'football/world-cup-2026/overview',
+ },
+ // TODO: add golden boot link once interactive page ID is known
+ // TODO: add team guide link once interactive page ID is known
+ {
+ label: 'Full coverage',
+ id: 'football/world-cup-2026',
+ },
+ ],
+ // TODO: add backgroundImages once assets are available
+ },
// Winter Olympics 2026
{
pageIds: [
From f65260e3946de0ec210646b4d67a2b0384a5e05e Mon Sep 17 00:00:00 2001
From: DanielCliftonGuardian
<110032454+DanielCliftonGuardian@users.noreply.github.com>
Date: Wed, 6 May 2026 16:18:08 +0100
Subject: [PATCH 02/16] Add links, update title and background colour
---
.../src/components/DirectoryPageNav.tsx | 37 +++++++++++++------
1 file changed, 26 insertions(+), 11 deletions(-)
diff --git a/dotcom-rendering/src/components/DirectoryPageNav.tsx b/dotcom-rendering/src/components/DirectoryPageNav.tsx
index f505971d0c6..6ad181d7262 100644
--- a/dotcom-rendering/src/components/DirectoryPageNav.tsx
+++ b/dotcom-rendering/src/components/DirectoryPageNav.tsx
@@ -46,28 +46,43 @@ const configs = [
],
tagIds: ['football/world-cup-2026'],
textColor: palette.neutral[100],
- backgroundColor: palette.sport[400],
+ backgroundColor: palette.brand[400],
title: {
- label: 'World Cup 2026',
+ label: 'World Cup',
id: 'football/world-cup-2026',
},
links: [
{
- label: 'Fixtures',
- id: 'football/world-cup-2026/fixtures',
+ label: 'Match centre',
+ id: 'football/world-cup-2026/overview',
},
{
- label: 'Tables',
- id: 'football/world-cup-2026/overview',
+ label: 'Player guide',
+ id: '',
},
- // TODO: add golden boot link once interactive page ID is known
- // TODO: add team guide link once interactive page ID is known
{
- label: 'Full coverage',
- id: 'football/world-cup-2026',
+ label: 'Bracketology',
+ id: '',
+ },
+ {
+ label: 'Golden boot',
+ id: '',
+ },
+ {
+ label: 'More football',
+ id: 'football',
},
],
- // TODO: add backgroundImages once assets are available
+ // backgroundImages: {
+ // mobile: '',
+ // mobileLandscape:
+ // '',
+ // phablet:
+ // '',
+ // tablet: '',
+ // desktop:
+ // '',
+ // },
},
// Winter Olympics 2026
{
From aeb8670f4df485b72d6f76c62035c2edc9c1b64d Mon Sep 17 00:00:00 2001
From: DanielCliftonGuardian
<110032454+DanielCliftonGuardian@users.noreply.github.com>
Date: Fri, 15 May 2026 16:36:11 +0100
Subject: [PATCH 03/16] Remove subnav above header
---
dotcom-rendering/src/components/DirectoryPageNav.tsx | 2 +-
dotcom-rendering/src/layouts/FrontLayout.tsx | 8 +++++++-
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/dotcom-rendering/src/components/DirectoryPageNav.tsx b/dotcom-rendering/src/components/DirectoryPageNav.tsx
index 6ad181d7262..3082ad67659 100644
--- a/dotcom-rendering/src/components/DirectoryPageNav.tsx
+++ b/dotcom-rendering/src/components/DirectoryPageNav.tsx
@@ -44,7 +44,7 @@ const configs = [
'football/world-cup-2026/fixtures',
'football/world-cup-2026/overview',
],
- tagIds: ['football/world-cup-2026'],
+ tagIds: [],
textColor: palette.neutral[100],
backgroundColor: palette.brand[400],
title: {
diff --git a/dotcom-rendering/src/layouts/FrontLayout.tsx b/dotcom-rendering/src/layouts/FrontLayout.tsx
index 8ebbbcaed45..2f22162efbe 100644
--- a/dotcom-rendering/src/layouts/FrontLayout.tsx
+++ b/dotcom-rendering/src/layouts/FrontLayout.tsx
@@ -122,6 +122,12 @@ export const FrontLayout = ({ front, NAV }: Props) => {
const hasPageSkin = renderAds && hasPageSkinConfig;
+ const isWorldCup2026 = [
+ 'football/world-cup-2026',
+ 'football/world-cup-2026/fixtures',
+ 'football/world-cup-2026/overview',
+ ].includes(pageId);
+
const filteredCollections = front.pressedPage.collections.filter(
(collection) => !isHighlights(collection),
);
@@ -227,7 +233,7 @@ export const FrontLayout = ({ front, NAV }: Props) => {
discussionApiUrl={front.config.discussionApiUrl}
contributionsServiceUrl={contributionsServiceUrl}
idApiUrl={front.config.idApiUrl}
- showSubNav={!isPaidContent}
+ showSubNav={!isPaidContent && !isWorldCup2026}
showSlimNav={false}
hasPageSkin={hasPageSkin}
hasPageSkinContentSelfConstrain={true}
From 9371ea1b7e0d8fe449fd6d31ab65ac725e3891f7 Mon Sep 17 00:00:00 2001
From: DanielCliftonGuardian
<110032454+DanielCliftonGuardian@users.noreply.github.com>
Date: Fri, 15 May 2026 16:45:15 +0100
Subject: [PATCH 04/16] Remove subnav for world cup 2026 fixtures page
---
dotcom-rendering/src/layouts/SportDataPageLayout.tsx | 9 ++++++++-
dotcom-rendering/src/layouts/TagPageLayout.tsx | 8 +++++++-
2 files changed, 15 insertions(+), 2 deletions(-)
diff --git a/dotcom-rendering/src/layouts/SportDataPageLayout.tsx b/dotcom-rendering/src/layouts/SportDataPageLayout.tsx
index 90f7ddfbe06..c296ca55dec 100644
--- a/dotcom-rendering/src/layouts/SportDataPageLayout.tsx
+++ b/dotcom-rendering/src/layouts/SportDataPageLayout.tsx
@@ -2,6 +2,7 @@ import { palette } from '@guardian/source/foundations';
import { AdSlot } from '../components/AdSlot.web';
import { AppsFooter } from '../components/AppsFooter.island';
import { CricketScorecardPage } from '../components/CricketScorecardPage';
+import { DirectoryPageNav } from '../components/DirectoryPageNav';
import { FootballMatchesPageWrapper } from '../components/FootballMatchesPageWrapper.island';
import { FootballMatchInfoPage } from '../components/FootballMatchInfoPage';
import { FootballTablesPage } from '../components/FootballTablesPage';
@@ -100,6 +101,12 @@ export const SportDataPageLayout = (
const contributionsServiceUrl = getContributionsServiceUrl(sportData);
+ const isWorldCup2026 = [
+ 'football/world-cup-2026',
+ 'football/world-cup-2026/fixtures',
+ 'football/world-cup-2026/overview',
+ ].includes(sportData.config.pageId);
+
return (
<>
{isWeb && (
@@ -126,7 +133,7 @@ export const SportDataPageLayout = (
discussionApiUrl={sportData.config.discussionApiUrl}
idApiUrl={sportData.config.idApiUrl}
contributionsServiceUrl={contributionsServiceUrl}
- showSubNav={true}
+ showSubNav={!isWorldCup2026}
showSlimNav={false}
hasPageSkin={sportData.config.hasPageSkin}
pageId={sportData.config.pageId}
diff --git a/dotcom-rendering/src/layouts/TagPageLayout.tsx b/dotcom-rendering/src/layouts/TagPageLayout.tsx
index d67a815a0c2..ab67cb6ded5 100644
--- a/dotcom-rendering/src/layouts/TagPageLayout.tsx
+++ b/dotcom-rendering/src/layouts/TagPageLayout.tsx
@@ -66,6 +66,12 @@ export const TagPageLayout = ({ tagPage, NAV }: Props) => {
const isAccessibilityPage =
tagPage.config.pageId === 'help/accessibility-help';
+ const isWorldCup2026 = [
+ 'football/world-cup-2026',
+ 'football/world-cup-2026/fixtures',
+ 'football/world-cup-2026/overview',
+ ].includes(pageId);
+
return (
<>
@@ -91,7 +97,7 @@ export const TagPageLayout = ({ tagPage, NAV }: Props) => {
discussionApiUrl={tagPage.config.discussionApiUrl}
idApiUrl={tagPage.config.idApiUrl}
contributionsServiceUrl={contributionsServiceUrl}
- showSubNav={true}
+ showSubNav={!isWorldCup2026}
showSlimNav={false}
hasPageSkin={hasPageSkin}
pageId={pageId}
From 20ccc93643389c415e5546a111d5ce872cbead63 Mon Sep 17 00:00:00 2001
From: DanielCliftonGuardian
<110032454+DanielCliftonGuardian@users.noreply.github.com>
Date: Fri, 15 May 2026 16:46:29 +0100
Subject: [PATCH 05/16] Update SportDataPageLayout.tsx
---
dotcom-rendering/src/layouts/SportDataPageLayout.tsx | 2 ++
1 file changed, 2 insertions(+)
diff --git a/dotcom-rendering/src/layouts/SportDataPageLayout.tsx b/dotcom-rendering/src/layouts/SportDataPageLayout.tsx
index c296ca55dec..17ca51b8c8f 100644
--- a/dotcom-rendering/src/layouts/SportDataPageLayout.tsx
+++ b/dotcom-rendering/src/layouts/SportDataPageLayout.tsx
@@ -145,6 +145,8 @@ export const SportDataPageLayout = (
{isWeb && renderAds && hasSurveyAd &&
}
+ {isWeb &&
}
+
Date: Fri, 15 May 2026 16:52:02 +0100
Subject: [PATCH 06/16] Update SportDataPageLayout.tsx
---
dotcom-rendering/src/layouts/SportDataPageLayout.tsx | 3 ---
1 file changed, 3 deletions(-)
diff --git a/dotcom-rendering/src/layouts/SportDataPageLayout.tsx b/dotcom-rendering/src/layouts/SportDataPageLayout.tsx
index 17ca51b8c8f..c84d26050f6 100644
--- a/dotcom-rendering/src/layouts/SportDataPageLayout.tsx
+++ b/dotcom-rendering/src/layouts/SportDataPageLayout.tsx
@@ -2,7 +2,6 @@ import { palette } from '@guardian/source/foundations';
import { AdSlot } from '../components/AdSlot.web';
import { AppsFooter } from '../components/AppsFooter.island';
import { CricketScorecardPage } from '../components/CricketScorecardPage';
-import { DirectoryPageNav } from '../components/DirectoryPageNav';
import { FootballMatchesPageWrapper } from '../components/FootballMatchesPageWrapper.island';
import { FootballMatchInfoPage } from '../components/FootballMatchInfoPage';
import { FootballTablesPage } from '../components/FootballTablesPage';
@@ -145,8 +144,6 @@ export const SportDataPageLayout = (
{isWeb && renderAds && hasSurveyAd && }
- {isWeb && }
-
Date: Mon, 18 May 2026 09:21:13 +0100
Subject: [PATCH 07/16] Hf/tweak world cup nav (#15886)
* update style to match new design and add image assets
* add wide images to old headers
* fix nav style and add mobLandscape and phablet image
* remove unused
---
.../src/components/DirectoryPageNav.tsx | 161 ++++++++++--------
1 file changed, 90 insertions(+), 71 deletions(-)
diff --git a/dotcom-rendering/src/components/DirectoryPageNav.tsx b/dotcom-rendering/src/components/DirectoryPageNav.tsx
index 3082ad67659..88e4a55b270 100644
--- a/dotcom-rendering/src/components/DirectoryPageNav.tsx
+++ b/dotcom-rendering/src/components/DirectoryPageNav.tsx
@@ -3,13 +3,10 @@ import {
type Breakpoint,
breakpoints,
from,
- headlineBold15Object,
- headlineBold17Object,
headlineBold24Object,
headlineBold42Object,
- headlineMedium15Object,
- headlineMedium17Object,
palette,
+ textSans14Object,
} from '@guardian/source/foundations';
import { grid } from '../grid';
import { generateImageURL } from '../lib/image';
@@ -25,6 +22,7 @@ interface DirectoryPageNavConfig {
tagIds: string[];
textColor: string;
backgroundColor: string;
+ titleIconImage: string;
title: { label: string; id: string };
links: { label: string; id: string }[];
backgroundImages?: {
@@ -33,6 +31,7 @@ interface DirectoryPageNavConfig {
phablet: string;
tablet: string;
desktop: string;
+ wide: string;
};
}
@@ -51,6 +50,8 @@ const configs = [
label: 'World Cup',
id: 'football/world-cup-2026',
},
+ titleIconImage:
+ 'data:image/svg+xml,%3Csvg%20width%3D%2240%22%20height%3D%2246%22%20viewBox%3D%220%200%2040%2046%22%20fill%3D%22none%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%3E%3Crect%20width%3D%2212.3697%22%20height%3D%2232.4706%22%20fill%3D%22%23D71921%22/%3E%3Crect%20x%3D%2213.5294%22%20y%3D%2213.5295%22%20width%3D%2212.3697%22%20height%3D%2232.4706%22%20fill%3D%22white%22/%3E%3Crect%20x%3D%2227.059%22%20width%3D%2212.3697%22%20height%3D%2232.4706%22%20fill%3D%22%23007E46%22/%3E%3Ccircle%20cx%3D%2219.7142%22%20cy%3D%226.18487%22%20r%3D%226.18487%22%20fill%3D%22white%22/%3E%3C/svg%3E',
links: [
{
label: 'Match centre',
@@ -73,16 +74,17 @@ const configs = [
id: 'football',
},
],
- // backgroundImages: {
- // mobile: '',
- // mobileLandscape:
- // '',
- // phablet:
- // '',
- // tablet: '',
- // desktop:
- // '',
- // },
+ backgroundImages: {
+ mobile: 'https://media.guim.co.uk/4ba0caac6d18c1fe6a5a3267b270d8c21ae6f940/0_0_750_376/750.jpg',
+ mobileLandscape:
+ 'https://media.guim.co.uk/8e1356cc926c6bbfcdb3da5908252ba0b4cbd3bb/0_0_960_376/960.jpg',
+ phablet:
+ 'https://media.guim.co.uk/ed4fe540c6a114db35c1f73fc41ee802c3fea7d3/0_0_1320_282/1320.jpg',
+ tablet: 'https://media.guim.co.uk/861646115875f3f246313036f754b2f5f1480b1a/0_0_1480_276/1480.jpg',
+ desktop:
+ 'https://media.guim.co.uk/167bec4a208bfc7fdc6b2127186b9bb183932259/0_0_1960_276/1960.jpg',
+ wide: 'https://media.guim.co.uk/4e44f9a88fcc9a3b1b5294f7e581644baa75c904/0_0_2600_276/2600.jpg',
+ },
},
// Winter Olympics 2026
{
@@ -99,6 +101,7 @@ const configs = [
label: 'Winter Olympics 2026',
id: 'sport/winter-olympics-2026',
},
+ titleIconImage: '',
links: [
{
label: 'Schedule',
@@ -126,6 +129,7 @@ const configs = [
tablet: 'https://uploads.guim.co.uk/2026/02/03/winter-olympics-740px-thin.jpg',
desktop:
'https://uploads.guim.co.uk/2026/02/03/winter-olympics-980px.jpg',
+ wide: 'https://uploads.guim.co.uk/2026/02/03/winter-olympics-980px.jpg',
},
},
// Winter Paralympics 2026
@@ -142,6 +146,7 @@ const configs = [
label: 'Winter Paralympics 2026',
id: 'sport/winter-paralympics-2026',
},
+ titleIconImage: '',
links: [
{
label: 'Results',
@@ -165,6 +170,7 @@ const configs = [
tablet: 'https://uploads.guim.co.uk/2026/03/03/winter-paralympics-740px-thin.jpg',
desktop:
'https://uploads.guim.co.uk/2026/03/03/winter-paralympics-980px.jpg',
+ wide: 'https://uploads.guim.co.uk/2026/03/03/winter-paralympics-980px.jpg',
},
},
] satisfies DirectoryPageNavConfig[];
@@ -188,14 +194,20 @@ export const DirectoryPageNav = ({ pageId, pageTags }: Props) => {
backgroundColor,
'&': css(grid.paddedContainer),
alignContent: 'space-between',
+ position: 'relative',
});
const largeLinkStyles = css({
+ position: 'absolute',
+ top: '4px',
+ left: 0,
...headlineBold24Object,
color: textColor,
textDecoration: 'none',
'&': css(grid.column.centre),
gridRow: 1,
+ display: 'flex',
+ alignItems: 'flex-start',
[from.tablet]: headlineBold42Object,
[from.leftCol]: css(
grid.between('left-column-start', 'right-column-end'),
@@ -204,35 +216,38 @@ export const DirectoryPageNav = ({ pageId, pageTags }: Props) => {
const list = css({
display: 'flex',
- flexWrap: 'wrap',
'&': css(grid.column.all),
- gridRow: 2,
alignSelf: 'end',
position: 'relative',
'--top-border-gap': '1.55rem',
+ overflowX: 'scroll',
+ scrollbarWidth: 'none',
+ padding: '10px 10px',
+ borderTop: '1px solid',
+ borderColor: palette.brand[600],
+ height: '100%',
[from.mobileLandscape]: {
- paddingLeft: 10,
+ padding: '10px 20px',
},
[from.tablet]: {
'--top-border-gap': '3rem',
},
- backgroundImage: `
- linear-gradient(
- ${textColor} 0,
- ${textColor} 1px,
- transparent 1px,
- transparent var(--top-border-gap),
- ${textColor} var(--top-border-gap),
- ${textColor} calc(var(--top-border-gap) + 1px),
- transparent 1px,
- transparent var(--top-border-gap)
- )
- `,
+ '&:after': {
+ content: '""',
+ position: 'sticky',
+ right: '-10px',
+ top: '-10px',
+ height: '100%',
+ minWidth: 40,
+ background: `linear-gradient(to left, ${backgroundColor}, transparent)`,
+ [from.mobileLandscape]: {
+ right: '-20px',
+ },
+ },
});
const selectedStyles = {
- '--selected-height': '4px',
- '--selected-opacity': '1',
+ fontWeight: 'bold',
};
const listItem = css({
@@ -249,62 +264,40 @@ export const DirectoryPageNav = ({ pageId, pageTags }: Props) => {
backgroundColor: textColor,
transition: 'height 0.3s ease-in-out, opacity 0.05s 0.1s linear',
},
- '&:hover': selectedStyles,
- [from.leftCol]: {
- flexBasis: 160,
+ [from.desktop]: {
+ '&:hover a': {
+ textDecoration: 'underline',
+ color: 'var(--masthead-nav-link-text-hover)',
+ },
},
});
const smallLink = css({
- ...headlineBold15Object,
- padding: '4px 10px 6px',
+ ...textSans14Object,
+ padding: '4px 12px 6px 0',
display: 'block',
lineHeight: 1,
color: textColor,
textDecoration: 'none',
- '&::after': {
- content: '""',
- display: 'block',
- position: 'absolute',
- top: 0,
- right: 0,
- width: 1,
- height: '1.3rem',
- backgroundColor: textColor,
- },
- [from.tablet]: headlineBold17Object,
- [from.leftCol]: {
- padding: '9px 10px 10px',
- },
- });
-
- const lastSmallLink = css(smallLink, {
- ...headlineMedium15Object,
- lineHeight: 1,
- [from.tablet]: headlineMedium17Object,
+ whiteSpace: 'nowrap',
});
return (