diff --git a/website/src/components/Extras/Frame/Frame.tsx b/website/src/components/Extras/Frame/Frame.tsx index 45b7fae97..4c4ed60ea 100644 --- a/website/src/components/Extras/Frame/Frame.tsx +++ b/website/src/components/Extras/Frame/Frame.tsx @@ -3,7 +3,38 @@ import styles from "./Frame.module.css"; import clsx from "clsx"; import OgImage from "../../OgImage"; -function extractImagesFromParagraph(children: React.ReactNode): { +// Adds className to img elements so it persists when medium-zoom clones them +function addClassToImages(children: React.ReactNode, className?: string): React.ReactNode { + if (!className) return children; + + if (React.isValidElement(children)) { + if (children.type === "img") { + return React.cloneElement(children, { + className: clsx(children.props.className, className), + }); + } + if (children.props?.children) { + return React.cloneElement(children, { + children: addClassToImages(children.props.children, className), + }); + } + } + + if (Array.isArray(children)) { + return children.map((child, i) => + React.isValidElement(child) + ? React.cloneElement(addClassToImages(child, className) as React.ReactElement, { key: i }) + : child + ); + } + + return children; +} + +function extractImagesFromParagraph( + children: React.ReactNode, + className?: string +): { content: React.ReactNode; single: boolean; } { @@ -15,7 +46,7 @@ function extractImagesFromParagraph(children: React.ReactNode): { const processedChildren = pChildren.map((child) => { if (React.isValidElement(child) && child.type === "figure") { return React.isValidElement(child) ? ( -
{child.props?.children}
+
{addClassToImages(child.props?.children, className)}
) : ( child ); @@ -27,7 +58,7 @@ function extractImagesFromParagraph(children: React.ReactNode): { if (React.isValidElement(pChildren) && pChildren.type === "figure") { return { - content:
{pChildren.props?.children}
, + content:
{addClassToImages(pChildren.props?.children, className)}
, single, }; } @@ -37,9 +68,9 @@ function extractImagesFromParagraph(children: React.ReactNode): { return { content: React.isValidElement(children) && children.type === "figure" ? ( -
{children.props?.children}
+
{addClassToImages(children.props?.children, className)}
) : ( - children + addClassToImages(children, className) ), single: false, }; @@ -77,12 +108,14 @@ export default function Frame({ caption, children, ogImage = false, + className, }: { caption?: string; children: React.ReactNode; ogImage?: boolean; + className?: string; }) { - const { content: processedChildren, single } = extractImagesFromParagraph(children); + const { content: processedChildren, single } = extractImagesFromParagraph(children, className); const ogImageSrc = ogImage ? extractFirstImageSrc(children) : undefined; return ( diff --git a/website/src/css/customizations/_site.scss b/website/src/css/customizations/_site.scss index 9e4928ad4..0dc6114c8 100644 --- a/website/src/css/customizations/_site.scss +++ b/website/src/css/customizations/_site.scss @@ -215,3 +215,10 @@ This file contains site-wide structural stylings. That includes sidebars, drop-d .pagination-nav__link:hover { background: color-mix(in srgb, var(--ifm-card-background-color) 60%, transparent); } + +// Diagram images: invert colors in dark mode for visibility +// Target both wrapper approach and direct img.diagram class (for medium-zoom cloned images) +[data-theme="dark"] .diagram img, +[data-theme="dark"] img.diagram { + filter: invert(1) hue-rotate(180deg); +}