diff --git a/src/common/YoutubeVideoEmbed.tsx b/src/common/YoutubeVideoEmbed.tsx
index d9fec8c29..08c535523 100644
--- a/src/common/YoutubeVideoEmbed.tsx
+++ b/src/common/YoutubeVideoEmbed.tsx
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: MIT
*/
-import { styled } from "styled-system/jsx";
+import { AspectRatio } from "styled-system/jsx";
export interface YoutubeVideo {
alt: string;
@@ -18,20 +18,20 @@ interface YoutubeVideoProps {
const YoutubeVideoEmbed = ({ alt, youtubeId }: YoutubeVideoProps) => {
return (
-
+
+
+
);
};
diff --git a/src/common/documentation-styles.ts b/src/common/documentation-styles.ts
index 04c161c25..b11e869b9 100644
--- a/src/common/documentation-styles.ts
+++ b/src/common/documentation-styles.ts
@@ -5,6 +5,26 @@
*/
import { css } from "styled-system/css";
+// Follows the pattern used by the AspectRatio component which uses a
+// percentage-padding spacer rather than the aspect-ratio property, which is
+// not supported in Safari 14. Only apply when --aspect-ratio-padding is set or
+// the wrapper collapses to zero height.
+export const runtimeAspectRatioClass = css({
+ position: "relative",
+ _before: {
+ content: '""',
+ display: "block",
+ height: 0,
+ paddingBottom: "var(--aspect-ratio-padding)",
+ },
+ "& > *": {
+ position: "absolute",
+ inset: 0,
+ width: "100%",
+ height: "100%",
+ },
+});
+
// A class rather than a style object: Panda can only statically extract
// styles written literally at the definition site, not objects spread into
// a css prop elsewhere.
diff --git a/src/common/imageUrlBuilder.ts b/src/common/imageUrlBuilder.ts
index 21ca7331d..e02992b84 100644
--- a/src/common/imageUrlBuilder.ts
+++ b/src/common/imageUrlBuilder.ts
@@ -17,12 +17,18 @@ export const imageUrlBuilder = unconfiguredImageUrlBuilder()
.dpr(window.devicePixelRatio ?? 1)
.quality(defaultQuality);
-export const getAspectRatio = (imageRef: string): string | undefined => {
+/**
+ * Percentage padding-bottom matching the image's aspect ratio, read from the
+ * dimensions embedded in a Sanity image reference. Used to reserve an image's
+ * height before it loads via a padding spacer rather than the aspect-ratio
+ * property, which Safari 14 does not support.
+ */
+export const getAspectRatioPadding = (imageRef: string): string | undefined => {
const dimensionsArr = imageRef.match(/\d+x\d+/g);
if (!dimensionsArr) {
return undefined;
}
const dimensions = dimensionsArr.join().split("x");
const [width, height] = dimensions.map((n: string) => Number(n));
- return (width / height).toString();
+ return `${(height / width) * 100}%`;
};
diff --git a/src/documentation/common/DocumentationContent.test.tsx b/src/documentation/common/DocumentationContent.test.tsx
index 953f253de..9bb0fa2f2 100644
--- a/src/documentation/common/DocumentationContent.test.tsx
+++ b/src/documentation/common/DocumentationContent.test.tsx
@@ -58,7 +58,7 @@ describe("DocumentationContent", () => {
];
const view = render();
expect(view.container.innerHTML).toMatchInlineSnapshot(
- `"

"`
+ `""`
);
});
});
diff --git a/src/documentation/common/DocumentationContent.tsx b/src/documentation/common/DocumentationContent.tsx
index d24cc7089..df5984499 100644
--- a/src/documentation/common/DocumentationContent.tsx
+++ b/src/documentation/common/DocumentationContent.tsx
@@ -8,7 +8,11 @@ import BlockContent from "@sanity/block-content-to-react";
import React, { ReactNode, useContext, useMemo } from "react";
import { RiExternalLinkLine } from "react-icons/ri";
import { Box, Stack } from "styled-system/jsx";
-import { getAspectRatio, imageUrlBuilder } from "../../common/imageUrlBuilder";
+import { runtimeAspectRatioClass } from "../../common/documentation-styles";
+import {
+ getAspectRatioPadding,
+ imageUrlBuilder,
+} from "../../common/imageUrlBuilder";
import { PortableText, SimpleImage } from "../../common/sanity";
import { useRouterState } from "../../router-hooks";
import {
@@ -200,23 +204,29 @@ const serializers = {
),
simpleImage: (props: SerializerNodeProps) => {
+ const padding = getAspectRatioPadding(props.node.asset._ref);
return (
- }
- alt={props.node.alt}
+
+ style={{ "--aspect-ratio-padding": padding } as React.CSSProperties}
+ >
+ }
+ alt={props.node.alt}
+ borderRadius="lg"
+ border="solid 1px"
+ borderColor="gray.300"
+ />
+
);
},
},
diff --git a/src/documentation/ideas/IdeasDocumentation.tsx b/src/documentation/ideas/IdeasDocumentation.tsx
index acd855d4d..44a3045e0 100644
--- a/src/documentation/ideas/IdeasDocumentation.tsx
+++ b/src/documentation/ideas/IdeasDocumentation.tsx
@@ -4,13 +4,19 @@
* SPDX-License-Identifier: MIT
*/
import { Link, Text } from "@microbit/ui";
-import { ReactNode, useCallback, useRef } from "react";
+import { CSSProperties, ReactNode, useCallback, useRef } from "react";
import { FormattedMessage, useIntl } from "react-intl";
-import { Grid, Stack } from "styled-system/jsx";
+import { Box, Grid, Stack } from "styled-system/jsx";
import AreaHeading from "../../common/AreaHeading";
-import { docStylesClass } from "../../common/documentation-styles";
+import {
+ docStylesClass,
+ runtimeAspectRatioClass,
+} from "../../common/documentation-styles";
import HeadedScrollablePanel from "../../common/HeadedScrollablePanel";
-import { getAspectRatio, imageUrlBuilder } from "../../common/imageUrlBuilder";
+import {
+ getAspectRatioPadding,
+ imageUrlBuilder,
+} from "../../common/imageUrlBuilder";
import { useResizeObserverContentRect } from "../../common/use-resize-observer";
import { Anchor, useRouterTabSlug } from "../../router-hooks";
import { useAnimationDirection } from "../common/documentation-animation-hooks";
@@ -75,6 +81,10 @@ const ActiveLevel = ({
const numCols =
!contentWidth || contentWidth > 1100 ? 3 : contentWidth > 550 ? 2 : 1;
if (activeIdea) {
+ // Runtime value derived from the image reference.
+ const imagePadding = activeIdea.image
+ ? getAspectRatioPadding(activeIdea.image.asset._ref)
+ : undefined;
return (
{activeIdea.image && (
- }
- alt=""
- borderTopRadius="lg"
+
+ maxWidth="100%"
+ className={imagePadding ? runtimeAspectRatioClass : undefined}
+ style={
+ {
+ "--aspect-ratio-padding": imagePadding,
+ } as CSSProperties
+ }
+ >
+ }
+ alt=""
+ borderTopRadius="lg"
+ />
+
)}
-
+
+
+
{
-
-
+
@@ -256,13 +263,12 @@ const MicroPythonSection = () => {
const intl = useIntl();
return (
-
+
-
+