Skip to content

fix(ui): portal fullscreen lyrics so opacity tween doesn't bleed through#140

Merged
InstaZDLL merged 2 commits into
mainfrom
fix/fullscreen-lyrics-portal
May 24, 2026
Merged

fix(ui): portal fullscreen lyrics so opacity tween doesn't bleed through#140
InstaZDLL merged 2 commits into
mainfrom
fix/fullscreen-lyrics-portal

Conversation

@InstaZDLL

@InstaZDLL InstaZDLL commented May 24, 2026

Copy link
Copy Markdown
Owner

Bug

Switching from the immersive Now Playing view (`FullscreenNowPlaying`) to the fullscreen lyrics view via the lyrics button caused a ~500 ms glitch where the regular app UI (search bar, sidebar, top bar) was visible through the new overlay. The reverse direction (lyrics → immersive) was unaffected.

Root cause

`LyricsPanel` is a `motion.aside` with `initial={{ width: 0, opacity: 0 }}` (LyricsPanel.tsx#L180-L186), animating to full opacity over a spring tween (~500 ms). `FullscreenLyrics` was rendered as a descendant of that motion subtree (LyricsPanel.tsx#L329), so:

  • Immersive → Lyrics: `activeRightPanel` flips to `"lyrics"`, `LyricsPanel` mounts and starts its opacity-from-0 tween, dragging `FullscreenLyrics` along with it. The overlay is transparent at first paint; the app UI shows through. `FullscreenNowPlaying` (sibling under the always-mounted `PlayerBar`) unmounts instantly without an exit tween.
  • Lyrics → Immersive: `LyricsPanel` is already at opacity 1; only `fullscreenOverlay` changes. `FullscreenLyrics` unmounts, `FullscreenNowPlaying` mounts and runs its own 300 ms `animate-fade-in` against a stable parent. No bleed-through.

Fix

Portal `FullscreenLyrics` to `document.body` via `react-dom/createPortal`, so the rendered subtree sits next to `` instead of inside the animated `motion.aside`. The panel still owns the lyrics fetch / parse state — only the visual subtree moves. Same pattern already used in `QueuePanel.tsx` for the dnd-kit drag overlay.

Test plan

  • `bun run typecheck` passes.
  • `bun run lint` passes.
  • Click the cover thumbnail in the player bar → immersive Now Playing opens.
  • From immersive, click the Lyrics button → fullscreen lyrics overlay appears without any flash of the app UI behind it.
  • From fullscreen lyrics, click the cover thumbnail (or Maximize2 icon) → immersive Now Playing returns smoothly.
  • Open the lyrics side panel (via the player-bar Lyrics button while the lyrics overlay isn't open), then click Maximize2 → fullscreen lyrics still mounts cleanly (panel already opaque).

Summary by CodeRabbit

  • Refactor
    • Amélioration de l'affichage des écrans plein écran (paroles et lecture) : meilleure isolation visuelle et stabilité, fond flouté ou dégradé avec voile sombre, base opaque pour éviter les artefacts, et transitions d'apparition réajustées pour des animations plus cohérentes. L'overlay est désormais monté à la racine du document pour un rendu plus stable.

Review Change Stack

LyricsPanel is a `motion.aside` that animates `opacity: 0 -> 1` on
mount. FullscreenLyrics was rendered as its descendant, so the
opacity tween applied to the whole subtree: switching from the
immersive Now Playing view to fullscreen lyrics flashed the regular
app UI (search bar, sidebar) through the still-transparent overlay
for the duration of the spring animation. The reverse direction was
fine because LyricsPanel was already fully opaque by then.

Portal the overlay to `document.body` so it sits as a sibling of the
root and renders at full opacity from the first paint. The panel
still owns the lyrics fetch + parse state — only the rendered subtree
moves.
@coderabbitai

coderabbitai Bot commented May 24, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 472494ad-8384-4a52-90a4-815821aaec8f

📥 Commits

Reviewing files that changed from the base of the PR and between 24ac19a and 7491ae8.

📒 Files selected for processing (2)
  • src/components/player/FullscreenLyrics.tsx
  • src/components/player/FullscreenNowPlaying.tsx

📝 Walkthrough

Walkthrough

Le rendu de l'overlay lyrics passe à un React Portal monté sur document.body ; parallèlement, les classes d'animation et les fonds floutés sont déplacés des conteneurs externes vers des wrappers absolus dans FullscreenLyrics et FullscreenNowPlaying.

Changes

Overlay UI & mounting

Layer / File(s) Summary
Portal mounting for FullscreenLyrics
src/components/layout/LyricsPanel.tsx
Import de createPortal et migration du rendu de FullscreenLyrics vers un portal monté sur document.body, conservant les mêmes props et callbacks.
FullscreenLyrics background wrapper and fallback
src/components/player/FullscreenLyrics.tsx
Le conteneur principal devient opaque (bg-zinc-950), l'animation animate-fade-in est déplacée vers un sous-wrapper absolu contenant l'arrière-plan flouté ; le fallback artwork/degradé et la voile noire sont réorganisés.
FullscreenNowPlaying animation repositioning
src/components/player/FullscreenNowPlaying.tsx
Le conteneur externe reçoit bg-zinc-950, l'animation de fade-in est déplacée vers le bloc de fond absolu et le Foreground reçoit désormais animate-fade-in.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • InstaZDLL/WaveFlow#124: Modifie également la couche UI de fullscreen lyrics (FullscreenLyrics overlay/header behavior).

Suggested labels

size: m

Un overlay qui s'envole, loin du nid,
Vers le corps du document il va vite,
Fond flou et voile noire en coulisse,
L'animation trouve sa nouvelle piste,
Les paroles brillent — détachées et nettes.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed Le titre suit les Conventional Commits et décrit précisément la correction : utiliser createPortal pour éviter que la tween d'opacité n'affecte le rendu de FullscreenLyrics.
Description check ✅ Passed La description inclut le problème, la cause racine, la solution et le plan de test avec étapes concrètes. Tous les éléments requis du template sont présents et bien remplis.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/fullscreen-lyrics-portal

Comment @coderabbitai help to get the list of available commands and usage tips.

@InstaZDLL InstaZDLL added scope: frontend React/Vite frontend (src/) type: fix Bug fix size: s 10-50 lines labels May 24, 2026
@InstaZDLL InstaZDLL self-assigned this May 24, 2026
… flashing

`animate-fade-in` was on the outer wrapper of both FullscreenNowPlaying
and FullscreenLyrics, so during the 300 ms opacity 0 -> 1 ramp the
overlay itself was semi-transparent and the home view (search bar,
cards, sidebar) showed through.

Move the keyframe down one level: outer wrapper paints solid
`bg-zinc-950` from frame 1 (blocks the page underneath), and the
blurred-artwork backdrop + foreground content each fade in on top.
The user-facing transition still looks like a 300 ms fade — without the
ghost-of-home-view flash behind it.
@InstaZDLL InstaZDLL added size: m 50-200 lines and removed size: s 10-50 lines labels May 24, 2026
@InstaZDLL InstaZDLL merged commit 7097318 into main May 24, 2026
13 checks passed
@InstaZDLL InstaZDLL deleted the fix/fullscreen-lyrics-portal branch May 24, 2026 23:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

scope: frontend React/Vite frontend (src/) size: m 50-200 lines type: fix Bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant