diff --git a/dotcom-rendering/src/components/Card/Card.tsx b/dotcom-rendering/src/components/Card/Card.tsx
index 98c585fd5ae..27df29641ea 100644
--- a/dotcom-rendering/src/components/Card/Card.tsx
+++ b/dotcom-rendering/src/components/Card/Card.tsx
@@ -33,7 +33,7 @@ import type {
DCRSupportingContent,
} from '../../types/front';
import type { MainMedia } from '../../types/mainMedia';
-import type { OnwardsSource } from '../../types/onwards';
+import type { OnwardContainerType, OnwardsSource } from '../../types/onwards';
import { Avatar } from '../Avatar';
import { CardCommentCount } from '../CardCommentCount.importable';
import { CardHeadline, type ResponsiveFontSize } from '../CardHeadline';
@@ -124,7 +124,7 @@ export type Props = {
supportingContentPosition?: Position;
snapData?: DCRSnapType;
containerPalette?: DCRContainerPalette;
- containerType?: DCRContainerType;
+ containerType?: DCRContainerType | OnwardContainerType;
showAge?: boolean;
discussionApiUrl: string;
discussionId?: string;
@@ -150,6 +150,8 @@ export type Props = {
uniqueId?: string;
/** The Splash card in a flexible container gets a different visual treatment to other cards */
isFlexSplash?: boolean;
+ /** The Splash card in an onward container gets a different visual treatment to other cards */
+ isOnwardSplash?: boolean;
showTopBarDesktop?: boolean;
showTopBarMobile?: boolean;
trailTextSize?: TrailTextSize;
@@ -393,6 +395,7 @@ export const Card = ({
index = 0,
uniqueId = '',
isFlexSplash,
+ isOnwardSplash,
showTopBarDesktop = true,
showTopBarMobile = true,
trailTextSize,
@@ -585,6 +588,8 @@ export const Card = ({
containerType === 'flexible/special' ||
containerType === 'flexible/general';
+ const isOnwardContainer = containerType === 'more-galleries';
+
const isSmallCard =
containerType === 'scrollable/small' ||
containerType === 'scrollable/medium';
@@ -603,18 +608,26 @@ export const Card = ({
const hideTrailTextUntil = () => {
if (isFlexibleContainer) {
+ return 'tablet';
+ }
+ if (isOnwardSplash) {
return undefined;
- } else if (
+ }
+ if (
mediaSize === 'large' &&
mediaPositionOnDesktop === 'right' &&
media?.type !== 'avatar'
) {
return 'desktop';
- } else {
- return 'tablet';
}
+
+ return 'tablet';
};
+ const shouldShowTrailText = isOnwardContainer
+ ? media?.type !== 'podcast' && isOnwardSplash
+ : media?.type !== 'podcast';
+
/**
* Determines the gap of between card components based on card properties
* Order matters here as the logic is based on the card properties
@@ -1191,7 +1204,7 @@ export const Card = ({
)}
- {!!trailText && media?.type !== 'podcast' && (
+ {!!trailText && shouldShowTrailText && (
)}
diff --git a/dotcom-rendering/src/components/MoreGalleries.stories.tsx b/dotcom-rendering/src/components/MoreGalleries.stories.tsx
new file mode 100644
index 00000000000..1eff4d2774f
--- /dev/null
+++ b/dotcom-rendering/src/components/MoreGalleries.stories.tsx
@@ -0,0 +1,206 @@
+import type { Meta, StoryObj } from '@storybook/react';
+import { ArticleDesign, ArticleDisplay, Pillar } from '../lib/articleFormat';
+import { getDataLinkNameCard } from '../lib/getDataLinkName';
+import { MoreGalleries as MoreGalleriesComponent } from './MoreGalleries';
+
+const meta = {
+ title: 'Components/MoreGalleries',
+ component: MoreGalleriesComponent,
+} satisfies Meta;
+
+export default meta;
+
+type Story = StoryObj;
+
+export const MoreGalleries = {
+ args: {
+ absoluteServerTimes: false,
+ discussionApiUrl: 'https://discussion.theguardian.com/discussion-api',
+ headingLink: 'https://www.theguardian.com/inpictures/all',
+ trails: [
+ {
+ url: 'https://www.theguardian.com/environment/gallery/2025/aug/22/week-in-wildlife-a-clumsy-fox-swinging-orangutang-and-rescued-jaguarundi-cub',
+ linkText:
+ 'Week in wildlife: a clumsy fox, a swinging orangutan and a rescued jaguarundi cub',
+ showByline: false,
+ byline: 'Pejman Faratin',
+ image: {
+ src: 'https://media.guim.co.uk/a81e974ffee6c8c88fa280c2d02eaf5dc2af863e/151_292_1020_816/master/1020.jpg',
+ altText: '',
+ },
+ format: {
+ theme: Pillar.News,
+ design: ArticleDesign.Gallery,
+ display: ArticleDisplay.Standard,
+ },
+ webPublicationDate: '2025-08-22T06:00:25.000Z',
+ headline:
+ 'Week in wildlife: a clumsy fox, a swinging orangutan and a rescued jaguarundi cub',
+ shortUrl: 'https://www.theguardian.com/p/x32n89',
+ discussion: {
+ isCommentable: false,
+ isClosedForComments: true,
+ discussionId: '/p/x32n89',
+ },
+ dataLinkName: getDataLinkNameCard(
+ {
+ theme: Pillar.News,
+ design: ArticleDesign.Gallery,
+ display: ArticleDisplay.Standard,
+ },
+ '0',
+ 0,
+ ),
+ trailText:
+ 'Guinness World Records is looking back at the extraordinary feats achieved since its inception - as well as unveiling 70 whacky and unclaimed records ',
+ kickerText: 'Politics',
+ mainMedia: { type: 'Gallery', count: '6' },
+ },
+ {
+ url: 'https://www.theguardian.com/money/gallery/2025/aug/22/characterful-cottages-for-sale-in-england-in-pictures',
+ linkText:
+ 'Characterful cottages for sale in England – in pictures',
+ showByline: false,
+ byline: 'Anna White',
+ image: {
+ src: 'https://media.guim.co.uk/58cd9356e6d68e8efa6028162bb959f9798307d5/515_0_5000_4000/master/5000.jpg',
+ altText: '',
+ },
+ format: {
+ design: ArticleDesign.Gallery,
+ theme: Pillar.Lifestyle,
+ display: ArticleDisplay.Standard,
+ },
+ webPublicationDate: '2025-08-22T06:00:24.000Z',
+ headline:
+ 'Characterful cottages for sale in England – in pictures',
+ shortUrl: 'https://www.theguardian.com/p/x32gqj',
+ discussion: {
+ isCommentable: false,
+ isClosedForComments: true,
+ discussionId: '/p/x32gqj',
+ },
+ dataLinkName: getDataLinkNameCard(
+ {
+ design: ArticleDesign.Gallery,
+ theme: Pillar.Lifestyle,
+ display: ArticleDisplay.Standard,
+ },
+ '0',
+ 1,
+ ),
+ trailText:
+ 'Picked from a record 60,636 entries, the first images from the Natural History Museum’s wildlife photographer of the year competition have been released. The photographs, which range from a lion facing down a cobra to magnified mould spores, show the diversity, beauty and complexity of the natural world and humanity’s relationship with it',
+ mainMedia: { type: 'Gallery', count: '6' },
+ },
+ {
+ url: 'https://www.theguardian.com/news/gallery/2025/aug/22/sunsets-aid-parachutes-and-giant-pandas-photos-of-the-day-friday',
+ linkText:
+ 'Sunsets, aid parachutes and giant pandas: photos of the day – Friday ',
+ showByline: false,
+ byline: 'Eithne Staunton',
+ image: {
+ src: 'https://media.guim.co.uk/4ce0b080206fe9b65b976c1acf219d81072cc814/0_0_2113_1690/master/2113.png',
+ altText: '',
+ },
+ format: {
+ design: ArticleDesign.Gallery,
+ theme: Pillar.News,
+ display: ArticleDisplay.Standard,
+ },
+ webPublicationDate: '2025-08-22T12:49:42.000Z',
+ headline:
+ 'Sunsets, aid parachutes and giant pandas: photos of the day – Friday ',
+ shortUrl: 'https://www.theguardian.com/p/x3359z',
+ discussion: {
+ isCommentable: false,
+ isClosedForComments: true,
+ discussionId: '/p/x3359z',
+ },
+ dataLinkName: getDataLinkNameCard(
+ {
+ design: ArticleDesign.Gallery,
+ theme: Pillar.News,
+ display: ArticleDisplay.Standard,
+ },
+ '0',
+ 2,
+ ),
+ trailText:
+ 'From the mock-Tudor fad of the 1920s to drivers refuelling on a roundabout, each era produces its own distinctive petrol stations – as photographer Philip Butler discovered',
+ mainMedia: { type: 'Gallery', count: '6' },
+ },
+ {
+ url: 'https://www.theguardian.com/fashion/gallery/2025/aug/22/what-to-wear-to-notting-hill-carnival',
+ linkText: 'On parade: what to wear to Notting Hill carnival',
+ showByline: false,
+ byline: 'Melanie Wilkinson',
+ image: {
+ src: 'https://media.guim.co.uk/49a9656cd10c4f64f8bdd54380afb915c7a3648b/207_0_1500_1200/master/1500.jpg',
+ altText: '',
+ },
+ format: {
+ design: ArticleDesign.Gallery,
+ theme: Pillar.Lifestyle,
+ display: ArticleDisplay.Standard,
+ },
+ webPublicationDate: '2025-08-22T05:00:23.000Z',
+ headline: 'On parade: what to wear to Notting Hill carnival',
+ shortUrl: 'https://www.theguardian.com/p/x32mte',
+ discussion: {
+ isCommentable: false,
+ isClosedForComments: true,
+ discussionId: '/p/x32mte',
+ },
+ dataLinkName: getDataLinkNameCard(
+ {
+ design: ArticleDesign.Gallery,
+ theme: Pillar.Lifestyle,
+ display: ArticleDisplay.Standard,
+ },
+ '0',
+ 1,
+ ),
+ trailText:
+ 'The Guardian’s picture editors select photographs from around the world',
+ mainMedia: { type: 'Gallery', count: '6' },
+ },
+ {
+ url: 'https://www.theguardian.com/artanddesign/gallery/2025/aug/21/psychedelic-rock-glass-mountain-michael-lundgren',
+ linkText:
+ 'Psychedelic rock! Formations that mess with your mind – in pictures ',
+ showByline: false,
+ image: {
+ src: 'https://media.guim.co.uk/2810af61b2d2d2d5f71ec01e56e6555e0a6d4635/55_0_2813_2250/master/2813.jpg',
+ altText: '',
+ },
+ format: {
+ design: ArticleDesign.Gallery,
+ theme: Pillar.Culture,
+ display: ArticleDisplay.Standard,
+ },
+ webPublicationDate: '2025-08-21T06:01:01.000Z',
+ headline:
+ 'Psychedelic rock! Formations that mess with your mind – in pictures ',
+ shortUrl: 'https://www.theguardian.com/p/x2p663',
+ discussion: {
+ isCommentable: false,
+ isClosedForComments: true,
+ discussionId: '/p/x2p663',
+ },
+ dataLinkName: getDataLinkNameCard(
+ {
+ design: ArticleDesign.Gallery,
+ theme: Pillar.Culture,
+ display: ArticleDisplay.Standard,
+ },
+ '0',
+ 1,
+ ),
+ trailText:
+ 'Politicians and their partners put on their best show at this year’s Midwinter Ball, an annual dinner hosted by the Federal Parliamentary Press Gallery in Canberra',
+ mainMedia: { type: 'Gallery', count: '6' },
+ },
+ ],
+ },
+} satisfies Story;
diff --git a/dotcom-rendering/src/components/MoreGalleries.tsx b/dotcom-rendering/src/components/MoreGalleries.tsx
new file mode 100644
index 00000000000..55e721018b0
--- /dev/null
+++ b/dotcom-rendering/src/components/MoreGalleries.tsx
@@ -0,0 +1,264 @@
+import { css } from '@emotion/react';
+import {
+ from,
+ headlineBold24,
+ headlineBold28,
+ space,
+ until,
+} from '@guardian/source/foundations';
+import { StraightLines } from '@guardian/source-development-kitchen/react-components';
+import { formatAttrString } from '../lib/formatAttrString';
+import { palette } from '../palette';
+import { type OnwardsSource } from '../types/onwards';
+import { type TrailType } from '../types/trails';
+import { Card } from './Card/Card';
+import type { Props as CardProps } from './Card/Card';
+import { Hide } from './Hide';
+import { LeftColumn } from './LeftColumn';
+import { Section } from './Section';
+
+type Props = {
+ absoluteServerTimes: boolean;
+ trails: TrailType[];
+ discussionApiUrl: string;
+ headingLink?: string;
+};
+
+const wrapperStyle = css`
+ display: flex;
+ justify-content: space-between;
+ overflow: hidden;
+ ${from.desktop} {
+ padding-right: ${space[10]}px;
+ }
+`;
+
+const containerStyles = css`
+ display: flex;
+ flex-direction: column;
+ position: relative;
+
+ margin-top: ${space[2]}px;
+ padding-bottom: ${space[6]}px;
+
+ margin-left: 0px;
+ margin-right: 0px;
+
+ border-bottom: 1px solid ${palette('--onward-content-border')};
+
+ ${from.leftCol} {
+ margin-left: 10px;
+ margin-right: 100px;
+ }
+`;
+
+const standardCardStyles = css`
+ flex: 1;
+
+ position: relative;
+ display: flex;
+ padding: ${space[2]}px;
+ background-color: ${palette('--onward-card-background')};
+
+ :not(:first-child)::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: -10px; /* shift into the gap */
+ width: 1px;
+ background: ${palette('--onward-content-border')};
+ }
+`;
+
+const standardCardsListStyles = css`
+ width: 100%;
+ display: flex;
+ flex-direction: row;
+ gap: 20px;
+
+ ${from.tablet} {
+ padding-top: ${space[2]}px;
+ }
+
+ ${until.tablet} {
+ flex-direction: column;
+ width: 100%;
+ }
+`;
+
+const headerStyles = css`
+ color: ${palette('--carousel-text')};
+ ${headlineBold24};
+ padding-bottom: ${space[3]}px;
+ padding-top: ${space[1]}px;
+ margin-left: 0;
+
+ ${from.tablet} {
+ ${headlineBold28};
+ }
+`;
+
+const headerStylesWithUrl = css`
+ :hover {
+ text-decoration: underline;
+ }
+`;
+
+const titleStyle = css`
+ color: ${palette('--onward-text')};
+ display: inline-block;
+`;
+
+const getDefaultCardProps = (
+ trail: TrailType,
+ absoluteServerTimes: boolean,
+ discussionApiUrl: string,
+) => {
+ const defaultProps: CardProps = {
+ linkTo: trail.url,
+ format: trail.format,
+ headlineText: trail.headline,
+ byline: trail.byline,
+ showByline: trail.showByline,
+ showQuotedHeadline: trail.showQuotedHeadline,
+ webPublicationDate: trail.webPublicationDate,
+ kickerText: trail.kickerText,
+ showPulsingDot: false,
+ showClock: false,
+ image: trail.image,
+ isCrossword: trail.isCrossword,
+ starRating: trail.starRating,
+ dataLinkName: trail.dataLinkName,
+ snapData: trail.snapData,
+ discussionApiUrl,
+ discussionId: trail.discussionId,
+ avatarUrl: trail.avatarUrl,
+ mainMedia: trail.mainMedia,
+ isExternalLink: false,
+ branding: trail.branding,
+ absoluteServerTimes,
+ imageLoading: 'lazy',
+ trailText: trail.trailText,
+ showAge: false,
+ containerType: 'more-galleries',
+ showTopBarDesktop: false,
+ showTopBarMobile: false,
+ aspectRatio: '5:4',
+ };
+ return defaultProps;
+};
+
+export const MoreGalleries = (props: Props) => {
+ const [firstTrail, ...standardCards] = props.trails;
+ if (!firstTrail) return null;
+
+ const heading = 'More galleries';
+ const onwardsSource: OnwardsSource = 'more-galleries';
+
+ const defaultProps = getDefaultCardProps(
+ firstTrail,
+ props.absoluteServerTimes,
+ props.discussionApiUrl,
+ );
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {standardCards.map((trail) => (
+ -
+
+
+ ))}
+
+
+
+
+ );
+};
+
+const MoreGalleriesSplashCard = ({
+ defaultProps,
+}: {
+ defaultProps: CardProps;
+}) => {
+ const cardProps: Partial = {
+ headlineSizes: {
+ desktop: 'medium',
+ tablet: 'medium',
+ mobile: 'medium',
+ },
+ mediaPositionOnDesktop: 'right',
+ mediaPositionOnMobile: 'top',
+ mediaSize: 'medium',
+ isOnwardSplash: true,
+ };
+ return (
+
+
+
+ );
+};
+
+const Title = ({ title, url }: { title: string; url?: string }) =>
+ url ? (
+
+
+ {title}
+
+
+ ) : (
+
+ {title}
+
+ );
diff --git a/dotcom-rendering/src/components/YoutubeAtom/YoutubeAtomFeatureCardOverlay.tsx b/dotcom-rendering/src/components/YoutubeAtom/YoutubeAtomFeatureCardOverlay.tsx
index 8e1896001b9..451ffbeaf48 100644
--- a/dotcom-rendering/src/components/YoutubeAtom/YoutubeAtomFeatureCardOverlay.tsx
+++ b/dotcom-rendering/src/components/YoutubeAtom/YoutubeAtomFeatureCardOverlay.tsx
@@ -252,6 +252,7 @@ export const YoutubeAtomFeatureCardOverlay = ({
)}
trailTextSize="regular"
padBottom={false}
+ hideUntil="tablet"
/>
)}
diff --git a/dotcom-rendering/src/paletteDeclarations.ts b/dotcom-rendering/src/paletteDeclarations.ts
index e6e71c14f8a..1bd6cc0e4d0 100644
--- a/dotcom-rendering/src/paletteDeclarations.ts
+++ b/dotcom-rendering/src/paletteDeclarations.ts
@@ -7427,10 +7427,22 @@ const paletteColours = {
light: numberedListTitleLight,
dark: numberedListTitleDark,
},
+ '--onward-background': {
+ light: () => sourcePalette.neutral[100],
+ dark: () => sourcePalette.neutral[10],
+ },
+ '--onward-card-background': {
+ light: () => sourcePalette.neutral[97],
+ dark: () => sourcePalette.neutral[20],
+ },
'--onward-content-border': {
light: onwardContentBorderLight,
dark: () => sourcePalette.neutral[20],
},
+ '--onward-text': {
+ light: () => sourcePalette.neutral[7],
+ dark: () => sourcePalette.neutral[86],
+ },
'--pagination-text': {
light: paginationTextLight,
dark: paginationTextDark,
diff --git a/dotcom-rendering/src/types/onwards.ts b/dotcom-rendering/src/types/onwards.ts
index 948ca173272..5d0ee426cca 100644
--- a/dotcom-rendering/src/types/onwards.ts
+++ b/dotcom-rendering/src/types/onwards.ts
@@ -24,3 +24,5 @@ export type OnwardsSource =
| 'curated-content'
| 'newsletters-page'
| 'unknown-source'; // We should never see this in the analytics data!
+
+export type OnwardContainerType = 'more-galleries';