Repository navigation
fix(GlassPanes): upgrade off flat SSR fallback after mount #26
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -40,7 +40,7 @@ | |
| */ | ||
|
|
||
| import type { CSSProperties, HTMLAttributes } from "react"; | ||
| import { useEffect, useMemo } from "react"; | ||
| import { useEffect, useMemo, useState } from "react"; | ||
| import { resolveColor, useInView, usePrefersReducedMotion } from "../utils"; | ||
| import { GOLDEN_RATIO_FRAC, MIN_SPEED } from "../utils/constants"; | ||
| import { supportsBackdropFilter } from "../utils/featureDetect"; | ||
|
|
@@ -66,8 +66,8 @@ const MAX_COUNT = 8; | |
| const PANE_ANGLE_DEG = 8; | ||
|
|
||
| /** Pane blur depth range, px — depth is the point: far panes frost lightly, near panes heavily. */ | ||
| const MIN_BLUR_PX = 8; | ||
| const BLUR_SPAN_PX = 18; // 8-26px | ||
| const MIN_BLUR_PX = 4; | ||
| const BLUR_SPAN_PX = 8; // 4-12px | ||
|
|
||
| /** How far past each container edge the slot row extends, % of width — | ||
| * absorbs the sideways shift the shared rotation causes at the container's | ||
|
|
@@ -101,7 +101,7 @@ const PANE_SHADOW = | |
| const KEYFRAMES_CSS = ` | ||
| @keyframes ${SLIDE_KEYFRAMES_NAME} { | ||
| 0% { transform: rotate(var(--fluidkit-panes-rot)) translate(0%, 0%); } | ||
| 50% { transform: rotate(var(--fluidkit-panes-rot)) translate(var(--fluidkit-panes-drift), -2.5%); } | ||
| 50% { transform: rotate(var(--fluidkit-panes-rot)) translate(var(--fluidkit-panes-drift), -3.5%); } | ||
| 100% { transform: rotate(var(--fluidkit-panes-rot)) translate(0%, 0%); } | ||
| } | ||
| `; | ||
|
|
@@ -133,7 +133,7 @@ function layoutPanes(colors: string[] | undefined, count: number, speed: number) | |
| // covers every point regardless of count. | ||
| widthPct: spacing + MIN_OVERLAP_PCT + frac * OVERLAP_SPAN_PCT, | ||
| blurPx: Math.round(MIN_BLUR_PX + frac * BLUR_SPAN_PX), | ||
| driftPct: `${i % 2 === 0 ? 4 : -4}%`, | ||
| driftPct: `${i % 2 === 0 ? 13 : -13}%`, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔴 Tripled pane drift breaks the full-surface coverage guarantee, exposing gaps at default settings The pane slide distance is tripled ( Impact: At the default count of 3, and at count=2, visible gaps flash between the glass panes mid-animation, breaking the documented "every point is always behind at least one pane" guarantee. Coverage-guarantee math showing the gapThe overlap comment at CSS count=3 (default):
count=2:
The old ±4% drift kept the worst-case gap opening well under the 12% minimum overlap (e.g., count=2: 0.04×70×2 = 5.6% < 12%). The fix is either to reduce the drift back toward the old value, or to increase Prompt for agentsWas this helpful? React with 👍 or 👎 to provide feedback. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Missing changelog entry for user-visible behavior changes Several user-facing behaviors are changed — pane drift amplitude, blur range, and SSR/hydration rendering — but no Impact: Users upgrading the library won't know the glass-pane animation and SSR behavior changed. Was this helpful? React with 👍 or 👎 to provide feedback. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When consumers pass Useful? React with 👍 / 👎. |
||
| periodS, | ||
| // Negative delay starts each pane mid-cycle instead of all in-phase | ||
| // at 0%, so panes don't visibly synchronize. | ||
|
|
@@ -159,8 +159,20 @@ export function GlassPanes({ | |
| style, | ||
| ...rest | ||
| }: GlassPanesProps) { | ||
| const prefersReducedMotion = usePrefersReducedMotion(); | ||
| const rawPrefersReducedMotion = usePrefersReducedMotion(); | ||
| const { ref, inView } = useInView<HTMLDivElement>(); | ||
|
|
||
| // Feature detection (reduced-motion + backdrop-filter support) is | ||
| // static-safe during SSR and the first client render — it assumes reduced | ||
| // motion and no backdrop-filter. Without re-checking after mount the panes | ||
| // stay stuck on that flat, non-animated fallback in the browser. Flip a | ||
| // mounted flag in an effect so we re-render once with the real values. | ||
| const [mounted, setMounted] = useState(false); | ||
| useEffect(() => { | ||
| setMounted(true); | ||
| }, []); | ||
|
|
||
| const prefersReducedMotion = mounted ? rawPrefersReducedMotion : true; | ||
|
Comment on lines
+170
to
+175
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Missing test updates for changed animation and SSR behavior The PR changes drift amplitude, blur depth range, and adds a new SSR-to-client hydration path (the Impact: Regressions in the SSR fallback upgrade path or the new drift/blur values won't be caught by CI. Was this helpful? React with 👍 or 👎 to provide feedback. |
||
| const animating = !prefersReducedMotion && inView; | ||
|
|
||
| useEffect(() => { | ||
|
|
@@ -170,8 +182,10 @@ export function GlassPanes({ | |
| const clampedSpeed = Math.max(speed, MIN_SPEED); | ||
| const clampedCount = Math.min(MAX_COUNT, Math.max(1, Math.round(count))); | ||
| // Guarded pure function (never throws, false in SSR) — same detector the | ||
| // engine's resolveMaterial uses, safe to call during render. | ||
| const frosted = supportsBackdropFilter(); | ||
| // engine's resolveMaterial uses, safe to call during render. Gate on | ||
| // `mounted` so SSR/first render stays on the flat fallback and the client | ||
| // upgrades to frosted glass after mount. | ||
| const frosted = mounted && supportsBackdropFilter(); | ||
|
Comment on lines
182
to
+188
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔍 Extra render cycle from mounted-state hydration gate The (Refers to lines 170-188) Was this helpful? React with 👍 or 👎 to provide feedback. |
||
| const panes = useMemo( | ||
| () => layoutPanes(colors, clampedCount, clampedSpeed), | ||
| [colors, clampedCount, clampedSpeed] | ||
|
Comment on lines
189
to
191
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📝 Info: useMemo dependency on colors array identity may cause unnecessary recalculations after mount The (Refers to lines 189-192) Was this helpful? React with 👍 or 👎 to provide feedback. |
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔍 Blur depth range halved — significantly reduced visual depth differentiation
The blur range was reduced from 8–26px (
MIN_BLUR_PX=8,BLUR_SPAN_PX=18) to 4–12px (MIN_BLUR_PX=4,BLUR_SPAN_PX=8atsrc/components/GlassPanes.tsx:69-70). The component's core visual premise — documented in the header comment at lines 4-6 — is that each pane gets a DIFFERENT blur strength so overlaps read as physical depth. The old 18px span gave a 3.25× ratio between the lightest and heaviest frost (8px vs 26px); the new 8px span gives only a 3× ratio (4px vs 12px) with much less absolute difference. At 4px minimum blur, the lightest-frosted pane may not read as frosted at all on high-DPI screens. This is a deliberate visual tuning choice but worth confirming it still achieves the intended depth layering effect in practice.Was this helpful? React with 👍 or 👎 to provide feedback.