Skip to content

cards demo: keep the embed pinned in Safari, swap the theme default card - #1038

Merged
patcapulong merged 3 commits into
mainfrom
pat/cards-demo-embed-scroll
Sep 18, 2026
Merged

patcapulong merged 3 commits into
mainfrom
pat/cards-demo-embed-scroll

Conversation

@patcapulong

Copy link
Copy Markdown
Contributor

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"> is position: absolute with no positioned ancestor, so its static position (~900px down in the config panel's content) was measured against the viewport and escaped main's overflow: hidden. Safari's scroll chaining made that overflow reachable.

What

  • .logoRow becomes position: relative so it is the input's containing block (inside .group's overflow: clip), and the input is pinned to its origin. Row click still opens the picker via click(). Verified in WebKit: iframe scrollHeight now equals its viewport at 388/688/338px (was 930).
  • The theme default card is reversed: black on light, white on dark, so the starting card contrasts with its stage.

Notes

The three pre-existing tsc errors in AppShell.tsx / LiquidGlass.tsx (cornerShape typings) are untouched.

patcapulong and others added 2 commits September 18, 2026 16:05
… 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>
@vercel

vercel Bot commented Sep 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
grid-cards-demo Ready Ready Preview Sep 18, 2026 11:14pm UTC
grid-statements-demo Error Error Sep 18, 2026 11:14pm UTC
2 Skipped Deployments
Project Deployment Actions Updated
grid-flow-builder Ignored Ignored Preview Sep 18, 2026 11:14pm UTC
grid-wallet-demo Ignored Ignored Preview Sep 18, 2026 11:14pm UTC

Request Review

@patcapulong

Copy link
Copy Markdown
Contributor Author

@greptileai

@greptile-apps

greptile-apps Bot commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 5/5

The PR appears safe to merge with no outstanding correctness or repository-rule issues.

Findings

  1. P2 Dark mode flashes black
Fix with agent prompt
### Issue 1
components/grid-cards-demo/src/data/design.ts:undefined-353
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.

Summary

The PR prevents Safari scroll chaining from moving the cards-demo iframe by containing the hidden file input within its clipped logo row. It also reverses the theme-aware default card colors and synchronizes the default in a layout effect to avoid a pre-paint color flash.

  • Positions .logoRow as the hidden file input’s containing block and pins the input to its origin.
  • Uses a black default card on the light stage and a white default card on the dark stage.
  • Applies theme-driven default changes before paint.

Reviews (3) · Last reviewed commit: "cards demo: the theme's default card lan..."

* 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 };

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 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.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
@patcapulong

Copy link
Copy Markdown
Contributor Author

@greptileai

@patcapulong
patcapulong merged commit 39baddf into main Sep 18, 2026
9 of 10 checks passed
@patcapulong
patcapulong deleted the pat/cards-demo-embed-scroll branch September 18, 2026 23:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants