From 92e2f7f63e530ab02898913637fe7db6b4f716d6 Mon Sep 17 00:00:00 2001 From: Pat Capulong Date: Fri, 18 Sep 2026 16:05:37 -0700 Subject: [PATCH 1/3] cards demo: the hidden file input no longer lets the embed scroll off the top MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The design picker's file input is position: absolute with no positioned ancestor, so its static position (deep in the config panel's scrolled content, ~900px down) was measured against the viewport and escaped main's overflow clip. The document grew to ~930px regardless of the iframe's height, and Safari's scroll chaining let a trackpad scroll past the config panel drag the whole layout up under the docs navbar, leaving blank body below — worse the shorter the window. Give .logoRow position: relative so it is the input's containing block (inside .group's overflow: clip), and pin the input to its origin. Co-authored-by: Cursor --- .../src/components/DesignPicker/DesignPicker.module.scss | 9 +++++++++ 1 file changed, 9 insertions(+) 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; From 83a3ac2634b305bdb928b12d64dd295298f7604e Mon Sep 17 00:00:00 2001 From: Pat Capulong Date: Fri, 18 Sep 2026 16:05:37 -0700 Subject: [PATCH 2/3] cards demo: the default card is black on light, white on dark Reverse the theme default so the starting card contrasts with the stage it first appears on. Co-authored-by: Cursor --- components/grid-cards-demo/src/data/design.ts | 6 +++--- components/grid-cards-demo/src/hooks/useCardsDemoLogic.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) 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..57595f3fb 100644 --- a/components/grid-cards-demo/src/hooks/useCardsDemoLogic.ts +++ b/components/grid-cards-demo/src/hooks/useCardsDemoLogic.ts @@ -97,7 +97,7 @@ 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 + // 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. const designed = useRef(false); useEffect(() => { From ba8ea152b54bd8a3df9e6550432141cea35df7bc Mon Sep 17 00:00:00 2001 From: Pat Capulong Date: Fri, 18 Sep 2026 16:14:06 -0700 Subject: [PATCH 3/3] cards demo: the theme's default card lands before paint The theme hydrates as light and flips to dark in the same commit, but the design followed it in a passive effect, so a dark load could paint one frame of the light stage's black card before turning white. A layout effect flushes the swap before paint. Co-authored-by: Cursor --- .../grid-cards-demo/src/hooks/useCardsDemoLogic.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/components/grid-cards-demo/src/hooks/useCardsDemoLogic.ts b/components/grid-cards-demo/src/hooks/useCardsDemoLogic.ts index 57595f3fb..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, @@ -98,9 +98,12 @@ export function useCardsDemoLogic() { const [design, setDesign] = useState(initialDesign); // Until the visitor designs something, the card is the theme's default: // 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 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.