diff --git a/src/app/ItemPage.js b/src/app/ItemPage.js
index 0e07eea..5d4edfb 100644
--- a/src/app/ItemPage.js
+++ b/src/app/ItemPage.js
@@ -19,6 +19,7 @@ import {
Wrap,
WrapItem,
Flex,
+ usePrefersReducedMotion,
} from "@chakra-ui/react";
import {
CheckIcon,
@@ -34,7 +35,7 @@ import { useQuery, useMutation } from "@apollo/client";
import { Link, useParams } from "react-router-dom";
import ItemPageLayout, { SubtleSkeleton } from "./ItemPageLayout";
-import { Delay, usePageTitle } from "./util";
+import { Delay, logAndCapture, usePageTitle } from "./util";
import {
itemAppearanceFragment,
petAppearanceFragment,
@@ -813,25 +814,80 @@ function CustomizeMoreButton({ speciesId, colorId, pose, itemId }) {
// The default background is good in light mode, but in dark mode it's a
// very subtle transparent white... make it a semi-transparent black, for
// better contrast against light-colored background items!
- const backgroundColor = useColorModeValue(undefined, "blackAlpha.600");
- const backgroundColorHover = useColorModeValue(undefined, "blackAlpha.700");
+ const backgroundColor = useColorModeValue(undefined, "blackAlpha.700");
+ const backgroundColorHover = useColorModeValue(undefined, "blackAlpha.900");
return (
-
- }
- position="absolute"
- top="2"
- right="2"
- size="sm"
- background={backgroundColor}
- _hover={{ backgroundColor: backgroundColorHover }}
- _focus={{ backgroundColor: backgroundColorHover }}
- boxShadow="sm"
- />
-
+
+ );
+}
+
+/**
+ * ExpandOnGroupHover starts at width=0, and expands to full width when a
+ * parent with role="group" gains hover or focus state.
+ */
+function ExpandOnGroupHover({ children, ...props }) {
+ const [measuredWidth, setMeasuredWidth] = React.useState(null);
+ const measurerRef = React.useRef(null);
+ const prefersReducedMotion = usePrefersReducedMotion();
+
+ React.useLayoutEffect(() => {
+ if (!measurerRef) {
+ // I don't think this is possible, but I'd like to know if it happens!
+ logAndCapture(
+ new Error(
+ `Measurer node not ready during effect. Transition won't be smooth.`
+ )
+ );
+ return;
+ }
+
+ if (measuredWidth != null) {
+ // Skip re-measuring when we already have a measured width. This is
+ // mainly defensive, to prevent the possibility of loops, even though
+ // this algorithm should be stable!
+ return;
+ }
+
+ const newMeasuredWidth = measurerRef.current.offsetWidth;
+ setMeasuredWidth(newMeasuredWidth);
+ }, [measuredWidth]);
+
+ return (
+
+
+ {children}
+
+
);
}