From 6dc802e578ee6ff58da0ea6ee761ce8cf67a6df5 Mon Sep 17 00:00:00 2001 From: Frederick O'Brien Date: Tue, 13 Jan 2026 14:31:15 +0000 Subject: [PATCH 1/6] Breathtakingly rough first pass --- dotcom-rendering/index.d.ts | 1 + dotcom-rendering/src/components/Figure.tsx | 23 ++++++++++++++++++- .../InteractiveBlockComponent.importable.tsx | 2 ++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/dotcom-rendering/index.d.ts b/dotcom-rendering/index.d.ts index 017d6bd5ed5..205b2ed24c4 100644 --- a/dotcom-rendering/index.d.ts +++ b/dotcom-rendering/index.d.ts @@ -94,6 +94,7 @@ declare namespace JSX { 'data-spacefinder-role'?: | 'nested' | 'immersive' + | 'fullWidth' | 'inline' | 'richLink' | 'thumbnail'; diff --git a/dotcom-rendering/src/components/Figure.tsx b/dotcom-rendering/src/components/Figure.tsx index d169c06b022..71c729bbee4 100644 --- a/dotcom-rendering/src/components/Figure.tsx +++ b/dotcom-rendering/src/components/Figure.tsx @@ -70,6 +70,25 @@ const roleCss = { } `, + fullWidth: css` + position: relative; + margin-top: ${space[3]}px; + margin-bottom: ${space[3]}px; + width: calc(100vw - var(--scrollbar-width)); + left: 50%; + right: 50%; + margin-left: calc(-50vw + var(--half-scrollbar-width)); + ${from.tablet} { + margin-left: calc(-50vw + var(--half-scrollbar-width) + 10px); + } + ${from.leftCol} { + margin-left: calc(-50vw - var(--half-scrollbar-width)); + } + ${from.wide} { + margin-left: calc(-50vw + var(--half-scrollbar-width)); + } + `, + showcase: css` margin-top: ${space[3]}px; margin-bottom: ${space[3]}px; @@ -150,7 +169,7 @@ const roleCss = { // Used for vast majority of layouts. export const defaultRoleStyles = ( - role: RoleType | 'richLink', + role: RoleType | 'richLink' | 'fullWidth', format: ArticleFormat, isTimeline = false, ) => { @@ -161,6 +180,8 @@ export const defaultRoleStyles = ( return roleCss.supporting; case 'immersive': return roleCss.immersive; + case 'fullWidth': + return roleCss.fullWidth; case 'showcase': if (isTimeline) { return css` diff --git a/dotcom-rendering/src/components/InteractiveBlockComponent.importable.tsx b/dotcom-rendering/src/components/InteractiveBlockComponent.importable.tsx index ab5cc104ae4..18c70f1f37c 100644 --- a/dotcom-rendering/src/components/InteractiveBlockComponent.importable.tsx +++ b/dotcom-rendering/src/components/InteractiveBlockComponent.importable.tsx @@ -144,6 +144,8 @@ const datawrapperRoleStyles = (role: RoleType): SerializedStyles | null => { case 'thumbnail': case 'halfWidth': return null; + default: + return null; } }; From 026430a334c5e3958b2cab6132417963ea243a29 Mon Sep 17 00:00:00 2001 From: Frederick O'Brien Date: Tue, 13 Jan 2026 17:56:27 +0000 Subject: [PATCH 2/6] Refactor to use CSS Grid I may cry if this actually works --- dotcom-rendering/src/components/Figure.tsx | 17 ++--------------- .../InteractiveBlockComponent.importable.tsx | 2 -- 2 files changed, 2 insertions(+), 17 deletions(-) diff --git a/dotcom-rendering/src/components/Figure.tsx b/dotcom-rendering/src/components/Figure.tsx index 71c729bbee4..d0a2ede89b7 100644 --- a/dotcom-rendering/src/components/Figure.tsx +++ b/dotcom-rendering/src/components/Figure.tsx @@ -7,7 +7,7 @@ type Props = { children: React.ReactNode; format: ArticleFormat; isMainMedia: boolean; - role?: RoleType | 'richLink'; + role?: RoleType | 'richLink' | 'fullWidth'; id?: string; className?: string; type?: FEElement['_type']; @@ -71,22 +71,9 @@ const roleCss = { `, fullWidth: css` - position: relative; margin-top: ${space[3]}px; margin-bottom: ${space[3]}px; - width: calc(100vw - var(--scrollbar-width)); - left: 50%; - right: 50%; - margin-left: calc(-50vw + var(--half-scrollbar-width)); - ${from.tablet} { - margin-left: calc(-50vw + var(--half-scrollbar-width) + 10px); - } - ${from.leftCol} { - margin-left: calc(-50vw - var(--half-scrollbar-width)); - } - ${from.wide} { - margin-left: calc(-50vw + var(--half-scrollbar-width)); - } + grid-column: 1 / -1; `, showcase: css` diff --git a/dotcom-rendering/src/components/InteractiveBlockComponent.importable.tsx b/dotcom-rendering/src/components/InteractiveBlockComponent.importable.tsx index 18c70f1f37c..ab5cc104ae4 100644 --- a/dotcom-rendering/src/components/InteractiveBlockComponent.importable.tsx +++ b/dotcom-rendering/src/components/InteractiveBlockComponent.importable.tsx @@ -144,8 +144,6 @@ const datawrapperRoleStyles = (role: RoleType): SerializedStyles | null => { case 'thumbnail': case 'halfWidth': return null; - default: - return null; } }; From 17f575bddfa93dcb18617da9f428b60e523d5ca0 Mon Sep 17 00:00:00 2001 From: Frederick O'Brien Date: Tue, 20 Jan 2026 15:22:22 +0000 Subject: [PATCH 3/6] Don't use CSS grid Alas --- dotcom-rendering/src/components/Figure.tsx | 43 +++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/dotcom-rendering/src/components/Figure.tsx b/dotcom-rendering/src/components/Figure.tsx index d0a2ede89b7..6cb08e0d21e 100644 --- a/dotcom-rendering/src/components/Figure.tsx +++ b/dotcom-rendering/src/components/Figure.tsx @@ -73,7 +73,48 @@ const roleCss = { fullWidth: css` margin-top: ${space[3]}px; margin-bottom: ${space[3]}px; - grid-column: 1 / -1; + + overflow-x: clip; + + ${until.mobileLandscape} { + margin-left: -10px; + margin-right: -10px; + } + ${until.tablet} { + margin-left: -20px; + margin-right: -20px; + } + ${from.tablet} { + width: calc(100vw - var(--scrollbar-width, 0px)); + max-width: calc(100vw - var(--scrollbar-width, 0px)); + + --grid-container-max-width: 740px; + --grid-container-left-margin: calc( + ((-100vw + (var(--grid-container-max-width) - 42px)) / 2) + + var(--half-scrollbar-width, 0px) + ); + --grid-body-column-left: calc( + (var(--grid-container-left-margin, 0px) * -1) + + var(--grid-left-col-width, 0px) + ); + + margin-left: var(--grid-container-left-margin); + } + ${from.desktop} { + --grid-container-max-width: 980px; + } + ${from.leftCol} { + --grid-container-max-width: 1140px; + --grid-left-col-width: 140px; + --grid-body-column-left: calc( + (var(--grid-container-left-margin, 0px) * -1) + + var(--grid-left-col-width, 0px) + 21px + ); + } + ${from.wide} { + --grid-container-max-width: 1300px; + --grid-left-col-width: 219px; + } `, showcase: css` From b18330fcc4f846c96d7233b2677783a78c53ea90 Mon Sep 17 00:00:00 2001 From: Frederick O'Brien Date: Wed, 21 Jan 2026 09:34:22 +0000 Subject: [PATCH 4/6] Limit full width weighting to interactive articles --- .../src/layouts/lib/interactiveLegacyStyling.ts | 2 +- dotcom-rendering/src/lib/renderElement.tsx | 12 +++++++++++- dotcom-rendering/src/types/content.ts | 2 +- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/dotcom-rendering/src/layouts/lib/interactiveLegacyStyling.ts b/dotcom-rendering/src/layouts/lib/interactiveLegacyStyling.ts index 660edff22c2..698e7a109a9 100644 --- a/dotcom-rendering/src/layouts/lib/interactiveLegacyStyling.ts +++ b/dotcom-rendering/src/layouts/lib/interactiveLegacyStyling.ts @@ -9,7 +9,7 @@ export const isInteractive = (design: ArticleDesign): boolean => export const interactiveLegacyFigureClasses = ( elementType: string, - role?: RoleType, + role?: RoleType | 'fullWidth', ): string => { const elementClasses: { [key: string]: string } = { 'model.dotcomrendering.pageElements.InteractiveAtomBlockElement': diff --git a/dotcom-rendering/src/lib/renderElement.tsx b/dotcom-rendering/src/lib/renderElement.tsx index 55d8c605e95..c38822e8a33 100644 --- a/dotcom-rendering/src/lib/renderElement.tsx +++ b/dotcom-rendering/src/lib/renderElement.tsx @@ -1060,7 +1060,17 @@ export const RenderArticleElement = ({ }); const needsFigure = !bareElements.has(element._type); - const role = 'role' in element ? (element.role as RoleType) : undefined; + + const isInteractiveFormat = + format.design === ArticleDesign.Interactive || + format.design === ArticleDesign.FullPageInteractive; + + const role: RoleType | 'fullWidth' | undefined = + 'role' in element + ? element.role === 'fullWidth' && !isInteractiveFormat + ? 'immersive' + : (element.role as RoleType | undefined) + : undefined; return needsFigure ? (
Date: Wed, 21 Jan 2026 09:53:07 +0000 Subject: [PATCH 5/6] Fresh schemas --- dotcom-rendering/src/frontend/schemas/feArticle.json | 11 ++++++++++- dotcom-rendering/src/model/block-schema.json | 11 ++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/dotcom-rendering/src/frontend/schemas/feArticle.json b/dotcom-rendering/src/frontend/schemas/feArticle.json index 76e940db3c7..2423bc93ee0 100644 --- a/dotcom-rendering/src/frontend/schemas/feArticle.json +++ b/dotcom-rendering/src/frontend/schemas/feArticle.json @@ -2378,7 +2378,16 @@ "type": "string" }, "role": { - "$ref": "#/definitions/RoleType" + "enum": [ + "fullWidth", + "halfWidth", + "immersive", + "inline", + "showcase", + "supporting", + "thumbnail" + ], + "type": "string" } }, "required": [ diff --git a/dotcom-rendering/src/model/block-schema.json b/dotcom-rendering/src/model/block-schema.json index e1556c4a221..9423b6cf0ef 100644 --- a/dotcom-rendering/src/model/block-schema.json +++ b/dotcom-rendering/src/model/block-schema.json @@ -1866,7 +1866,16 @@ "type": "string" }, "role": { - "$ref": "#/definitions/RoleType" + "enum": [ + "fullWidth", + "halfWidth", + "immersive", + "inline", + "showcase", + "supporting", + "thumbnail" + ], + "type": "string" } }, "required": [ From 5c956c531dc0ea73c15b6086f7f0837ea3fbd91d Mon Sep 17 00:00:00 2001 From: Frederick O'Brien Date: Wed, 21 Jan 2026 17:27:28 +0000 Subject: [PATCH 6/6] Mimic `--scrollbar-width` --- dotcom-rendering/src/components/Figure.tsx | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/dotcom-rendering/src/components/Figure.tsx b/dotcom-rendering/src/components/Figure.tsx index 6cb08e0d21e..8ce7dbc7679 100644 --- a/dotcom-rendering/src/components/Figure.tsx +++ b/dotcom-rendering/src/components/Figure.tsx @@ -74,7 +74,13 @@ const roleCss = { margin-top: ${space[3]}px; margin-bottom: ${space[3]}px; - overflow-x: clip; + @property --scrollbar-width { + syntax: ''; + inherits: true; + initial-value: 15px; + } + + --scrollbar-width: 15px; ${until.mobileLandscape} { margin-left: -10px; @@ -85,20 +91,20 @@ const roleCss = { margin-right: -20px; } ${from.tablet} { - width: calc(100vw - var(--scrollbar-width, 0px)); - max-width: calc(100vw - var(--scrollbar-width, 0px)); + width: calc(100vw - var(--scrollbar-width, 15px)); + max-width: calc(100vw - var(--scrollbar-width, 15px)); --grid-container-max-width: 740px; --grid-container-left-margin: calc( ((-100vw + (var(--grid-container-max-width) - 42px)) / 2) + - var(--half-scrollbar-width, 0px) + (var(--scrollbar-width, 15px) / 2) ); --grid-body-column-left: calc( (var(--grid-container-left-margin, 0px) * -1) + var(--grid-left-col-width, 0px) ); - margin-left: var(--grid-container-left-margin); + margin-left: calc(var(--grid-container-left-margin)); } ${from.desktop} { --grid-container-max-width: 980px;