From 38be413a300eb80d9b68bdb0f1e3b30823dd94cc Mon Sep 17 00:00:00 2001 From: Charlotte Date: Mon, 22 Jun 2026 11:50:55 +0100 Subject: [PATCH 1/9] add hosted override colours to gallery layout --- .../src/layouts/HostedGalleryLayout.tsx | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/dotcom-rendering/src/layouts/HostedGalleryLayout.tsx b/dotcom-rendering/src/layouts/HostedGalleryLayout.tsx index 916e47dea39..17d493c0afe 100644 --- a/dotcom-rendering/src/layouts/HostedGalleryLayout.tsx +++ b/dotcom-rendering/src/layouts/HostedGalleryLayout.tsx @@ -105,6 +105,32 @@ const ctaButtonStyles = css` margin-right: ${space[3]}px; `; +/** + * Overrides palette declarations in light mode to use the accent color for the hosted content. + * @param accentColor - The accentColor to use for the hosted content in light mode. + * @returns A CSS string with the overridden palette declarations. + */ +export const overridePaletteColours = (accentColor?: string) => { + return css` + @media (prefers-color-scheme: light) { + --article-link-text: ${accentColor ?? 'inherit'}; + --article-link-text-hover: ${accentColor ?? 'inherit'}; + --article-link-border-hover: ${accentColor ?? 'inherit'}; + --accent-colour: ${accentColor ?? `${sourcePalette.neutral[38]}`}; + --lightbox-divider: ${accentColor ?? 'inherit'}; + } + /* The following styles are to reflect the current accentColor behaviour in storybook as well so we maintain consistency */ + [data-color-scheme='dark'] & { + --article-link-text: inherit; + --article-link-text-hover: inherit; + --article-link-border-hover: inherit; + /* This CSS variable only exists in the scope of hosted content and it isn't defined in the paletteDeclarations.ts */ + --accent-colour: ${sourcePalette.neutral[86]}; + --lightbox-divider: ${accentColor ?? 'inherit'}; + } + `; +}; + export const HostedGalleryLayout = (props: WebProps | AppProps) => { const { gallery, renderingTarget, format, serverTime } = props; const { frontendData } = gallery; From dd36c0558c4c1515ecabf915a482934c9b01f363 Mon Sep 17 00:00:00 2001 From: Charlotte Date: Mon, 22 Jun 2026 11:51:08 +0100 Subject: [PATCH 2/9] add storybook variation for hosted content lightbox --- .../src/components/Lightbox.stories.tsx | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/dotcom-rendering/src/components/Lightbox.stories.tsx b/dotcom-rendering/src/components/Lightbox.stories.tsx index fef4d92e983..61c23fea7b0 100644 --- a/dotcom-rendering/src/components/Lightbox.stories.tsx +++ b/dotcom-rendering/src/components/Lightbox.stories.tsx @@ -1,3 +1,4 @@ +import { css } from '@emotion/react'; import { storage } from '@guardian/libs'; import { breakpoints } from '@guardian/source/foundations'; import type { Meta, StoryObj } from '@storybook/react-webpack5'; @@ -265,3 +266,29 @@ export const WithLabs = { ], }, } satisfies Story; + +export const WithHostedAccentColour = { + args: { + format: { + display: ArticleDisplay.Standard, + design: ArticleDesign.HostedGallery, + theme: ArticleSpecial.Labs, + }, + images: [ + { + ...testImage, + title: 'Title', + displayCredit: true, + }, + ], + }, + decorators: (Story) => ( +
+ {Story()} +
+ ), +} satisfies Story; From d840740db08245e054d90e3b368828a7d2bd9e8b Mon Sep 17 00:00:00 2001 From: Charlotte Date: Mon, 22 Jun 2026 12:25:31 +0100 Subject: [PATCH 3/9] duplicate colour overrides function in each hosted content layout component --- .../src/layouts/HostedArticleLayout.tsx | 4 ++- .../src/layouts/HostedVideoLayout.tsx | 27 ++++++++++++++++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/dotcom-rendering/src/layouts/HostedArticleLayout.tsx b/dotcom-rendering/src/layouts/HostedArticleLayout.tsx index b1660fa5473..9b612c57d1c 100644 --- a/dotcom-rendering/src/layouts/HostedArticleLayout.tsx +++ b/dotcom-rendering/src/layouts/HostedArticleLayout.tsx @@ -173,13 +173,14 @@ const sideBorders = css` * @param accentColor - The accentColor to use for the hosted content in light mode. * @returns A CSS string with the overridden palette declarations. */ -export const overridePaletteColours = (accentColor?: string) => { +const overridePaletteColours = (accentColor?: string) => { return css` @media (prefers-color-scheme: light) { --article-link-text: ${accentColor ?? 'inherit'}; --article-link-text-hover: ${accentColor ?? 'inherit'}; --article-link-border-hover: ${accentColor ?? 'inherit'}; --accent-colour: ${accentColor ?? `${sourcePalette.neutral[38]}`}; + --lightbox-divider: ${accentColor ?? 'inherit'}; } /* The following styles are to reflect the current accentColor behaviour in storybook as well so we maintain consistency */ [data-color-scheme='dark'] & { @@ -188,6 +189,7 @@ export const overridePaletteColours = (accentColor?: string) => { --article-link-border-hover: inherit; /* This CSS variable only exists in the scope of hosted content and it isn't defined in the paletteDeclarations.ts */ --accent-colour: ${sourcePalette.neutral[86]}; + --lightbox-divider: ${accentColor ?? 'inherit'}; } `; }; diff --git a/dotcom-rendering/src/layouts/HostedVideoLayout.tsx b/dotcom-rendering/src/layouts/HostedVideoLayout.tsx index 0c414ea6ed2..04ce0377003 100644 --- a/dotcom-rendering/src/layouts/HostedVideoLayout.tsx +++ b/dotcom-rendering/src/layouts/HostedVideoLayout.tsx @@ -26,7 +26,6 @@ import type { Article } from '../types/article'; import type { Block } from '../types/blocks'; import type { FEElement } from '../types/content'; import type { RenderingTarget } from '../types/renderingTarget'; -import { overridePaletteColours } from './HostedArticleLayout'; import { Stuck } from './lib/stickiness'; interface Props { @@ -161,6 +160,32 @@ const sideBorders = css` } `; +/** + * Overrides palette declarations in light mode to use the accent color for the hosted content. + * @param accentColor - The accentColor to use for the hosted content in light mode. + * @returns A CSS string with the overridden palette declarations. + */ +const overridePaletteColours = (accentColor?: string) => { + return css` + @media (prefers-color-scheme: light) { + --article-link-text: ${accentColor ?? 'inherit'}; + --article-link-text-hover: ${accentColor ?? 'inherit'}; + --article-link-border-hover: ${accentColor ?? 'inherit'}; + --accent-colour: ${accentColor ?? `${sourcePalette.neutral[38]}`}; + --lightbox-divider: ${accentColor ?? 'inherit'}; + } + /* The following styles are to reflect the current accentColor behaviour in storybook as well so we maintain consistency */ + [data-color-scheme='dark'] & { + --article-link-text: inherit; + --article-link-text-hover: inherit; + --article-link-border-hover: inherit; + /* This CSS variable only exists in the scope of hosted content and it isn't defined in the paletteDeclarations.ts */ + --accent-colour: ${sourcePalette.neutral[86]}; + --lightbox-divider: ${accentColor ?? 'inherit'}; + } + `; +}; + export const HostedVideoLayout = (props: WebProps | AppProps) => { const { content: { frontendData }, From 5f81d5d54c3f988d813e91e39d0c8deeb921d7a8 Mon Sep 17 00:00:00 2001 From: Charlotte Date: Mon, 22 Jun 2026 12:33:38 +0100 Subject: [PATCH 4/9] extract palette overrides function and use in hosted gallery layout --- .../src/layouts/HostedArticleLayout.tsx | 27 +-------------- .../src/layouts/HostedGalleryLayout.tsx | 34 +++---------------- .../src/layouts/HostedVideoLayout.tsx | 27 +-------------- dotcom-rendering/src/lib/hostedContent.ts | 31 +++++++++++++++++ 4 files changed, 38 insertions(+), 81 deletions(-) create mode 100644 dotcom-rendering/src/lib/hostedContent.ts diff --git a/dotcom-rendering/src/layouts/HostedArticleLayout.tsx b/dotcom-rendering/src/layouts/HostedArticleLayout.tsx index 9b612c57d1c..06a2e8c3889 100644 --- a/dotcom-rendering/src/layouts/HostedArticleLayout.tsx +++ b/dotcom-rendering/src/layouts/HostedArticleLayout.tsx @@ -22,6 +22,7 @@ import { grid } from '../grid'; import type { ArticleFormat } from '../lib/articleFormat'; import { getContributionsServiceUrl } from '../lib/contributions'; import { decideMainMediaCaption } from '../lib/decide-caption'; +import { overridePaletteColours } from '../lib/hostedContent'; import { palette as themePalette } from '../palette'; import type { Article } from '../types/article'; import type { Block } from '../types/blocks'; @@ -168,32 +169,6 @@ const sideBorders = css` } `; -/** - * Overrides palette declarations in light mode to use the accent color for the hosted content. - * @param accentColor - The accentColor to use for the hosted content in light mode. - * @returns A CSS string with the overridden palette declarations. - */ -const overridePaletteColours = (accentColor?: string) => { - return css` - @media (prefers-color-scheme: light) { - --article-link-text: ${accentColor ?? 'inherit'}; - --article-link-text-hover: ${accentColor ?? 'inherit'}; - --article-link-border-hover: ${accentColor ?? 'inherit'}; - --accent-colour: ${accentColor ?? `${sourcePalette.neutral[38]}`}; - --lightbox-divider: ${accentColor ?? 'inherit'}; - } - /* The following styles are to reflect the current accentColor behaviour in storybook as well so we maintain consistency */ - [data-color-scheme='dark'] & { - --article-link-text: inherit; - --article-link-text-hover: inherit; - --article-link-border-hover: inherit; - /* This CSS variable only exists in the scope of hosted content and it isn't defined in the paletteDeclarations.ts */ - --accent-colour: ${sourcePalette.neutral[86]}; - --lightbox-divider: ${accentColor ?? 'inherit'}; - } - `; -}; - export const HostedArticleLayout = (props: WebProps | AppProps) => { const { content: { frontendData }, diff --git a/dotcom-rendering/src/layouts/HostedGalleryLayout.tsx b/dotcom-rendering/src/layouts/HostedGalleryLayout.tsx index 17d493c0afe..dc8064a057d 100644 --- a/dotcom-rendering/src/layouts/HostedGalleryLayout.tsx +++ b/dotcom-rendering/src/layouts/HostedGalleryLayout.tsx @@ -17,6 +17,7 @@ import { ShareButton } from '../components/ShareButton.island'; import { Standfirst } from '../components/Standfirst'; import { grid } from '../grid'; import type { ArticleFormat } from '../lib/articleFormat'; +import { overridePaletteColours } from '../lib/hostedContent'; import { palette } from '../palette'; import type { Gallery } from '../types/article'; import type { RenderingTarget } from '../types/renderingTarget'; @@ -105,32 +106,6 @@ const ctaButtonStyles = css` margin-right: ${space[3]}px; `; -/** - * Overrides palette declarations in light mode to use the accent color for the hosted content. - * @param accentColor - The accentColor to use for the hosted content in light mode. - * @returns A CSS string with the overridden palette declarations. - */ -export const overridePaletteColours = (accentColor?: string) => { - return css` - @media (prefers-color-scheme: light) { - --article-link-text: ${accentColor ?? 'inherit'}; - --article-link-text-hover: ${accentColor ?? 'inherit'}; - --article-link-border-hover: ${accentColor ?? 'inherit'}; - --accent-colour: ${accentColor ?? `${sourcePalette.neutral[38]}`}; - --lightbox-divider: ${accentColor ?? 'inherit'}; - } - /* The following styles are to reflect the current accentColor behaviour in storybook as well so we maintain consistency */ - [data-color-scheme='dark'] & { - --article-link-text: inherit; - --article-link-text-hover: inherit; - --article-link-border-hover: inherit; - /* This CSS variable only exists in the scope of hosted content and it isn't defined in the paletteDeclarations.ts */ - --accent-colour: ${sourcePalette.neutral[86]}; - --lightbox-divider: ${accentColor ?? 'inherit'}; - } - `; -}; - export const HostedGalleryLayout = (props: WebProps | AppProps) => { const { gallery, renderingTarget, format, serverTime } = props; const { frontendData } = gallery; @@ -166,9 +141,10 @@ export const HostedGalleryLayout = (props: WebProps | AppProps) => { ) : null}
{ - return css` - @media (prefers-color-scheme: light) { - --article-link-text: ${accentColor ?? 'inherit'}; - --article-link-text-hover: ${accentColor ?? 'inherit'}; - --article-link-border-hover: ${accentColor ?? 'inherit'}; - --accent-colour: ${accentColor ?? `${sourcePalette.neutral[38]}`}; - --lightbox-divider: ${accentColor ?? 'inherit'}; - } - /* The following styles are to reflect the current accentColor behaviour in storybook as well so we maintain consistency */ - [data-color-scheme='dark'] & { - --article-link-text: inherit; - --article-link-text-hover: inherit; - --article-link-border-hover: inherit; - /* This CSS variable only exists in the scope of hosted content and it isn't defined in the paletteDeclarations.ts */ - --accent-colour: ${sourcePalette.neutral[86]}; - --lightbox-divider: ${accentColor ?? 'inherit'}; - } - `; -}; - export const HostedVideoLayout = (props: WebProps | AppProps) => { const { content: { frontendData }, diff --git a/dotcom-rendering/src/lib/hostedContent.ts b/dotcom-rendering/src/lib/hostedContent.ts new file mode 100644 index 00000000000..ea6fe5c6cb4 --- /dev/null +++ b/dotcom-rendering/src/lib/hostedContent.ts @@ -0,0 +1,31 @@ +import type { SerializedStyles } from '@emotion/react'; +import { css } from '@emotion/react'; +import { palette as sourcePalette } from '@guardian/source/foundations'; + +/** + * Overrides palette declarations in light mode to use the accent color for the hosted content. + * @param accentColor - The accentColor to use for the hosted content in light mode. + * @returns A CSS string with the overridden palette declarations. + */ +export const overridePaletteColours = ( + accentColor?: string, +): SerializedStyles => { + return css` + @media (prefers-color-scheme: light) { + --article-link-text: ${accentColor ?? 'inherit'}; + --article-link-text-hover: ${accentColor ?? 'inherit'}; + --article-link-border-hover: ${accentColor ?? 'inherit'}; + --accent-colour: ${accentColor ?? `${sourcePalette.neutral[38]}`}; + --lightbox-divider: ${accentColor ?? 'inherit'}; + } + /* The following styles are to reflect the current accentColor behaviour in storybook as well so we maintain consistency */ + [data-color-scheme='dark'] & { + --article-link-text: inherit; + --article-link-text-hover: inherit; + --article-link-border-hover: inherit; + /* This CSS variable only exists in the scope of hosted content and it isn't defined in the paletteDeclarations.ts */ + --accent-colour: ${sourcePalette.neutral[86]}; + --lightbox-divider: ${accentColor ?? 'inherit'}; + } + `; +}; From b247eba99267d58aa06b91e86763f92a7c9154fd Mon Sep 17 00:00:00 2001 From: Charlotte Date: Tue, 23 Jun 2026 12:21:09 +0100 Subject: [PATCH 5/9] rework the hosted content overrides by extracting into a helper file and using in the root CSS at the page level --- .../src/components/HostedContentPage.tsx | 11 +++- .../src/layouts/HostedArticleLayout.tsx | 6 +- .../src/layouts/HostedGalleryLayout.tsx | 8 +-- .../src/layouts/HostedVideoLayout.tsx | 6 +- dotcom-rendering/src/lib/hostedContent.ts | 31 --------- .../src/lib/hostedContentStyles.ts | 65 +++++++++++++++++++ dotcom-rendering/src/lib/rootStyles.ts | 22 +++---- 7 files changed, 86 insertions(+), 63 deletions(-) delete mode 100644 dotcom-rendering/src/lib/hostedContent.ts create mode 100644 dotcom-rendering/src/lib/hostedContentStyles.ts diff --git a/dotcom-rendering/src/components/HostedContentPage.tsx b/dotcom-rendering/src/components/HostedContentPage.tsx index 860d2c1cf48..b292590caf6 100644 --- a/dotcom-rendering/src/components/HostedContentPage.tsx +++ b/dotcom-rendering/src/components/HostedContentPage.tsx @@ -75,9 +75,18 @@ export const HostedContentPage = (props: WebProps | AppProps) => { const { darkModeAvailable } = useConfig(); const format = { design, display, theme }; + const { branding } = + frontendData.commercialProperties[frontendData.editionId]; + return ( - + {isWeb && } { ) : null} -
+
{ ) : null} -
+
{ ) : null} -
+
{ - return css` - @media (prefers-color-scheme: light) { - --article-link-text: ${accentColor ?? 'inherit'}; - --article-link-text-hover: ${accentColor ?? 'inherit'}; - --article-link-border-hover: ${accentColor ?? 'inherit'}; - --accent-colour: ${accentColor ?? `${sourcePalette.neutral[38]}`}; - --lightbox-divider: ${accentColor ?? 'inherit'}; - } - /* The following styles are to reflect the current accentColor behaviour in storybook as well so we maintain consistency */ - [data-color-scheme='dark'] & { - --article-link-text: inherit; - --article-link-text-hover: inherit; - --article-link-border-hover: inherit; - /* This CSS variable only exists in the scope of hosted content and it isn't defined in the paletteDeclarations.ts */ - --accent-colour: ${sourcePalette.neutral[86]}; - --lightbox-divider: ${accentColor ?? 'inherit'}; - } - `; -}; diff --git a/dotcom-rendering/src/lib/hostedContentStyles.ts b/dotcom-rendering/src/lib/hostedContentStyles.ts new file mode 100644 index 00000000000..78f3bcd3266 --- /dev/null +++ b/dotcom-rendering/src/lib/hostedContentStyles.ts @@ -0,0 +1,65 @@ +import type { SerializedStyles } from '@emotion/react'; +import { css } from '@emotion/react'; +import { from, palette as sourcePalette } from '@guardian/source/foundations'; + +/** + * Overrides palette declarations in light mode to use the accent color for the hosted content. + * @param accentColor - The accentColor to use for the hosted content in light mode. + * @returns A CSS string with the overridden palette declarations. + */ +const hostedPaletteOverrides = ( + colourScheme: 'light' | 'dark', + accentColor?: string, +): SerializedStyles => { + switch (colourScheme) { + case 'light': + return css` + --article-link-text: ${accentColor ?? 'inherit'}; + --article-link-text-hover: ${accentColor ?? 'inherit'}; + --article-link-border-hover: ${accentColor ?? 'inherit'}; + --lightbox-divider: ${accentColor ?? 'inherit'}; + /* + * This CSS variable only exists in the scope of hosted content + * and it isn't defined in the paletteDeclarations.ts + */ + --accent-colour: ${accentColor ?? + `${sourcePalette.neutral[38]}`}; + `; + case 'dark': + return css` + --article-link-text: inherit; + --article-link-text-hover: inherit; + --article-link-border-hover: inherit; + --lightbox-divider: ${accentColor ?? 'inherit'}; + /* + * This CSS variable only exists in the scope of hosted content + * and it isn't defined in the paletteDeclarations.ts + */ + --accent-colour: ${sourcePalette.neutral[86]}; + `; + } +}; + +export const hostedContentStyleOverrides = ( + darkModeAvailable: boolean, + accentColour?: string, +): SerializedStyles => { + return css` + /* Brute force fix for the fixed hosted header causing odd behaviour on skip to main content */ + ${from.tablet} { + scroll-padding-top: 250px; + } + + ${hostedPaletteOverrides('light', accentColour)} + + ${darkModeAvailable + ? css` + @media (prefers-color-scheme: dark) { + :root:not([data-color-scheme='light']) { + ${hostedPaletteOverrides('dark', accentColour)} + } + } + ` + : ''} + `; +}; diff --git a/dotcom-rendering/src/lib/rootStyles.ts b/dotcom-rendering/src/lib/rootStyles.ts index 9738d5ca834..54c8840a4e6 100644 --- a/dotcom-rendering/src/lib/rootStyles.ts +++ b/dotcom-rendering/src/lib/rootStyles.ts @@ -1,24 +1,12 @@ import { css, type SerializedStyles } from '@emotion/react'; import { focusHalo, - from, palette as sourcePalette, } from '@guardian/source/foundations'; import { paletteDeclarations } from '../paletteDeclarations'; import { rootAdStyles } from './adStyles'; import { type ArticleFormat, isHostedContentDesign } from './articleFormat'; - -const hostedHeaderOffset = (format: ArticleFormat) => { - if (isHostedContentDesign(format.design)) { - return css` - /* Brute force fix for the fixed hosted header causing odd behaviour on skip to main content */ - ${from.tablet} { - scroll-padding-top: 250px; - } - `; - } - return ''; -}; +import { hostedContentStyleOverrides } from './hostedContentStyles'; /** * Global styles for pages: @@ -29,6 +17,8 @@ const hostedHeaderOffset = (format: ArticleFormat) => { export const rootStyles = ( format: ArticleFormat, darkModeAvailable: boolean, + /** For hosted content only */ + hostedAccentColour?: string, ): SerializedStyles => css` :root { /* Light palette is default on all platforms */ @@ -52,8 +42,12 @@ export const rootStyles = ( } ` : ''} - ${hostedHeaderOffset(format)} + + ${isHostedContentDesign(format.design) + ? hostedContentStyleOverrides(darkModeAvailable, hostedAccentColour) + : ''} } + /* Crude but effective mechanism. Specific components may need to improve on this behaviour. */ /* The not(.src...) selector is to work with Source's FocusStyleManager. */ *:focus { From b69dde4e083c2095ba092635e2b10c40dd2cb842 Mon Sep 17 00:00:00 2001 From: Charlotte Date: Tue, 23 Jun 2026 12:27:21 +0100 Subject: [PATCH 6/9] add data-layout attribute to hosted gallery main tag --- dotcom-rendering/src/layouts/HostedGalleryLayout.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dotcom-rendering/src/layouts/HostedGalleryLayout.tsx b/dotcom-rendering/src/layouts/HostedGalleryLayout.tsx index 81665bf5c28..1dc8bde41b3 100644 --- a/dotcom-rendering/src/layouts/HostedGalleryLayout.tsx +++ b/dotcom-rendering/src/layouts/HostedGalleryLayout.tsx @@ -139,7 +139,10 @@ export const HostedGalleryLayout = (props: WebProps | AppProps) => { ) : null} -
+
Date: Mon, 29 Jun 2026 18:45:49 +0100 Subject: [PATCH 7/9] update stories with hosted palette decorator for accent colour overrides --- .../.storybook/decorators/themeDecorator.tsx | 9 +++++ .../HostedContentDisclaimer.stories.tsx | 16 ++++---- .../HostedContentHeader.stories.tsx | 38 ++++++++++--------- .../HostedContentOnwards.stories.tsx | 35 +++++++---------- .../layouts/HostedArticleLayout.stories.tsx | 6 +++ .../layouts/HostedGalleryLayout.stories.tsx | 11 ++++++ .../src/layouts/HostedVideoLayout.stories.tsx | 6 +++ 7 files changed, 73 insertions(+), 48 deletions(-) diff --git a/dotcom-rendering/.storybook/decorators/themeDecorator.tsx b/dotcom-rendering/.storybook/decorators/themeDecorator.tsx index f38de153fb9..afdf594690e 100644 --- a/dotcom-rendering/.storybook/decorators/themeDecorator.tsx +++ b/dotcom-rendering/.storybook/decorators/themeDecorator.tsx @@ -14,6 +14,7 @@ import { import type { CSSProperties } from 'react'; import type { ArticleFormat } from '../../src/lib/articleFormat'; import { storybookPaletteDeclarations as paletteDeclarations } from '../mocks/paletteDeclarations'; +import { hostedContentStyleOverrides } from '../../src/lib/hostedContentStyles'; const darkStoryCss = css` background-color: ${sourcePalette.neutral[0]}; @@ -113,3 +114,11 @@ export const browserThemeDecorator =
); + +export const hostedPaletteDecorator = + (accentColour: string): Decorator => + (Story) => ( +
+ +
+ ); diff --git a/dotcom-rendering/src/components/HostedContentDisclaimer.stories.tsx b/dotcom-rendering/src/components/HostedContentDisclaimer.stories.tsx index 7e8b346a956..2f16330d425 100644 --- a/dotcom-rendering/src/components/HostedContentDisclaimer.stories.tsx +++ b/dotcom-rendering/src/components/HostedContentDisclaimer.stories.tsx @@ -1,13 +1,12 @@ +import { hostedPaletteDecorator } from '../../.storybook/decorators/themeDecorator'; +import preview from '../../.storybook/preview'; import { HostedContentDisclaimer } from './HostedContentDisclaimer'; import { Section } from './Section'; -export default { +const meta = preview.meta({ component: HostedContentDisclaimer, title: 'Components/HostedContentDisclaimer', -}; - -export const Default = () => { - return ( + render: () => (
{ >
- ); -}; + ), + decorators: hostedPaletteDecorator('#d90c1f'), +}); -Default.storyName = 'default'; +export const Default = meta.story({}); diff --git a/dotcom-rendering/src/components/HostedContentHeader.stories.tsx b/dotcom-rendering/src/components/HostedContentHeader.stories.tsx index bb7f49f78d7..696f494da12 100644 --- a/dotcom-rendering/src/components/HostedContentHeader.stories.tsx +++ b/dotcom-rendering/src/components/HostedContentHeader.stories.tsx @@ -1,31 +1,32 @@ import { palette as sourcePalette } from '@guardian/source/foundations'; +import { hostedPaletteDecorator } from '../../.storybook/decorators/themeDecorator'; import preview from '../../.storybook/preview'; import type { Branding } from '../types/branding'; import { HostedContentHeader } from './HostedContentHeader.island'; import type { Props as HostedContentHeaderProps } from './HostedContentHeader.island'; import { Section } from './Section'; +const branding = { + brandingType: { name: 'paid-content' }, + sponsorName: 'We Are Still In', + logo: { + src: 'https://static.theguardian.com/commercial/sponsor/16/Aug/2018/d5e82ba3-297d-473d-8362-c04f519e5fe1-WASI-logo-grey.png', + dimensions: { + width: 1250, + height: 575, + }, + link: 'https://www.wearestillin.com/', + label: 'Paid for by', + }, + aboutThisLink: + 'https://www.theguardian.com/info/2016/jan/25/content-funding', + hostedCampaignColour: '#d90c1f', +} satisfies Branding; + const meta = preview.meta({ component: HostedContentHeader, title: 'Components/HostedContentHeader', - args: { - branding: { - brandingType: { name: 'paid-content' }, - sponsorName: 'We Are Still In', - logo: { - src: 'https://static.theguardian.com/commercial/sponsor/16/Aug/2018/d5e82ba3-297d-473d-8362-c04f519e5fe1-WASI-logo-grey.png', - dimensions: { - width: 1250, - height: 575, - }, - link: 'https://www.wearestillin.com/', - label: 'Paid for by', - }, - aboutThisLink: - 'https://www.theguardian.com/info/2016/jan/25/content-funding', - hostedCampaignColour: '#d90c1f', - } satisfies Branding, - }, + args: { branding }, render: (args: HostedContentHeaderProps) => (
), + decorators: hostedPaletteDecorator(branding.hostedCampaignColour), }); export const Default = meta.story(); diff --git a/dotcom-rendering/src/components/HostedContentOnwards.stories.tsx b/dotcom-rendering/src/components/HostedContentOnwards.stories.tsx index 713ccb47ad5..ab8c4612f45 100644 --- a/dotcom-rendering/src/components/HostedContentOnwards.stories.tsx +++ b/dotcom-rendering/src/components/HostedContentOnwards.stories.tsx @@ -1,29 +1,20 @@ +import { hostedPaletteDecorator } from '../../.storybook/decorators/themeDecorator'; +import preview from '../../.storybook/preview'; import { hostedOnwardsTrails } from '../../fixtures/manual/onwardsTrails'; import { HostedContentOnwards } from './HostedContentOnwards'; -export default { +const meta = preview.meta({ component: HostedContentOnwards, title: 'Components/HostedContentOnwards', -}; + args: { + trails: hostedOnwardsTrails, + brandName: 'TrendAI', + }, + render: (args) => , +}); -export const Default = () => { - return ( - - ); -}; +export const Default = meta.story({}); -Default.storyName = 'default'; - -export const WithAccentColour = () => { - return ( - - ); -}; - -WithAccentColour.storyName = 'with accent colour'; +export const WithAccentColour = meta.story({ + decorators: hostedPaletteDecorator('#d90c1f'), +}); diff --git a/dotcom-rendering/src/layouts/HostedArticleLayout.stories.tsx b/dotcom-rendering/src/layouts/HostedArticleLayout.stories.tsx index 91def812c32..51f91492680 100644 --- a/dotcom-rendering/src/layouts/HostedArticleLayout.stories.tsx +++ b/dotcom-rendering/src/layouts/HostedArticleLayout.stories.tsx @@ -1,3 +1,4 @@ +import { hostedPaletteDecorator } from '../../.storybook/decorators/themeDecorator'; import { allModes } from '../../.storybook/modes'; import preview from '../../.storybook/preview'; import { hostedArticle } from '../../fixtures/manual/hostedArticle'; @@ -21,6 +22,9 @@ const mockOnwardsContentFetch = customMockFetch([ }, ]); +const { hostedCampaignColour = '' } = + hostedArticle.commercialProperties.UK.branding ?? {}; + const meta = preview.meta({ title: 'Layouts/HostedArticle', component: HostedArticleLayout, @@ -35,6 +39,7 @@ const meta = preview.meta({ global.fetch = mockOnwardsContentFetch; return ; }, + decorators: hostedPaletteDecorator(hostedCampaignColour), }); const format = { @@ -57,6 +62,7 @@ export const Apps = meta.story({ chromatic: { modes: { 'light mobileMedium': allModes['light mobileMedium'], + splitVertical: allModes['splitVertical'], }, }, }, diff --git a/dotcom-rendering/src/layouts/HostedGalleryLayout.stories.tsx b/dotcom-rendering/src/layouts/HostedGalleryLayout.stories.tsx index 65de50cda57..b62c15cd467 100644 --- a/dotcom-rendering/src/layouts/HostedGalleryLayout.stories.tsx +++ b/dotcom-rendering/src/layouts/HostedGalleryLayout.stories.tsx @@ -1,3 +1,4 @@ +import { hostedPaletteDecorator } from '../../.storybook/decorators/themeDecorator'; import { allModes } from '../../.storybook/modes'; import preview from '../../.storybook/preview'; import { hostedGallery } from '../../fixtures/manual/hostedGallery'; @@ -21,6 +22,9 @@ const meta = preview.meta({ }, }); +const { hostedCampaignColour = '' } = + hostedGallery.commercialProperties.UK.branding ?? {}; + const format = { theme: ArticleSpecial.Labs, design: ArticleDesign.HostedGallery, @@ -42,6 +46,7 @@ export const Apps = meta.story({ renderingTarget: 'Apps', }, }, + decorators: hostedPaletteDecorator(hostedCampaignColour), }); const webHostedGallery = enhanceArticleType(hostedGallery, 'Web'); @@ -55,6 +60,12 @@ export const Web = meta.story({ renderingTarget: 'Web', }, parameters: { + chromatic: { + modes: { + 'light leftCol': allModes['light leftCol'], + splitVertical: allModes['splitVertical'], + }, + }, config: { renderingTarget: 'Web', }, diff --git a/dotcom-rendering/src/layouts/HostedVideoLayout.stories.tsx b/dotcom-rendering/src/layouts/HostedVideoLayout.stories.tsx index 7d1109abc6a..fb9acb1d170 100644 --- a/dotcom-rendering/src/layouts/HostedVideoLayout.stories.tsx +++ b/dotcom-rendering/src/layouts/HostedVideoLayout.stories.tsx @@ -1,3 +1,4 @@ +import { hostedPaletteDecorator } from '../../.storybook/decorators/themeDecorator'; import { allModes } from '../../.storybook/modes'; import preview from '../../.storybook/preview'; import { hostedVideo } from '../../fixtures/manual/hostedVideo'; @@ -20,6 +21,9 @@ const mockOnwardsContentFetch = customMockFetch([ }, ]); +const { hostedCampaignColour = '' } = + hostedVideo.commercialProperties.UK.branding ?? {}; + const meta = preview.meta({ title: 'Layouts/HostedVideo', component: HostedVideoLayout, @@ -34,6 +38,7 @@ const meta = preview.meta({ global.fetch = mockOnwardsContentFetch; return ; }, + decorators: hostedPaletteDecorator(hostedCampaignColour), }); const format = { @@ -56,6 +61,7 @@ export const Apps = meta.story({ chromatic: { modes: { 'light mobileMedium': allModes['light mobileMedium'], + splitVertical: allModes['splitVertical'], }, }, }, From 500544306a818a0bab4351caf43e6085f7f56116 Mon Sep 17 00:00:00 2001 From: Charlotte Date: Tue, 30 Jun 2026 08:25:27 +0100 Subject: [PATCH 8/9] update chromatic modes for hosted page layouts + refactor hosted article story --- .../layouts/HostedArticleLayout.stories.tsx | 106 +++++++++++------- .../layouts/HostedGalleryLayout.stories.tsx | 11 +- .../src/layouts/HostedVideoLayout.stories.tsx | 3 +- 3 files changed, 69 insertions(+), 51 deletions(-) diff --git a/dotcom-rendering/src/layouts/HostedArticleLayout.stories.tsx b/dotcom-rendering/src/layouts/HostedArticleLayout.stories.tsx index 51f91492680..62cb344ee2f 100644 --- a/dotcom-rendering/src/layouts/HostedArticleLayout.stories.tsx +++ b/dotcom-rendering/src/layouts/HostedArticleLayout.stories.tsx @@ -9,8 +9,8 @@ import { ArticleSpecial, } from '../lib/articleFormat'; import { customMockFetch } from '../lib/mockRESTCalls'; +import type { Article } from '../types/article'; import { enhanceArticleType } from '../types/article'; -import type { Branding } from '../types/branding'; import { HostedArticleLayout } from './HostedArticleLayout'; const mockOnwardsContentFetch = customMockFetch([ @@ -61,8 +61,7 @@ export const Apps = meta.story({ }, chromatic: { modes: { - 'light mobileMedium': allModes['light mobileMedium'], - splitVertical: allModes['splitVertical'], + 'vertical mobileMedium': allModes['vertical mobileMedium'], }, }, }, @@ -83,27 +82,50 @@ export const Web = meta.story({ }, }); -export const WithoutAccentColour = meta.story({ - args: { - content: { - ...webHostedArticle, - frontendData: { - ...webHostedArticle.frontendData, - commercialProperties: { - ...webHostedArticle.frontendData.commercialProperties, - UK: { - ...webHostedArticle.frontendData.commercialProperties - .UK, +const overrideBranding = (article: Article): Article => { + const brandingWithoutAccentColour = { + sponsorName: 'Croatia NTB', + logo: { + src: 'https://static.theguardian.com/commercial/sponsor/27/Mar/2026/2bd1de6c-47ce-4152-9031-66ce0b1bd96f-croatia_pos_140.png', + dimensions: { + width: 140, + height: 90, + }, + link: 'https://croatia.hr/en-gb/why-visit-croatia-in-spring/spring-is-meant-to-be-felt', + label: 'Paid for by', + }, + logoForDarkBackground: { + src: 'https://static.theguardian.com/commercial/sponsor/27/Mar/2026/6f3bc0bd-41ef-4970-9cd7-c75fd1e8a0ce-croatia_pos_140.png', + dimensions: { + width: 140, + height: 90, + }, + link: 'https://croatia.hr/en-gb/why-visit-croatia-in-spring/spring-is-meant-to-be-felt', + label: 'Paid for by', + }, + aboutThisLink: + 'https://www.theguardian.com/info/2016/jan/25/content-funding', + hostedCampaignColour: '', + }; - branding: { - ...webHostedArticle.frontendData - .commercialProperties.UK.branding, - hostedCampaignColour: undefined, - } as Branding, - }, + return { + ...article, + frontendData: { + ...article.frontendData, + commercialProperties: { + ...article.frontendData.commercialProperties, + UK: { + ...article.frontendData.commercialProperties.UK, + branding: brandingWithoutAccentColour, }, }, }, + }; +}; + +export const WithoutAccentColour = meta.story({ + args: { + content: overrideBranding(webHostedArticle), format, renderingTarget: 'Web', }, @@ -112,33 +134,31 @@ export const WithoutAccentColour = meta.story({ renderingTarget: 'Web', }, }, + decorators: hostedPaletteDecorator(''), +}); + +const overrideMainMediaCaption = (article: Article): Article => ({ + ...article, + frontendData: { + ...article.frontendData, + mainMediaElements: [ + ...article.frontendData.mainMediaElements.map((el) => { + if ( + el._type === + 'model.dotcomrendering.pageElements.ImageBlockElement' + ) { + el.data.caption = undefined; + el.data.credit = undefined; + } + return el; + }), + ], + }, }); export const WithoutMainMediaCaption = meta.story({ args: { - content: { - ...webHostedArticle, - frontendData: { - ...webHostedArticle.frontendData, - mainMediaElements: - webHostedArticle.frontendData.mainMediaElements[0] - ?._type === - 'model.dotcomrendering.pageElements.ImageBlockElement' - ? [ - { - ...webHostedArticle.frontendData - .mainMediaElements[0], - data: { - ...webHostedArticle.frontendData - .mainMediaElements[0].data, - caption: undefined, - credit: undefined, - }, - }, - ] - : webHostedArticle.frontendData.mainMediaElements, - }, - }, + content: overrideMainMediaCaption(webHostedArticle), format, renderingTarget: 'Web', }, diff --git a/dotcom-rendering/src/layouts/HostedGalleryLayout.stories.tsx b/dotcom-rendering/src/layouts/HostedGalleryLayout.stories.tsx index b62c15cd467..4fe1d96497c 100644 --- a/dotcom-rendering/src/layouts/HostedGalleryLayout.stories.tsx +++ b/dotcom-rendering/src/layouts/HostedGalleryLayout.stories.tsx @@ -45,6 +45,11 @@ export const Apps = meta.story({ config: { renderingTarget: 'Apps', }, + chromatic: { + modes: { + 'vertical mobileMedium': allModes['vertical mobileMedium'], + }, + }, }, decorators: hostedPaletteDecorator(hostedCampaignColour), }); @@ -60,12 +65,6 @@ export const Web = meta.story({ renderingTarget: 'Web', }, parameters: { - chromatic: { - modes: { - 'light leftCol': allModes['light leftCol'], - splitVertical: allModes['splitVertical'], - }, - }, config: { renderingTarget: 'Web', }, diff --git a/dotcom-rendering/src/layouts/HostedVideoLayout.stories.tsx b/dotcom-rendering/src/layouts/HostedVideoLayout.stories.tsx index fb9acb1d170..b447fc21f82 100644 --- a/dotcom-rendering/src/layouts/HostedVideoLayout.stories.tsx +++ b/dotcom-rendering/src/layouts/HostedVideoLayout.stories.tsx @@ -60,8 +60,7 @@ export const Apps = meta.story({ }, chromatic: { modes: { - 'light mobileMedium': allModes['light mobileMedium'], - splitVertical: allModes['splitVertical'], + 'vertical mobileMedium': allModes['vertical mobileMedium'], }, }, }, From 4dc9e157ca24b3c8eabbaf841917e2c505ede5a4 Mon Sep 17 00:00:00 2001 From: Charlotte Date: Tue, 30 Jun 2026 15:45:49 +0100 Subject: [PATCH 9/9] fix theming in storybook for hosted content pages --- .../.storybook/decorators/themeDecorator.tsx | 28 +++++++++++++++++-- .../layouts/HostedArticleLayout.stories.tsx | 11 ++++++-- .../layouts/HostedGalleryLayout.stories.tsx | 1 + .../src/layouts/HostedVideoLayout.stories.tsx | 1 + .../src/lib/hostedContentStyles.ts | 6 ++-- 5 files changed, 38 insertions(+), 9 deletions(-) diff --git a/dotcom-rendering/.storybook/decorators/themeDecorator.tsx b/dotcom-rendering/.storybook/decorators/themeDecorator.tsx index afdf594690e..ed68aedbf86 100644 --- a/dotcom-rendering/.storybook/decorators/themeDecorator.tsx +++ b/dotcom-rendering/.storybook/decorators/themeDecorator.tsx @@ -14,7 +14,7 @@ import { import type { CSSProperties } from 'react'; import type { ArticleFormat } from '../../src/lib/articleFormat'; import { storybookPaletteDeclarations as paletteDeclarations } from '../mocks/paletteDeclarations'; -import { hostedContentStyleOverrides } from '../../src/lib/hostedContentStyles'; +import { hostedPaletteOverrides } from '../../src/lib/hostedContentStyles'; const darkStoryCss = css` background-color: ${sourcePalette.neutral[0]}; @@ -115,10 +115,32 @@ export const browserThemeDecorator = ); +/** + * Colour scheme decorator specifically for hosted content pages, + * where the accent colour from the branding overrides some palette colours + */ export const hostedPaletteDecorator = (accentColour: string): Decorator => - (Story) => ( -
+ (Story, context) => ( +
); diff --git a/dotcom-rendering/src/layouts/HostedArticleLayout.stories.tsx b/dotcom-rendering/src/layouts/HostedArticleLayout.stories.tsx index 62cb344ee2f..639bdb67da8 100644 --- a/dotcom-rendering/src/layouts/HostedArticleLayout.stories.tsx +++ b/dotcom-rendering/src/layouts/HostedArticleLayout.stories.tsx @@ -29,6 +29,7 @@ const meta = preview.meta({ title: 'Layouts/HostedArticle', component: HostedArticleLayout, parameters: { + config: { darkModeAvailable: true }, chromatic: { modes: { 'light leftCol': allModes['light leftCol'], @@ -147,8 +148,14 @@ const overrideMainMediaCaption = (article: Article): Article => ({ el._type === 'model.dotcomrendering.pageElements.ImageBlockElement' ) { - el.data.caption = undefined; - el.data.credit = undefined; + return { + ...el, + data: { + caption: '', + credit: '', + alt: '', + }, + }; } return el; }), diff --git a/dotcom-rendering/src/layouts/HostedGalleryLayout.stories.tsx b/dotcom-rendering/src/layouts/HostedGalleryLayout.stories.tsx index 4fe1d96497c..09ae8e498d3 100644 --- a/dotcom-rendering/src/layouts/HostedGalleryLayout.stories.tsx +++ b/dotcom-rendering/src/layouts/HostedGalleryLayout.stories.tsx @@ -14,6 +14,7 @@ const meta = preview.meta({ title: 'Layouts/HostedGallery', component: HostedGalleryLayout, parameters: { + config: { darkModeAvailable: true }, chromatic: { modes: { 'light leftCol': allModes['light leftCol'], diff --git a/dotcom-rendering/src/layouts/HostedVideoLayout.stories.tsx b/dotcom-rendering/src/layouts/HostedVideoLayout.stories.tsx index b447fc21f82..d09ab69021d 100644 --- a/dotcom-rendering/src/layouts/HostedVideoLayout.stories.tsx +++ b/dotcom-rendering/src/layouts/HostedVideoLayout.stories.tsx @@ -28,6 +28,7 @@ const meta = preview.meta({ title: 'Layouts/HostedVideo', component: HostedVideoLayout, parameters: { + config: { darkModeAvailable: true }, chromatic: { modes: { 'light leftCol': allModes['light leftCol'], diff --git a/dotcom-rendering/src/lib/hostedContentStyles.ts b/dotcom-rendering/src/lib/hostedContentStyles.ts index 78f3bcd3266..f0dadaa63df 100644 --- a/dotcom-rendering/src/lib/hostedContentStyles.ts +++ b/dotcom-rendering/src/lib/hostedContentStyles.ts @@ -7,7 +7,7 @@ import { from, palette as sourcePalette } from '@guardian/source/foundations'; * @param accentColor - The accentColor to use for the hosted content in light mode. * @returns A CSS string with the overridden palette declarations. */ -const hostedPaletteOverrides = ( +export const hostedPaletteOverrides = ( colourScheme: 'light' | 'dark', accentColor?: string, ): SerializedStyles => { @@ -55,9 +55,7 @@ export const hostedContentStyleOverrides = ( ${darkModeAvailable ? css` @media (prefers-color-scheme: dark) { - :root:not([data-color-scheme='light']) { - ${hostedPaletteOverrides('dark', accentColour)} - } + ${hostedPaletteOverrides('dark', accentColour)} } ` : ''}