Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I CSS transition a modal dialog element when it opens?

I want to apply a transition to dialog elements when they are opened, but my transition attribute doesn't seem to be having any effect. Here's my code:

const dialog = document.querySelector("dialog");

document.querySelector("#open-button").addEventListener("click", () => {
  dialog.showModal();
});
document.querySelector("#close-button").addEventListener("click", () => {
  dialog.close();
});
button {
  display: block;
}

dialog {
  position: absolute;
  top: 50px;
  margin: auto;
  padding: 0;
  width: 50%;
  height: 50%;
  background-color: red;
  opacity: 0;
  -webkit-transition: opacity 2s ease-in, background-color 2s ease-in;
  -o-transition: opacity 2s ease-in, background-color 2s ease-in;
  transition: opacity 2s ease-in, background-color 2s ease-in;
}

dialog[open] {
  background-color: green;
  opacity: 1;
}
<button id="open-button">Open Dialog Element</button>

<dialog>
  <button id="close-button">Close Dialog Element</button>
</dialog>

My intent is to slowly fade the dialog into view as its background visibly changes from red to green.

Instead, the rules of dialog[open] are immediately applied? The MDN article doesn't mention transitions or animations anywhere, implying this should work as any other element.

I am testing this in Firefox on Windows 10.

like image 913
Daniel Kaplan Avatar asked Oct 12 '25 12:10

Daniel Kaplan


1 Answers

Your dialog transitions from display:none to display:block and that ruins transition. The idea was to add dialog { display:block; } but it turned out bad because the dialog is just hidden and reacts to the keyboard TAB navigation for example... So we need to keep a closed dialog display:none.

So a couple of solutions:

  1. You could use animation which I like since it's a pure CSS solution:
dialog[open] {
  animation: fadein 2s ease-in forwards;
}

@keyframes fadein{
  0%{
    opacity:0;
  }
  100%{
    opacity:1;
    background-color: green;
  }
}

const dialog = document.querySelector("dialog");

document.querySelector("#open-button").addEventListener("click", () => {
  dialog.showModal();
});
document.querySelector("#close-button").addEventListener("click", () => {
  dialog.close();
});
button {
  display: block;
}

dialog {
  position: absolute;
  top: 50px;
  margin: auto;
  padding: 0;
  width: 50%;
  height: 50%;
  background-color: red;
  opacity: 0;
}

dialog[open] {
  animation: fadein 2s ease-in forwards;
}

@keyframes fadein{
  0%{
    opacity:0;
  }
  100%{
    opacity:1;
    background-color: green;
  }
}
<button id="open-button">Open Dialog Element</button>

<dialog>
  <button id="close-button">Close Dialog Element</button>
</dialog>
  1. You could also add a CSS class after the opening with setTimeout to allow the DOM to be re-rendered and remove it on the closing with your transition left intact.
setTimeout(()=>dialog.classList.add('open'));
dialog.addEventListener('close', () => dialog.classList.remove('open'));

The extra benefit of this approach that you can close the dialog with the transition also:

document.querySelector("#close-button").addEventListener("click", () => {
  
  const close = e => e.propertyName === 'background-color' && 
    dialog.close() ||
    dialog.removeEventListener('transitionend', close);
    
  dialog.addEventListener('transitionend', close);
  dialog.classList.remove('open');
  
});

const dialog = document.querySelector("dialog");

dialog.addEventListener('close', () => dialog.classList.remove('open'));

document.querySelector("#open-button").addEventListener("click", () => {
  dialog.showModal();
  setTimeout(()=>dialog.classList.add('open'));
});

document.querySelector("#close-button").addEventListener("click", () => {
  
  const close = e => e.propertyName === 'background-color' && 
    dialog.close() ||
    dialog.removeEventListener('transitionend', close);
    
  dialog.addEventListener('transitionend', close);
  dialog.classList.remove('open');
  
});
button {
  display: block;
}

dialog {
  position: absolute;
  top: 50px;
  margin: auto;
  padding: 0;
  width: 50%;
  height: 50%;
  background-color: red;
  opacity: 0;
  -webkit-transition: opacity 2s ease-in, background-color 2s ease-in;
  -o-transition: opacity 2s ease-in, background-color 2s ease-in;
  transition: opacity 2s ease-in, background-color 2s ease-in;
}

dialog.open {
  background-color: green;
  opacity: 1;
}
<button id="open-button">Open Dialog Element</button>

<dialog>
  <button id="close-button">Close Dialog Element</button>
</dialog>
like image 89
Alexander Nenashev Avatar answered Oct 14 '25 12:10

Alexander Nenashev