From 15e561a91dc9fbaf6cab42d66b780992be695908 Mon Sep 17 00:00:00 2001 From: gustavo-olmedo Date: Tue, 15 Sep 2026 17:55:20 +0100 Subject: [PATCH 01/15] Add responsive ilustrated header --- .../src/layouts/PuzzlesLayout.test.tsx | 39 ++++++++++ .../src/layouts/PuzzlesLayout.tsx | 74 ++++++++++++++----- 2 files changed, 95 insertions(+), 18 deletions(-) diff --git a/dotcom-rendering/src/layouts/PuzzlesLayout.test.tsx b/dotcom-rendering/src/layouts/PuzzlesLayout.test.tsx index ef796d8400c..d49401d6787 100644 --- a/dotcom-rendering/src/layouts/PuzzlesLayout.test.tsx +++ b/dotcom-rendering/src/layouts/PuzzlesLayout.test.tsx @@ -61,4 +61,43 @@ describe('PuzzlesLayout', () => { document.getElementById('dfp-ad--mobile-above-nav'), ).not.toBeInTheDocument(); }); + it('renders the illustrated header with responsive sources and a fixed height', () => { + render(); + const header = screen + .getByRole('heading', { + level: 1, + name: 'Puzzles & Games', + }) + .closest('header')!; + expect(header).toHaveStyle({ height: '230px' }); + const picture = header.querySelector('picture')!; + const sources = Array.from(picture.querySelectorAll('source')); + const expected = [ + [1728, 'header-desktop-1728px'], + [1300, 'header-wide-1440px'], + [1140, 'header-leftcol-1280px'], + [980, 'header-desktop-1024px'], + [768, 'header-tablet-979px'], + [740, 'header-tablet-768px'], + [660, 'header-mobile-phablet-669px'], + [480, 'header-mobile-landscape-480px'], + [375, 'header-mobile-medium-393px'], + ]; + expect(sources).toHaveLength(expected.length); + for (const [index, [width, filename]] of expected.entries()) { + expect(sources[index]).toHaveAttribute( + 'media', + `(min-width: ${width}px)`, + ); + expect(sources[index]).toHaveAttribute( + 'srcset', + `https://i.guim.co.uk/img/uploads/2026/09/15/${filename}.png?width=440&dpr=2&s=none`, + ); + } + expect(picture.querySelector('img')).toHaveAttribute( + 'src', + 'https://i.guim.co.uk/img/uploads/2026/09/15/header-mobile-360px.png?width=440&dpr=2&s=none', + ); + expect(picture.querySelector('img')).toHaveAttribute('alt', ''); + }); }); diff --git a/dotcom-rendering/src/layouts/PuzzlesLayout.tsx b/dotcom-rendering/src/layouts/PuzzlesLayout.tsx index 3d85df4bab4..7c7f8c9c5c4 100644 --- a/dotcom-rendering/src/layouts/PuzzlesLayout.tsx +++ b/dotcom-rendering/src/layouts/PuzzlesLayout.tsx @@ -1,9 +1,9 @@ import { css } from '@emotion/react'; import { - from, - headlineBold50, + breakpoints, palette, space, + visuallyHidden, } from '@guardian/source/foundations'; import { Footer } from '../components/Footer'; import { HeaderAdSlot } from '../components/HeaderAdSlot'; @@ -22,26 +22,54 @@ const mainStyles = css` `; const brandStyles = css` + box-sizing: border-box; max-width: 1300px; + height: 230px; margin: 0 auto; - padding: ${space[6]}px ${space[3]}px ${space[8]}px; + overflow: hidden; border-right: 1px solid ${palette.neutral[86]}; border-left: 1px solid ${palette.neutral[86]}; - background: ${palette.neutral[97]}; + /* The artwork is transparent and uses this illustrated-header background. */ + background: #fff7f0; `; -const titleStyles = css` - max-width: 10ch; - margin: 0; - ${headlineBold50}; - font-size: 48px; - line-height: 0.9; +const brandImageStyles = css` + display: block; + width: 100%; + height: 100%; + object-fit: cover; +`; - ${from.tablet} { - font-size: 64px; - } +const brandPictureStyles = css` + display: block; + height: 100%; `; +const headerArtwork = (filename: string) => + `https://i.guim.co.uk/img/uploads/2026/09/15/${filename}.png?width=440&dpr=2&s=none`; + +// Descending media queries ensure that the browser chooses the largest match. +const headerSources = [ + { breakpoint: 1728, filename: 'header-desktop-1728px' }, + { breakpoint: breakpoints.wide, filename: 'header-wide-1440px' }, + { breakpoint: breakpoints.leftCol, filename: 'header-leftcol-1280px' }, + { breakpoint: breakpoints.desktop, filename: 'header-desktop-1024px' }, + { breakpoint: 768, filename: 'header-tablet-979px' }, + { breakpoint: breakpoints.tablet, filename: 'header-tablet-768px' }, + { + breakpoint: breakpoints.phablet, + filename: 'header-mobile-phablet-669px', + }, + { + breakpoint: breakpoints.mobileLandscape, + filename: 'header-mobile-landscape-480px', + }, + { + breakpoint: breakpoints.mobileMedium, + filename: 'header-mobile-medium-393px', + }, +]; + export const PuzzlesLayout = ({ puzzlesPage, NAV, @@ -88,11 +116,21 @@ export const PuzzlesLayout = ({ id="maincontent" >
-

