Skip to content

Conversation

@Georges-GNM
Copy link
Contributor

@Georges-GNM Georges-GNM commented Dec 15, 2025

What does this change?

Part of the AI tag pages work. This adds the content to the tag pages model, enabling it to be passed through from the work in this frontend PR, implements the figma designs (available here although there’s also been some subsequent decisions not updated in the figma), and includes new storybook stories for testing.

Worth flagging at the outset that this is all behind a 0% experiment in frontend - if not in the test, frontend doesn't retrieve the needed content (just returning None). In January, we plan to start a 50% test on a limited number of pages; after which we'll determine if we want to keep the feature and deploy it on a wider number of tags.

It looks like a big PR, but it's meant to comprehensively deliver the feature (with the associated model, data transformation and rendering changes, which has also meant splitting this up is arguably not ideal). A significant portion of the diff comes from code which is either very similar to an existing component, or from schema changes/fixtures/storybook setup.

Chromatic tests only flagged one false positive for an existing component (change of the classname), so I'm fairly confident this doesn't affect existing styling.




What does this actually change?

We add the content type to the tag page model, based on the structure provided by the tool and correspondingly sent by frontend.

As a quick overview, we have:
-> content
---> array of storylines
----> in each storyline, an array of categories (e.g. key stories, opinions, deep reads)
-----> in each category, an array of articles, with the minimum data required to render them by DCR (headline, url, publication date, maybe byline, image data and if relevant, main media data).

The content gets rendered in the tag page layout just below the first container (thereby always showing the most recent articles on a given tag at the top of the page). The designs are inspired by the front page redesign, and notably the flexible general container, so the implementation has taken this into account and tried to reuse existing logic where possible.

We add two new components, StorylinesSection and StorylinesSectionContent.importable. The former provides the general structure of the section (it's very similar to the FrontSection component, particularly in terms of the style and grid setup, but slightly adapted and removing unnecessary logic).

The latter calls an enhanceAITagPageContent function to transform the data sent by frontend, turning the very basic article information we receive (headline, url, image/multimedia data) into the DCRGroupedTrails and DCRFrontCard shape. Based on the categories these articles are a part of, we can be deterministic in terms of how values need to be defined for a DCRFrontCard (e.g. for the format). Most of these front cards end up being part of the standard group in a trail, but the key stories category is a bit different: the image for that gets taken from the first (latest) article in the category, and each of the headlines get used as the "supporting content" on the left of the image.

Once we've got the data in shape, each of the categories in a storyline gets rendered as a flexible general container. These are individual containers mainly so we can add the title of the category before them.

For the most part this just uses the pre-existing logic, but some variations needed to be tweaked (e.g. the rendering of the key stories category in particular), and so we pass a boolean isStorylines through FlexibleGeneral and Card to enable this (and add a SupportingKeyStoriesContent component, similar to the supporting content component, but with enough notable differences). CardAge and feature card components (FeatureCard and FeatureCardAge) also get touched slightly, to ensure we display the age correctly (as the cards are displayed on an “archive” page, it feels important to indicate when the article was published).

The only piece of AI generated text, the titles of the storylines, get rendered at the top of the section. These are tabs that can be clicked on to switch stories, so we need this to be an island to enable the necessary javascript.

Few more points to note:

  • The storyline section has some text at the top left (under the "Storylines" container title), with a link that should point to further information - we've not settled on exactly where this is going yet, but I'd prefer to keep this text as is (and update the link in a subsequent PR) to keep a view of it as part of the design. In order to provide a valid link, I've pointed to the AI guidelines for now.
  • The bottom left of the storyline section (the standard "treats" position) makes use of the like/dislike feedback footer usually found in atoms. This is so we can try to get a bit more qualitative feedback from readers in the test. I've added the relevant component type to the thrift model and bumped the version of tracker.js to enable this (following this PR in Ophan)
  • The StorylinesSection and SupportingKeyStoriesContent components were copied from FrontSection and SupportingContent and then simplified for this situation. I removed as many unused props as possible, but kept some intentionally as we may want to use them in the future; happy to strip these out though if we’d prefer to keep things as concise as possible. (Notably flagging the toggleable and alignment props, respectively, despite the section not currently being toggleable and the supporting content alignment always being defined as vertical)

For future reference

It's possible we'll want to remove this code altogether, which should a straightforward process; mostly this should be a question of deleting the new components/files and following the trail of the isStorylines prop and removing references/uses, and I've added a couple of comments prefaced with AIStorylines in front of the few other lines we'd want to remove.

Screenshots

Category Desktop Mobile
Full page fullPage-desktop fullPage-mobile
Key stories keyStories-desktop keyStories-mobile
Opinions/Explainers opinionsAndExplainers-desktop opinionsAndExplainers-mobile
Deep Reads deepReads-desktop deepReads-mobile
Multimedia multimedia-desktop multimedia-mobile
Profiles profilesAndInterviews-desktop profilesAndInterviews-mobile

@Georges-GNM Georges-GNM added run_chromatic Runs chromatic when label is applied feature Departmental tracking: work on a new feature and removed run_chromatic Runs chromatic when label is applied labels Dec 15, 2025
@Georges-GNM Georges-GNM force-pushed the gl/sc-tag-page-mockup branch from 27791e1 to dd106f8 Compare December 15, 2025 11:10
@github-actions
Copy link

github-actions bot commented Dec 15, 2025

@github-actions github-actions bot removed the run_chromatic Runs chromatic when label is applied label Dec 15, 2025
@github-actions
Copy link

github-actions bot commented Dec 15, 2025

@github-actions github-actions bot removed the run_chromatic Runs chromatic when label is applied label Dec 30, 2025
@Georges-GNM Georges-GNM marked this pull request as ready for review December 30, 2025 15:41
@Georges-GNM Georges-GNM requested a review from a team as a code owner December 30, 2025 15:41
@github-actions
Copy link

Hello 👋! When you're ready to run Chromatic, please apply the run_chromatic label to this PR.

You will need to reapply the label each time you want to run Chromatic.

Click here to see the Chromatic project.

@Georges-GNM Georges-GNM added the run_chromatic Runs chromatic when label is applied label Jan 6, 2026
@github-actions github-actions bot removed the run_chromatic Runs chromatic when label is applied label Jan 6, 2026
@domlander
Copy link
Contributor

This isn't a blocker, but I think hover state could be improved on the splash container:

  • No hover state on the tabs.
  • Hovering over the card but not the headlines uses the image overlay.
Screen.Recording.2026-01-07.at.17.00.34.mov

isTagPage: boolean;
showClock?: boolean;
colour?: string;
storylinesStyle?: boolean;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if isStorylines might be a bit clearer, similar to isTagPage above. Will leave up to you.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, isStorylines looks a bit more in keeping with other props on card.tsx, will update that.

alignment="vertical"
containerPalette={containerPalette}
fillBackgroundOnMobile={isFlexSplash}
storylinesStyle={storylinesStyle}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the same?

storylinesStyle={true}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Effectively yep, will update that so it's more clear.

mediaSize: avatarUrl ? 'large' : 'xlarge',
supportingContentAlignment:
supportingContentLength >= 4 ? 'horizontal' : 'vertical',
supportingContentAlignment: storylinesStyle
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would recommend extracting this. Nested ternary's can be harder to read.

pillar?: string;
};

