diff --git a/components/grid-cards-demo/src/components/DesignPicker/DesignPicker.module.scss b/components/grid-cards-demo/src/components/DesignPicker/DesignPicker.module.scss index e65176dc8..959942aad 100644 --- a/components/grid-cards-demo/src/components/DesignPicker/DesignPicker.module.scss +++ b/components/grid-cards-demo/src/components/DesignPicker/DesignPicker.module.scss @@ -350,6 +350,8 @@ } .logoRow { + /* Containing block for .fileInput — see there. */ + position: relative; flex: 1; display: flex; justify-content: flex-end; @@ -436,8 +438,15 @@ } } +/* Hidden picker, opened from the row's click. Pinned to .logoRow's origin: + without a positioned ancestor its static position (deep in the panel's + scrolled content) was measured against the viewport and escaped main's + overflow clip, so the document grew to ~930px and Safari let the whole + embed scroll off the top, leaving blank body below. */ .fileInput { position: absolute; + top: 0; + left: 0; width: 0; height: 0; opacity: 0; diff --git a/components/grid-cards-demo/src/data/design.ts b/components/grid-cards-demo/src/data/design.ts index eefb4208a..61e4830fd 100644 --- a/components/grid-cards-demo/src/data/design.ts +++ b/components/grid-cards-demo/src/data/design.ts @@ -347,10 +347,10 @@ export const initialDesign: CardDesign = { orientation: 'landscape', }; -/** The starting design for a theme: the card is ink on dark, white on light, - * so it reads against the stage it first appears on. */ +/** The starting design for a theme: the card is ink on light, white on dark, + * so it contrasts with the stage it first appears on. */ export function initialDesignFor(theme: 'light' | 'dark'): CardDesign { - return { ...initialDesign, color: theme === 'dark' ? DESIGN_SWATCHES[0].color : DESIGN_SWATCHES[1].color }; + return { ...initialDesign, color: theme === 'dark' ? DESIGN_SWATCHES[1].color : DESIGN_SWATCHES[0].color }; } /** Field-by-field equality of two designs (layouts and gradients by value). */ diff --git a/components/grid-cards-demo/src/hooks/useCardsDemoLogic.ts b/components/grid-cards-demo/src/hooks/useCardsDemoLogic.ts index 4c6fbfb79..c96643bb4 100644 --- a/components/grid-cards-demo/src/hooks/useCardsDemoLogic.ts +++ b/components/grid-cards-demo/src/hooks/useCardsDemoLogic.ts @@ -1,6 +1,6 @@ 'use client'; -import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; import type { ApiCall } from '@/data/flow'; import { ACTIONS, @@ -97,10 +97,13 @@ export function useCardsDemoLogic() { const theme = useThemeMode(); const [design, setDesign] = useState(initialDesign); // Until the visitor designs something, the card is the theme's default: - // ink on dark, white on light, following the theme if it changes. Reset - // (a design equal to a theme's default) hands it back to the theme. + // ink on light, white on dark, following the theme if it changes. Reset + // (a design equal to a theme's default) hands it back to the theme. A + // layout effect, so the swap lands before paint: the theme hydrates as + // light and flips to dark in the same commit, and the card must not show + // a frame of the other stage's color first. const designed = useRef(false); - useEffect(() => { + useLayoutEffect(() => { if (!designed.current) setDesign(initialDesignFor(theme)); }, [theme]); // The latest design, readable from callbacks without re-binding them.