Skip to content

fix(dev-mode): use animationend for modal scroll lock#1140

Open
AdeshDeshmukh wants to merge 1 commit intokitops-ml:mainfrom
AdeshDeshmukh:fix/settings-modal-animationend
Open

fix(dev-mode): use animationend for modal scroll lock#1140
AdeshDeshmukh wants to merge 1 commit intokitops-ml:mainfrom
AdeshDeshmukh:fix/settings-modal-animationend

Conversation

@AdeshDeshmukh
Copy link
Copy Markdown

Summary

This change fixes the Settings modal open lifecycle by replacing a timer-based body scroll lock with an animation-driven approach.

Problem

The modal previously used a hard-coded setTimeout(150) in onMounted to add overflow-hidden to document.body. This is brittle and can drift from actual animation timing.

Root cause

UI state synchronization depended on elapsed time instead of the real completion event of the modalShow animation.

Solution

  • Added onModalAnimationEnd(event: AnimationEvent)
  • Updated template to use @animationend="onModalAnimationEnd"
  • Removed timer logic from onMounted
  • Kept guard checks to ensure we only react to modalShow while modal is open

Validation

  • npx eslint src/components/SettingsModal.vue passes
  • Note: repo-wide frontend lint/type-check currently has unrelated pre-existing issues in other files

Risk

Low. Change is localized to one component (SettingsModal.vue) and only affects timing mechanism for applying body scroll lock.

Replace timer-based body scroll locking with an animationend handler tied to the modalShow animation. This removes timing race conditions and keeps modal behavior robust if animation duration changes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the dev-mode Settings modal lifecycle to lock body scrolling based on the modal’s actual CSS animation completion (animationend) instead of a hard-coded mount timer, improving synchronization with the UI transition.

Changes:

  • Replaced setTimeout(150)-based scroll locking in onMounted with an @animationend handler.
  • Removed the now-unused onMounted import and isVisible state.
  • Added onModalAnimationEnd(event: AnimationEvent) to apply overflow-hidden when the open animation completes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

}, 150)
})
const onModalAnimationEnd = (event: AnimationEvent) => {
if (event.animationName !== 'modalShow' || !isModalOpen.value) {
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

Because this component uses <style scoped>, Vue’s SFC compiler rewrites keyframe names to include a scope hash (e.g., modalShow-xxxx) and updates the animation: declarations accordingly. At runtime event.animationName will likely be the rewritten name, so comparing it to the literal 'modalShow' can prevent the scroll lock from ever being applied. Consider loosening the check (e.g., startsWith('modalShow')), checking event.target === event.currentTarget instead, or moving the @keyframes definition to an unscoped/global style block so the name stays stable.

Suggested change
if (event.animationName !== 'modalShow' || !isModalOpen.value) {
if (!isModalOpen.value || event.target !== event.currentTarget) {

Copilot uses AI. Check for mistakes.
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.

This is true, Vue's scoped style adds a hash to both the element and the classes (including keyframes) to scope it. Im not sure if the suggested change makes sense tho.

@AdeshDeshmukh did you try the prod build on this? you can do pnpm build and then pnpm preview to run and test the production build.

What i would try would probably be something like:

if (!isModaOpen.value || !event.animationName.includes('modalShow')) {

I think that could work on both dev and prod regardless of the scoped style.

Another solution is to just make the style a global style (ie. remove the scoped attribute from the <style>) but i dont remember if this class would collapse with some other class (i doubt it tho).

Copy link
Copy Markdown
Contributor

@javisperez javisperez left a comment

Choose a reason for hiding this comment

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

Check copilot's review, it makes sense.

@javisperez
Copy link
Copy Markdown
Contributor

I also noticed that there are unsigned commits, so that too needs to be fixed.

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.

3 participants