- Puzzles -
- & Games -

+

Puzzles and Games

+ + {headerSources.map(({ breakpoint, filename }) => ( + + ))} + +
Date: Tue, 15 Sep 2026 18:20:39 +0100 Subject: [PATCH 02/15] Improve puzzle image accesibility and display stters --- .../src/components/NewsletterSignupCard.tsx | 109 +++++++---------- .../components/PuzzlesDirectory.stories.tsx | 29 +++-- .../src/components/PuzzlesDirectory.test.tsx | 113 ++++++++++++++++++ .../src/components/PuzzlesDirectory.tsx | 11 +- .../src/components/PuzzlesSupporting.tsx | 2 + .../src/layouts/PuzzlesLayout.test.tsx | 6 +- .../src/model/validate.puzzlesPage.test.ts | 20 ++++ dotcom-rendering/src/model/validate.ts | 2 + dotcom-rendering/src/types/puzzlesPage.ts | 2 + 9 files changed, 209 insertions(+), 85 deletions(-) diff --git a/dotcom-rendering/src/components/NewsletterSignupCard.tsx b/dotcom-rendering/src/components/NewsletterSignupCard.tsx index acd915bcf95..3be105a1594 100644 --- a/dotcom-rendering/src/components/NewsletterSignupCard.tsx +++ b/dotcom-rendering/src/components/NewsletterSignupCard.tsx @@ -4,7 +4,7 @@ import { headlineMedium20, space, textSans14, - textSansBold15, + textSans15, } from '@guardian/source/foundations'; import { SvgNewsletterFilled } from '@guardian/source/react-components'; import { palette as themePalette } from '../palette'; @@ -14,6 +14,8 @@ export type NewsletterSignupCardProps = { frequency: string; description: string; illustrationSquare?: string; + illustrationAlt?: string; + hideIllustrationFromScreenReaders?: boolean; children?: React.ReactNode; isSignedIn?: boolean | 'Pending'; isModal?: boolean; @@ -22,7 +24,7 @@ export type NewsletterSignupCardProps = { const containerStyles = css` clear: left; background-color: ${themePalette('--newsletter-card-background')}; - padding: ${space[2]}px ${space[3]}px ${space[4]}px ${space[3]}px; + padding: ${space[3]}px ${space[3]}px ${space[4]}px ${space[3]}px; `; const dividerStyles = css` @@ -55,64 +57,34 @@ const titleStyles = css` const frequencyTagStyles = css` display: flex; align-items: center; - gap: 6px; color: ${themePalette('--newsletter-card-frequency-tag')}; - ${textSansBold15}; - margin-bottom: ${space[2]}px; -`; - -const frequencyTextStyles = css` - display: flex; - flex-wrap: wrap; - column-gap: ${space[1]}px; -`; - -const frequencyLabelStyles = css` - white-space: nowrap; -`; - -const badgeStyles = css` - display: flex; - align-items: center; - justify-content: center; - flex-shrink: 0; - width: 24px; - height: 24px; - border-radius: 50%; - background-color: ${themePalette('--newsletter-card-badge-background')}; + ${textSans15}; + margin-left: -1px; + margin-top: -1px; + margin-bottom: ${space[1]}px; svg { - fill: ${themePalette('--newsletter-card-badge-icon')}; - height: 18px; - width: 18px; + fill: currentColor; + height: 20px; + width: 20px; } `; -const innerDividerStyles = css` - border: none; - border-top: 1px solid ${themePalette('--newsletter-card-divider')}; - margin: 0 -${space[3]}px ${space[2]}px; -`; - const descriptionStyles = css` ${textSans14}; line-height: 1.15; + margin-bottom: ${space[2]}px; clear: both; color: ${themePalette('--newsletter-card-description')}; `; const illustrationStyles = css` flex-shrink: 0; - width: 70px; - height: 70px; + width: 90px; + height: 90px; border-radius: 50%; object-fit: cover; - ${from.mobileMedium} { - width: 90px; - height: 90px; - } - ${from.tablet} { width: 100px; height: 100px; @@ -122,35 +94,30 @@ const illustrationStyles = css` const NewsletterSignupHeader = ( props: Omit, ) => ( - <> -
- +
+
+
- - - Free newsletter | - {props.frequency} - -
-
-
-
-

- Sign up to {props.name} -

-

{props.description}

+ Free newsletter | {props.frequency}
- {!!props.illustrationSquare && ( - - )} +

+ Sign up to {props.name} +

+

{props.description}

- + {!!props.illustrationSquare && ( + {props.illustrationAlt + )} +
); export const NewsletterSignupCard = ({ @@ -158,6 +125,8 @@ export const NewsletterSignupCard = ({ frequency, description, illustrationSquare, + illustrationAlt, + hideIllustrationFromScreenReaders, children, isSignedIn, isModal = false, @@ -169,6 +138,10 @@ export const NewsletterSignupCard = ({ name={name} description={description} illustrationSquare={illustrationSquare} + illustrationAlt={illustrationAlt} + hideIllustrationFromScreenReaders={ + hideIllustrationFromScreenReaders + } isSignedIn={isSignedIn} /> {children} diff --git a/dotcom-rendering/src/components/PuzzlesDirectory.stories.tsx b/dotcom-rendering/src/components/PuzzlesDirectory.stories.tsx index 238ead0c7a3..956bdf5e8d0 100644 --- a/dotcom-rendering/src/components/PuzzlesDirectory.stories.tsx +++ b/dotcom-rendering/src/components/PuzzlesDirectory.stories.tsx @@ -2,9 +2,6 @@ import { breakpoints } from '@guardian/source/foundations'; import type { PuzzlesLayoutType } from '../types/puzzlesPage'; import { PuzzlesDirectory } from './PuzzlesDirectory'; -const previewImage = - 'https://i.guim.co.uk/img/uploads/2023/11/01/SaturdayEdition_-_5-3.jpg?width=600&dpr=1&s=none&crop=5%3A3'; - const layout: PuzzlesLayoutType = { containers: [ { @@ -17,26 +14,28 @@ const layout: PuzzlesLayoutType = { items: [ [ { - id: 'ball', - title: 'On the ball', - type: 'quiz', - set: 'all', + id: 'featured-mini', + title: 'Mini crossword', + type: 'crossword', + set: 'mini', cardVariant: 'large', cadence: 'Daily', - image: previewImage, - slug: 'on-the-ball', - variant: 'iframe-page', + image: 'https://i.guim.co.uk/img/uploads/2026/09/15/crossword-MINI.png?width=440&dpr=2&s=none', + imageAlt: 'Mini crossword illustration', + setter: 'Example setter', + url: '/puzzles-and-games/crosswords/mini/1', backgroundColour: '#e5e5e5', }, { - id: 'film', - title: 'Film reveal', - type: 'quiz', + id: 'featured-word-wheel', + title: 'Word wheel', + type: 'word-wheel', set: 'all', cardVariant: 'large', cadence: 'Daily', - image: previewImage, - slug: 'film-reveal', + image: 'https://i.guim.co.uk/img/uploads/2026/09/15/word-games-WORD-WHEEL.png?width=440&dpr=2&s=none', + imageAlt: 'Word wheel illustration', + slug: 'word-wheel', variant: 'iframe-page', backgroundColour: '#e5e5e5', }, diff --git a/dotcom-rendering/src/components/PuzzlesDirectory.test.tsx b/dotcom-rendering/src/components/PuzzlesDirectory.test.tsx index 46f3d16fdb3..d65ef3e8ef6 100644 --- a/dotcom-rendering/src/components/PuzzlesDirectory.test.tsx +++ b/dotcom-rendering/src/components/PuzzlesDirectory.test.tsx @@ -33,6 +33,119 @@ const section = ( }); describe('PuzzlesDirectory', () => { + it('describes card artwork without adding it to screen-reader link names', () => { + const { container } = render( + , + ); + const image = container.querySelector('img'); + expect(image).toHaveAttribute('alt', 'Word wheel illustration'); + expect(image).toHaveAttribute('aria-hidden', 'true'); + expect(screen.queryByRole('img')).not.toBeInTheDocument(); + expect( + screen.getByRole('link', { name: 'Daily puzzle Daily' }), + ).toBeInTheDocument(); + }); + + it('provides fallback alt text and keeps compact cards text-only', () => { + const { container } = render( + , + ); + expect(container.querySelectorAll('img')).toHaveLength(1); + expect(container.querySelector('img')).toHaveAttribute( + 'alt', + 'Daily puzzle illustration', + ); + }); + + it('shows setters on crossword cards across variants but not on other puzzles', () => { + render( + [ + item({ + id: cardVariant, + type: 'crossword', + cardVariant, + setter: ' Example setter ', + }), + ]), + [ + item({ + id: 'word-wheel', + setter: 'Ignored setter', + }), + ], + [ + item({ + id: 'blank-setter', + type: 'crossword', + setter: ' ', + }), + ], + ], + nestedContainers: [], + }, + }), + ], + }} + renderAds={false} + />, + ); + expect(screen.getAllByText('By: Example setter')).toHaveLength(3); + expect( + screen.queryByText('By: Ignored setter'), + ).not.toBeInTheDocument(); + expect(screen.queryByText('By:')).not.toBeInTheDocument(); + expect(screen.queryByText('Played')).not.toBeInTheDocument(); + }); it('renders unique desktop and mobile IDs for multiple blueprint slots', () => { const layout: PuzzlesLayoutType = { containers: ['inline1', 'inline2'].map((adSlot) => diff --git a/dotcom-rendering/src/components/PuzzlesDirectory.tsx b/dotcom-rendering/src/components/PuzzlesDirectory.tsx index acd5d476df7..35e4fea1813 100644 --- a/dotcom-rendering/src/components/PuzzlesDirectory.tsx +++ b/dotcom-rendering/src/components/PuzzlesDirectory.tsx @@ -162,6 +162,12 @@ const cadenceStyles = css` ${textSans12}; `; +const setterStyles = css` + margin-top: ${space[1]}px; + color: ${palette.error[400]}; + ${textSans12}; +`; + const cardImageStyles = (isFeatured: boolean) => css` width: 100%; height: 100%; @@ -245,6 +251,7 @@ const PuzzleCard = ({ item: PuzzleItem; }) => { const url = getPuzzleUrl(item); + const setter = item.type === 'crossword' ? item.setter?.trim() : undefined; const hasImage = item.image !== undefined && item.image.length > 0 && @@ -258,10 +265,12 @@ const PuzzleCard = ({ {item.cadence !== undefined && item.cadence.length > 0 && ( {item.cadence} )} + {setter && By: {setter}}
{hasImage && ( diff --git a/dotcom-rendering/src/components/PuzzlesSupporting.tsx b/dotcom-rendering/src/components/PuzzlesSupporting.tsx index af2d6af9961..9a600d07a72 100644 --- a/dotcom-rendering/src/components/PuzzlesSupporting.tsx +++ b/dotcom-rendering/src/components/PuzzlesSupporting.tsx @@ -295,6 +295,8 @@ export const PuzzlesSupporting = ({ illustrationSquare={ newsletter.illustrationSquare } + illustrationAlt={`${newsletter.name} newsletter illustration`} + hideIllustrationFromScreenReaders={true} isModal={true} name={newsletter.name} > diff --git a/dotcom-rendering/src/layouts/PuzzlesLayout.test.tsx b/dotcom-rendering/src/layouts/PuzzlesLayout.test.tsx index d49401d6787..c3947169a04 100644 --- a/dotcom-rendering/src/layouts/PuzzlesLayout.test.tsx +++ b/dotcom-rendering/src/layouts/PuzzlesLayout.test.tsx @@ -98,6 +98,10 @@ describe('PuzzlesLayout', () => { 'src', 'https://i.guim.co.uk/img/uploads/2026/09/15/header-mobile-360px.png?width=440&dpr=2&s=none', ); - expect(picture.querySelector('img')).toHaveAttribute('alt', ''); + expect(picture.querySelector('img')).toHaveAttribute( + 'alt', + 'An owl carrying a crossword grid beside an octopus reading a puzzle', + ); + expect(picture.querySelector('img')).not.toHaveAttribute('aria-hidden'); }); }); diff --git a/dotcom-rendering/src/model/validate.puzzlesPage.test.ts b/dotcom-rendering/src/model/validate.puzzlesPage.test.ts index 141f482d64b..cf5ec0652a5 100644 --- a/dotcom-rendering/src/model/validate.puzzlesPage.test.ts +++ b/dotcom-rendering/src/model/validate.puzzlesPage.test.ts @@ -38,6 +38,26 @@ const validPage = () => ({ }); describe('validateAsPuzzlesPageType', () => { + it('accepts artwork descriptions and crossword setter names', () => { + const page = validPage(); + Object.assign(page.layout.containers[0]!.content.items[0]![0]!, { + imageAlt: 'Word wheel illustration', + setter: 'Example setter', + }); + expect(validateAsPuzzlesPageType(page)).toBeDefined(); + }); + + it.each(['imageAlt', 'setter'])( + 'rejects non-string %s metadata', + (field) => { + const page = validPage(); + Object.assign(page.layout.containers[0]!.content.items[0]![0]!, { + [field]: 123, + }); + expect(() => validateAsPuzzlesPageType(page)).toThrow(); + }, + ); + it.each(['inline1', 'mostpop'])( 'rejects repeated %s slot names', (adSlot) => { diff --git a/dotcom-rendering/src/model/validate.ts b/dotcom-rendering/src/model/validate.ts index 60480796519..06e992ae56c 100644 --- a/dotcom-rendering/src/model/validate.ts +++ b/dotcom-rendering/src/model/validate.ts @@ -193,6 +193,8 @@ const isPuzzleItem = (data: unknown): data is PuzzleItem => (isString(data.cadence) && data.cadence.trim().length > 0)) && (data.url === undefined || isString(data.url)) && (data.image === undefined || isString(data.image)) && + (data.imageAlt === undefined || isString(data.imageAlt)) && + (data.setter === undefined || isString(data.setter)) && (data.slug === undefined || isString(data.slug)) && (data.index === undefined || Number.isInteger(data.index)) && (data.variant === undefined || isString(data.variant)) && diff --git a/dotcom-rendering/src/types/puzzlesPage.ts b/dotcom-rendering/src/types/puzzlesPage.ts index f1be6c66d90..6d3afc5acf3 100644 --- a/dotcom-rendering/src/types/puzzlesPage.ts +++ b/dotcom-rendering/src/types/puzzlesPage.ts @@ -14,6 +14,8 @@ export type PuzzleItem = { cadence?: string; url?: string; image?: string; + imageAlt?: string; + setter?: string; slug?: string; index?: number; variant?: string; From 6961e244a24e8e2a79f9e1a64a8564383c649c0f Mon Sep 17 00:00:00 2001 From: gustavo-olmedo Date: Tue, 15 Sep 2026 19:10:06 +0100 Subject: [PATCH 03/15] Update to temporarily hide puzzles newsletter and popularity sections --- .../src/components/PuzzlesSupporting.test.tsx | 45 ++++ .../src/components/PuzzlesSupporting.tsx | 202 ++++++++++-------- 2 files changed, 153 insertions(+), 94 deletions(-) diff --git a/dotcom-rendering/src/components/PuzzlesSupporting.test.tsx b/dotcom-rendering/src/components/PuzzlesSupporting.test.tsx index 0b0f5a53b9f..2e232ca41c3 100644 --- a/dotcom-rendering/src/components/PuzzlesSupporting.test.tsx +++ b/dotcom-rendering/src/components/PuzzlesSupporting.test.tsx @@ -85,11 +85,54 @@ const supporting: PuzzlesSupportingContent = { }; describe('PuzzlesSupporting', () => { + it('shows only useful links by default, even when ads are enabled', () => { + render( + , + ); + expect( + screen.getByRole('heading', { name: 'Useful links' }), + ).toBeInTheDocument(); + expect( + screen.getByRole('link', { name: 'Support the Guardian' }), + ).toHaveAttribute('href', 'https://support.theguardian.com'); + expect( + screen.getByRole('link', { name: 'Help Centre' }), + ).toHaveAttribute('href', 'https://help.theguardian.com'); + expect(screen.getAllByRole('link')).toHaveLength(2); + expect( + screen.queryByTestId('newsletter-form-saturday-edition'), + ).not.toBeInTheDocument(); + expect( + screen.queryByRole('heading', { name: 'Most popular puzzles' }), + ).not.toBeInTheDocument(); + expect(screen.queryByTestId('mostpop-ad')).not.toBeInTheDocument(); + }); + it('renders configured links, newsletter and ranked puzzle groups', () => { render( , @@ -119,6 +162,7 @@ describe('PuzzlesSupporting', () => { adSlot="mostpop" layout={layout} renderAds={false} + showPopular={true} supporting={supporting} />, ); @@ -129,6 +173,7 @@ describe('PuzzlesSupporting', () => { adSlot="mostpop" layout={layout} renderAds={true} + showPopular={true} supporting={supporting} />, ); diff --git a/dotcom-rendering/src/components/PuzzlesSupporting.tsx b/dotcom-rendering/src/components/PuzzlesSupporting.tsx index 9a600d07a72..907e2793978 100644 --- a/dotcom-rendering/src/components/PuzzlesSupporting.tsx +++ b/dotcom-rendering/src/components/PuzzlesSupporting.tsx @@ -22,6 +22,8 @@ type Props = { adSlot?: string; id: string; layout: PuzzlesLayoutType; + showNewsletter?: boolean; + showPopular?: boolean; renderAds: boolean; supporting: PuzzlesSupportingContent; }; @@ -61,7 +63,7 @@ const sectionTitleStyles = css` } `; -const usefulContentStyles = css` +const usefulContentStyles = (showNewsletter: boolean) => css` display: grid; width: 100%; min-width: 0; @@ -69,7 +71,10 @@ const usefulContentStyles = css` border-top: 1px solid ${borderColour}; ${from.desktop} { - grid-template-columns: repeat(4, minmax(0, 1fr)); + grid-template-columns: repeat( + ${showNewsletter ? 4 : 2}, + minmax(0, 1fr) + ); } `; @@ -259,6 +264,9 @@ export const PuzzlesSupporting = ({ id, layout, renderAds, + // Keep these sections available, but hide them from the hub for now. + showNewsletter = false, + showPopular = false, supporting, }: Props) => { const itemsById = new Map( @@ -276,7 +284,7 @@ export const PuzzlesSupporting = ({ -
+
{supporting.usefulLinks.map((link) => ( ))} - {newsletter !== undefined && ( + {showNewsletter && newsletter !== undefined && (
- -
- -
-
- {supporting.popularGroups.map((group) => { - const items = group.itemIds - .map((_id) => itemsById.get(_id)) - .filter( - (item): item is PuzzleItem => - item !== undefined, - ); - if (items.length === 0) { - return null; - } - return ( -
-

- {group.title} -

-
    - {items.map((item, index) => { - const url = puzzleUrl(item); - const contents = ( - <> - - {item.title} - - {item.cadence !== - undefined && - item.cadence.length > - 0 && ( - - {item.cadence} - - )} - - ); - return ( -
  1. - - {url !== undefined ? ( - + +
  2. + ); + })} +
+
+ ); + })}
- )} -
-
+ {hasMostPopAd && ( +
+ +
+ )} +
+ + )} ); }; From e383713afc59e169b4cd95ab08cf67dd33c99963 Mon Sep 17 00:00:00 2001 From: gustavo-olmedo Date: Tue, 15 Sep 2026 19:15:41 +0100 Subject: [PATCH 04/15] Update to use guardian headline typography for puzzles useful links --- dotcom-rendering/src/components/PuzzlesSupporting.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dotcom-rendering/src/components/PuzzlesSupporting.tsx b/dotcom-rendering/src/components/PuzzlesSupporting.tsx index 907e2793978..2df88944df3 100644 --- a/dotcom-rendering/src/components/PuzzlesSupporting.tsx +++ b/dotcom-rendering/src/components/PuzzlesSupporting.tsx @@ -1,6 +1,7 @@ import { css } from '@emotion/react'; import { from, + headlineBold20, headlineBold24, palette, space, @@ -84,7 +85,7 @@ const usefulLinkStyles = css` border-right: 1px solid ${borderColour}; color: ${palette.neutral[7]}; text-decoration: none; - ${textSansBold14}; + ${headlineBold20}; :hover { text-decoration: underline; From bc290106b1ea7b2dd1044005db9c500e102501a3 Mon Sep 17 00:00:00 2001 From: gustavo-olmedo Date: Tue, 15 Sep 2026 19:16:08 +0100 Subject: [PATCH 05/15] Fix issue variable name --- dotcom-rendering/src/components/PuzzlesSupporting.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotcom-rendering/src/components/PuzzlesSupporting.tsx b/dotcom-rendering/src/components/PuzzlesSupporting.tsx index 2df88944df3..1660a5140ad 100644 --- a/dotcom-rendering/src/components/PuzzlesSupporting.tsx +++ b/dotcom-rendering/src/components/PuzzlesSupporting.tsx @@ -337,7 +337,7 @@ export const PuzzlesSupporting = ({
{supporting.popularGroups.map((group) => { const items = group.itemIds - .map((id) => itemsById.get(id)) + .map((_id) => itemsById.get(_id)) .filter( (item): item is PuzzleItem => item !== undefined, From 93f0194446bc6e8a176d596cd724eafdcc2a521d Mon Sep 17 00:00:00 2001 From: gustavo-olmedo Date: Tue, 15 Sep 2026 23:39:33 +0100 Subject: [PATCH 06/15] Update puzzles hub card styling and responsive dimensions --- .../components/PuzzlesArchiveMenu.island.tsx | 13 +- .../src/components/PuzzlesDirectory.test.tsx | 43 +++++ .../src/components/PuzzlesDirectory.tsx | 157 +++++++++++++----- .../src/layouts/PuzzlesLayout.test.tsx | 1 + .../src/layouts/PuzzlesLayout.tsx | 27 ++- 5 files changed, 193 insertions(+), 48 deletions(-) diff --git a/dotcom-rendering/src/components/PuzzlesArchiveMenu.island.tsx b/dotcom-rendering/src/components/PuzzlesArchiveMenu.island.tsx index 9db36b081f6..b901eaf0b4b 100644 --- a/dotcom-rendering/src/components/PuzzlesArchiveMenu.island.tsx +++ b/dotcom-rendering/src/components/PuzzlesArchiveMenu.island.tsx @@ -33,7 +33,7 @@ const wrapperStyles = css` position: relative; display: block; width: max-content; - margin-top: ${space[1]}px; + margin-top: ${space[2]}px; margin-left: auto; ${from.tablet} { @@ -46,8 +46,9 @@ const wrapperStyles = css` `; const summaryStyles = css` + position: relative; display: inline-flex; - min-height: 44px; + min-height: 24px; align-items: center; gap: ${space[2]}px; padding: 0; @@ -57,6 +58,12 @@ const summaryStyles = css` cursor: pointer; list-style: none; ${textSans14}; + /* Preserve a 44px pointer target around the 24px archive label. */ + ::before { + position: absolute; + inset: -10px 0; + content: ''; + } ::-webkit-details-marker { display: none; @@ -158,7 +165,7 @@ export const PuzzlesArchiveMenu = ({ className="archive-arrow" css={arrowStyles} > - {'>'} + →