cards demo: keep the embed pinned in Safari, swap the theme default card - #1038
Conversation
… the top 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 <cursoragent@cursor.com>
Reverse the theme default so the starting card contrasts with the stage it first appears on. Co-authored-by: Cursor <cursoragent@cursor.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
2 Skipped Deployments
|
|
| * 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 }; |
There was a problem hiding this comment.
On a dark-mode load, the card state still starts from the always-black initialDesign, while this change makes the dark-theme default white. The effect applies that white default only after the initial render, so the card can briefly appear black before switching to white. Initializing the state from the active theme, or applying the default before paint, would avoid this visible flash.
Prompt To Fix With AI
This is a comment left during a code review.
Path: components/grid-cards-demo/src/data/design.ts
Line: 353
Comment:
**Dark mode flashes black**
On a dark-mode load, the card state still starts from the always-black `initialDesign`, while this change makes the dark-theme default white. The effect applies that white default only after the initial render, so the card can briefly appear black before switching to white. Initializing the state from the active theme, or applying the default before paint, would avoid this visible flash.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.There was a problem hiding this comment.
@greptileai Fixed in ba8ea15: the theme→design sync is now a useLayoutEffect, so on a dark load the swap to the white default flushes before paint instead of in a passive effect after the first frame. A lazy useState initializer wouldn't help here — useThemeMode hydrates with the server snapshot (light) and flips to dark in the same commit, so the initializer would still see light.
There was a problem hiding this comment.
Thanks for the clarification. Verified in ba8ea15: using useLayoutEffect addresses the flash before paint, and the hydrated theme snapshot means a lazy initializer would not solve it. Resolving this comment.
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 <cursoragent@cursor.com>
Why
On docs.lightspark.com/cards/demo in Safari, a trackpad scroll past the bottom of the left config panel dragged the whole playground up under the docs navbar and left blank space below — worse the shorter the window.
The docs page and its fixed iframe host were fine. Inside the iframe, the demo's document was ~930px tall regardless of the iframe's height: the design picker's hidden
<input type="file">isposition: absolutewith no positioned ancestor, so its static position (~900px down in the config panel's content) was measured against the viewport and escapedmain'soverflow: hidden. Safari's scroll chaining made that overflow reachable.What
.logoRowbecomesposition: relativeso it is the input's containing block (inside.group'soverflow: clip), and the input is pinned to its origin. Row click still opens the picker viaclick(). Verified in WebKit: iframescrollHeightnow equals its viewport at 388/688/338px (was 930).Notes
The three pre-existing
tscerrors inAppShell.tsx/LiquidGlass.tsx(cornerShapetypings) are untouched.