diff --git a/dotcom-rendering/package.json b/dotcom-rendering/package.json index 77ee33e9863..fb6b8adaf2f 100644 --- a/dotcom-rendering/package.json +++ b/dotcom-rendering/package.json @@ -82,6 +82,7 @@ "@types/clean-css": "4.2.11", "@types/compression": "1.7.5", "@types/connect": "3.4.38", + "@types/crypto-js": "4.2.2", "@types/dompurify": "3.0.2", "@types/express": "4.17.21", "@types/he": "1.2.0", @@ -126,6 +127,7 @@ "compression": "1.7.4", "constructs": "10.4.2", "cpy": "11.0.0", + "crypto-js": "4.2.0", "css-loader": "7.1.2", "curlyquotes": "1.5.5", "dompurify": "3.2.4", diff --git a/dotcom-rendering/src/components/CardPicture.tsx b/dotcom-rendering/src/components/CardPicture.tsx index 0184e20cebf..961a97eee39 100644 --- a/dotcom-rendering/src/components/CardPicture.tsx +++ b/dotcom-rendering/src/components/CardPicture.tsx @@ -2,6 +2,7 @@ import { css } from '@emotion/react'; import { breakpoints, space, until } from '@guardian/source/foundations'; import type { ImgHTMLAttributes } from 'react'; import React from 'react'; +import { viewTransitionStyles } from '../lib/view-transition'; import type { AspectRatio } from '../types/front'; import type { ImageSizeType } from './Card/components/ImageWrapper'; import type { ImageWidthType } from './Picture'; @@ -220,6 +221,7 @@ export const CardPicture = ({ decideMobileAspectRatioStyles(mobileAspectRatio), roundedCorners && borderRadius, isCircular && circularStyles, + viewTransitionStyles('hero-image', alt), ]} > {sources.map((source) => { diff --git a/dotcom-rendering/src/components/DecideContainer.tsx b/dotcom-rendering/src/components/DecideContainer.tsx index 85e33692f1b..ce126867d32 100644 --- a/dotcom-rendering/src/components/DecideContainer.tsx +++ b/dotcom-rendering/src/components/DecideContainer.tsx @@ -29,7 +29,7 @@ import { FlexibleSpecial } from './FlexibleSpecial'; import { Island } from './Island'; import { NavList } from './NavList'; import { ScrollableFeature } from './ScrollableFeature.importable'; -import { ScrollableHighlights } from './ScrollableHighlights.importable'; +// import { ScrollableHighlights } from './ScrollableHighlights.importable'; import { ScrollableMedium } from './ScrollableMedium.importable'; import { ScrollableSmall } from './ScrollableSmall.importable'; import { StaticFeatureTwo } from './StaticFeatureTwo'; @@ -242,9 +242,10 @@ export const DecideContainer = ({ return ; case 'scrollable/highlights': return ( - - - + <> + // + // + // ); case 'flexible/special': return ( diff --git a/dotcom-rendering/src/components/Picture.tsx b/dotcom-rendering/src/components/Picture.tsx index dbb49e3292b..1108247d0e1 100644 --- a/dotcom-rendering/src/components/Picture.tsx +++ b/dotcom-rendering/src/components/Picture.tsx @@ -7,6 +7,7 @@ import { type ArticleFormat, } from '../lib/articleFormat'; import { generateImageURL } from '../lib/image'; +import { viewTransitionStyles } from '../lib/view-transition'; import type { RoleType } from '../types/content'; import type { AspectRatio } from '../types/front'; import type { Loading } from './CardPicture'; @@ -523,7 +524,12 @@ export const Picture = ({ const fallbackSource = getFallbackSource(sources); return ( - + {/* Immersive Main Media images get additional sources specifically for when in portrait orientation */} {format.display === ArticleDisplay.Immersive && isMainMedia && ( <> diff --git a/dotcom-rendering/src/lib/rootStyles.ts b/dotcom-rendering/src/lib/rootStyles.ts index bb486305302..69882e9829d 100644 --- a/dotcom-rendering/src/lib/rootStyles.ts +++ b/dotcom-rendering/src/lib/rootStyles.ts @@ -62,5 +62,72 @@ export const rootStyles = ( } } + @view-transition { + navigation: auto; + } + + /* ::view-transition-old(root) { + animation: 0.2s ease-in both slide-in; + } + + ::view-transition-new(root) { + animation: 0.2s ease-in both slide-in; + } */ + + /* ::view-transition-old(root) { + animation: 0.5s ease-in both spin; + } + + ::view-transition-new(root) { + animation: 0.5s ease-in both spin; + } */ + + /* ::view-transition-old(root) { + animation: 10.5s ease-in both spin; + } + + ::view-transition-new(root) { + animation: 10.5s ease-in both spin; + } */ + + /* Create a custom animation */ + @keyframes slide-out { + from { + transform: translateX(0%); + } + + to { + transform: translateX(-100%); + } + } + + @keyframes slide-in { + from { + transform: translateX(100%); + } + + to { + transform: translateX(0%); + } + } + + @keyframes spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } + } + + @keyframes zoom { + from { + transform: scale(0.95); + } + to { + transform: scale(1); + } + } + ${rootAdStyles} `; diff --git a/dotcom-rendering/src/lib/view-transition.ts b/dotcom-rendering/src/lib/view-transition.ts new file mode 100644 index 00000000000..e64439198c7 --- /dev/null +++ b/dotcom-rendering/src/lib/view-transition.ts @@ -0,0 +1,19 @@ +import type { SerializedStyles } from '@emotion/react'; +import { css } from '@emotion/react'; +import { SHA256 } from 'crypto-js'; + +function sha256Hash(message: string) { + return SHA256(message).toString(); +} + +export const viewTransitionStyles = ( + prefix: string, + stringToHash: string | undefined, +): SerializedStyles => { + const name = `${prefix}-${sha256Hash( + stringToHash ?? global.crypto.randomUUID(), + )}`; + return css` + /* view-transition-name: ${name}; */ + `; +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 254bbaac647..7e7c7700c1e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -460,6 +460,9 @@ importers: '@types/connect': specifier: 3.4.38 version: 3.4.38 + '@types/crypto-js': + specifier: 4.2.2 + version: 4.2.2 '@types/dompurify': specifier: 3.0.2 version: 3.0.2 @@ -592,6 +595,9 @@ importers: cpy: specifier: 11.0.0 version: 11.0.0 + crypto-js: + specifier: 4.2.0 + version: 4.2.0 css-loader: specifier: 7.1.2 version: 7.1.2(webpack@5.99.7) @@ -4074,7 +4080,7 @@ packages: '@typescript-eslint/parser': 6.18.0(eslint@8.56.0)(typescript@5.5.3) eslint: 8.56.0 eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.18.0)(eslint-plugin-import@2.29.1)(eslint@8.56.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.18.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.18.0)(eslint@8.56.0) tslib: 2.6.2 typescript: 5.5.3 transitivePeerDependencies: @@ -6122,7 +6128,7 @@ packages: react-docgen-typescript: 2.2.2(typescript@5.5.3) tslib: 2.6.2 typescript: 5.5.3 - webpack: 5.99.7(@swc/core@1.11.13)(esbuild@0.18.20)(webpack-cli@6.0.1) + webpack: 5.99.7(esbuild@0.18.20)(webpack-cli@6.0.1) transitivePeerDependencies: - supports-color dev: false @@ -6766,6 +6772,10 @@ packages: '@types/node': 22.14.1 dev: false + /@types/crypto-js@4.2.2: + resolution: {integrity: sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==} + dev: false + /@types/debug@4.1.12: resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} dependencies: @@ -7691,8 +7701,8 @@ packages: webpack: ^5.82.0 webpack-cli: 6.x.x dependencies: - webpack: 5.99.7(@swc/core@1.11.13)(esbuild@0.18.20)(webpack-cli@6.0.1) - webpack-cli: 6.0.1(webpack-bundle-analyzer@4.10.2)(webpack-dev-server@5.2.1)(webpack@5.99.7) + webpack: 5.99.7(esbuild@0.18.20)(webpack-cli@6.0.1) + webpack-cli: 6.0.1(webpack-dev-server@5.2.1)(webpack@5.99.7) dev: false /@webpack-cli/info@3.0.1(webpack-cli@6.0.1)(webpack@5.99.7): @@ -7702,8 +7712,8 @@ packages: webpack: ^5.82.0 webpack-cli: 6.x.x dependencies: - webpack: 5.99.7(@swc/core@1.11.13)(esbuild@0.18.20)(webpack-cli@6.0.1) - webpack-cli: 6.0.1(webpack-bundle-analyzer@4.10.2)(webpack-dev-server@5.2.1)(webpack@5.99.7) + webpack: 5.99.7(esbuild@0.18.20)(webpack-cli@6.0.1) + webpack-cli: 6.0.1(webpack-dev-server@5.2.1)(webpack@5.99.7) dev: false /@webpack-cli/serve@3.0.1(webpack-cli@6.0.1)(webpack-dev-server@5.2.1)(webpack@5.99.7): @@ -7717,8 +7727,8 @@ packages: webpack-dev-server: optional: true dependencies: - webpack: 5.99.7(@swc/core@1.11.13)(esbuild@0.18.20)(webpack-cli@6.0.1) - webpack-cli: 6.0.1(webpack-bundle-analyzer@4.10.2)(webpack-dev-server@5.2.1)(webpack@5.99.7) + webpack: 5.99.7(esbuild@0.18.20)(webpack-cli@6.0.1) + webpack-cli: 6.0.1(webpack-dev-server@5.2.1)(webpack@5.99.7) webpack-dev-server: 5.2.1(webpack-cli@6.0.1)(webpack@5.99.7) dev: false @@ -8392,7 +8402,7 @@ packages: dependencies: '@babel/core': 7.27.1 find-up: 5.0.0 - webpack: 5.99.7(@swc/core@1.11.13)(esbuild@0.18.20)(webpack-cli@6.0.1) + webpack: 5.99.7(esbuild@0.18.20)(webpack-cli@6.0.1) dev: false /babel-loader@9.2.1(@babel/core@7.27.1)(webpack@5.99.7): @@ -9584,7 +9594,7 @@ packages: postcss-modules-values: 4.0.0(postcss@8.4.47) postcss-value-parser: 4.2.0 semver: 7.5.4 - webpack: 5.99.7(@swc/core@1.11.13)(esbuild@0.18.20)(webpack-cli@6.0.1) + webpack: 5.99.7(esbuild@0.18.20)(webpack-cli@6.0.1) dev: false /css-loader@7.1.2(webpack@5.99.7): @@ -10631,7 +10641,7 @@ packages: enhanced-resolve: 5.18.1 eslint: 8.56.0 eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.18.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.18.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.18.0)(eslint@8.56.0) fast-glob: 3.3.2 get-tsconfig: 4.7.2 is-core-module: 2.16.1 @@ -11589,7 +11599,7 @@ packages: semver: 7.5.4 tapable: 2.2.1 typescript: 5.5.3 - webpack: 5.99.7(@swc/core@1.11.13)(esbuild@0.18.20)(webpack-cli@6.0.1) + webpack: 5.99.7(esbuild@0.18.20)(webpack-cli@6.0.1) dev: false /form-data-encoder@2.1.4: @@ -17156,7 +17166,7 @@ packages: peerDependencies: webpack: ^5.0.0 dependencies: - webpack: 5.99.7(@swc/core@1.11.13)(esbuild@0.18.20)(webpack-cli@6.0.1) + webpack: 5.99.7(esbuild@0.18.20)(webpack-cli@6.0.1) dev: false /stylelint-config-recommended@14.0.0(stylelint@16.5.0): @@ -17649,7 +17659,7 @@ packages: semver: 7.5.4 source-map: 0.7.4 typescript: 5.5.3 - webpack: 5.99.7(@swc/core@1.11.13)(esbuild@0.18.20)(webpack-cli@6.0.1) + webpack: 5.99.7(esbuild@0.18.20)(webpack-cli@6.0.1) dev: false /ts-node@10.9.2(@swc/core@1.11.13)(@types/node@16.18.68)(typescript@5.1.6): @@ -18402,7 +18412,7 @@ packages: mime-types: 2.1.35 range-parser: 1.2.1 schema-utils: 4.3.2 - webpack: 5.99.7(@swc/core@1.11.13)(esbuild@0.18.20)(webpack-cli@6.0.1) + webpack: 5.99.7(esbuild@0.18.20)(webpack-cli@6.0.1) dev: false /webpack-dev-middleware@7.4.2(webpack@5.99.7): @@ -18462,8 +18472,8 @@ packages: serve-index: 1.9.1 sockjs: 0.3.24 spdy: 4.0.2 - webpack: 5.99.7(@swc/core@1.11.13)(esbuild@0.18.20)(webpack-cli@6.0.1) - webpack-cli: 6.0.1(webpack-bundle-analyzer@4.10.2)(webpack-dev-server@5.2.1)(webpack@5.99.7) + webpack: 5.99.7(esbuild@0.18.20)(webpack-cli@6.0.1) + webpack-cli: 6.0.1(webpack-dev-server@5.2.1)(webpack@5.99.7) webpack-dev-middleware: 7.4.2(webpack@5.99.7) ws: 8.18.1 transitivePeerDependencies: @@ -18508,7 +18518,7 @@ packages: webpack: ^5.47.0 dependencies: tapable: 2.2.1 - webpack: 5.99.7(@swc/core@1.11.13)(esbuild@0.18.20)(webpack-cli@6.0.1) + webpack: 5.99.7(esbuild@0.18.20)(webpack-cli@6.0.1) webpack-sources: 2.3.1 dev: false patched: true