const setSelectedStorylineColour = (pillar?: string) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be handled in paletteDeclarations by creating a new css variable instead? pillarPalette() might be helpful.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'd leave that as a future improvement if we keep the feature. Gonna add a comment though.

padding-left: 10px; /* aligns with the headlines of the stories below */
`;

const setCategoryColour = (pillar?: string) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<>
<StorylineSection
title="Storylines"
containerPalette="LongRunningAltPalette"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this comment be added to the code? Just so we don't lose it/forget about it.

>
{activeStorylineId === storyline.id ? (
<>
<span css={[numberStyles]}>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<span css={[numberStyles]}>
<span css={numberStyles}>

<a href="https://www.theguardian.com/help/insideguardian/2023/jun/16/the-guardians-approach-to-generative-ai">
here
</a>
.{' '}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the trailing {' '} intentional?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAICT yep to keep the space outside the anchor tag.

/>
)}
{insertStorylinesSection && (
<Island priority="critical">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is displayed to users on slow connections? Do they see the content but with the tabbing disabled?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hadn't thought about that! Setting my browser to a 3G connection, it takes a few seconds (~10-15) for the tabbing to load but it eventually works.

sourcePalette.neutral[97];
const cardMediaBackgroundLight: PaletteFunction = (format) => {
switch (format.theme) {
case ArticleSpecial.SpecialReportAlt:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this affect the SpecialReportAlt theme outside of these new pages?

@domlander
Copy link
Contributor

The spacing below the headers doesn't look consistent with the rest of the site to me. This might be intentional or not important so ignore me if so!

Screenshot 2026-01-08 at 09 26 48

@Georges-GNM Georges-GNM added the run_chromatic Runs chromatic when label is applied label Jan 8, 2026
@Georges-GNM
Copy link
Contributor Author

The spacing below the headers doesn't look consistent with the rest of the site to me. This might be intentional or not important so ignore me if so!

Good spot, think that's got a touch more padding than it should, will update.

@github-actions github-actions bot removed the run_chromatic Runs chromatic when label is applied label Jan 8, 2026
@Georges-GNM Georges-GNM added the run_chromatic Runs chromatic when label is applied label Jan 8, 2026
@github-actions github-actions bot removed the run_chromatic Runs chromatic when label is applied label Jan 8, 2026
@Georges-GNM Georges-GNM merged commit 1994da5 into main Jan 8, 2026
24 checks passed
@Georges-GNM Georges-GNM deleted the gl/sc-tag-page-mockup branch January 8, 2026 14:14
@gu-prout
Copy link

gu-prout bot commented Jan 8, 2026

Seen on PROD (merged by @Georges-GNM 7 minutes and 51 seconds ago) Please check your changes!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature Departmental tracking: work on a new feature Seen-on-PROD

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants