8

I am trying to use sample tailwindui.com components. They have the animation part defined in the comments, but I am not able to make out how I should those in my code. I am using plain html/js and would prefer to not use any framework/lib for this.

Here I am trying to show/hide a modal dialog. The animation doe for the background overlay is given as

 <!--
      Background overlay, show/hide based on modal state.

      Entering: "ease-out duration-300"
        From: "opacity-0"
        To: "opacity-100"
      Leaving: "ease-in duration-200"
        From: "opacity-100"
        To: "opacity-0"
    --> 

I have no idea how to encode this information to the background overlay div.

<div id="myModal" class="hidden fixed z-10 inset-0 overflow-y-auto" aria-labelledby="modal-title" role="dialog" aria-modal="true">
  <div class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
    <!--
      Background overlay, show/hide based on modal state.

      Entering: "ease-out duration-300"
        From: "opacity-0"
        To: "opacity-100"
      Leaving: "ease-in duration-200"
        From: "opacity-100"
        To: "opacity-0"
    -->
    <div class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" aria-hidden="true"></div>

Here is what I have tried so far;

<style>
  .modalEntry {animation:modal-in 1s;}  
  .modalExit {animation:modal-in 1s;}

@keyframes modal-in {
    from {opacity-0;}
    to {opacity-100;}
}
@keyframes modal-out {
    from {opacity-100;}
    to {opacity-0;}
}
</style>

<button class="border border-red-500" onclick="toggleModal()">Toggle modal</button>

<!-- This example requires Tailwind CSS v2.0+ -->
<div id="myModal" class="hidden fixed z-10 inset-0 overflow-y-auto" aria-labelledby="modal-title" role="dialog" aria-modal="true">
  <div class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
    <!--
      Background overlay, show/hide based on modal state.

      Entering: "ease-out duration-300"
        From: "opacity-0"
        To: "opacity-100"
      Leaving: "ease-in duration-200"
        From: "opacity-100"
        To: "opacity-0"
    -->
    <div class="fixed modalEntry inset-0 bg-gray-500 bg-opacity-75 transition-opacity" aria-hidden="true"></div>

    <!-- This element is to trick the browser into centering the modal contents. -->
    <span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">&#8203;</span>

    <!--
      Modal panel, show/hide based on modal state.

      Entering: "ease-out duration-300"
        From: "opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
        To: "opacity-100 translate-y-0 sm:scale-100"
      Leaving: "ease-in duration-200"
        From: "opacity-100 translate-y-0 sm:scale-100"
        To: "opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
    -->
    <div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full">
      <div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
        <div class="sm:flex sm:items-start">
          <div class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10">
            <!-- Heroicon name: outline/exclamation -->
            <svg class="h-6 w-6 text-red-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
            </svg>
          </div>
          <div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
            <h3 class="text-lg leading-6 font-medium text-gray-900" id="modal-title">Deactivate account</h3>
            <div class="mt-2">
              <p class="text-sm text-gray-500">Are you sure you want to deactivate your account? All of your data will be permanently removed. This action cannot be undone.</p>
            </div>
          </div>
        </div>
      </div>
      <div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
        <button onclick="toggleModal()" type="button" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm">Deactivate</button>
        <button onclick="toggleModal()" type="button" class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm">Cancel</button>
      </div>
    </div>
  </div>
</div>

<script>
  function toggleModal () {
        document.getElementById("myModal").classList.toggle("hidden");
  }
</script>

I have put the same in a codepen for the ease of testing. https://codepen.io/rishavs/pen/BadMQaN?editors=1000

Rishav Sharan
  • 2,763
  • 8
  • 39
  • 55

2 Answers2

1

i`m using jquery on a legacy php project, perhaps you could get the idea from that.

First add an id to slide-over-title, and suggested animations.

<div class="fixed inset-0 overflow-hidden z-[2000] bg-gray-200 transform transition ease-in-out duration-500 sm:duration-700 translate-x-full" aria-labelledby="slide-over-title" id="slide-over-title" role="dialog" aria-modal="true">

then create simple "toggle" function to use anywhere

function toggleSlider() {
    if($("#slide-over-title").hasClass("translate-x-full")) {
    $("#slide-over-title").removeClass("translate-x-full");
    $("#slide-over-title").addClass("translate-x-0");
    } else  {
    $("#slide-over-title").addClass("translate-x-full");
    $("#slide-over-title").removeClass("translate-x-0");  
    }
}

On the snippet i am using the tailwind cdn, but for production you should use the tailwind preprocesor or cli to generate an optimized css file

function toggleSlider() {
        if($("#slide-over-title").hasClass("translate-x-full")) {
        $("#slide-over-title").removeClass("translate-x-full");
        $("#slide-over-title").addClass("translate-x-0");
        } else  {
        $("#slide-over-title").addClass("translate-x-full");
        $("#slide-over-title").removeClass("translate-x-0");  
        }
    }
<button onclick="toggleSlider()">Toggle Slider</button>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdn.tailwindcss.com"></script>
<div class="fixed inset-0 overflow-hidden z-[2000] bg-gray-200 transform transition ease-in-out duration-500 sm:duration-700 translate-x-full" aria-labelledby="slide-over-title" id="slide-over-title" role="dialog" aria-modal="true">
  <div class="absolute inset-0 overflow-hidden">
    
    <div class="absolute inset-0" aria-hidden="true"></div>

    <div class="pointer-events-none fixed inset-y-0 right-0 flex max-w-full pl-10 sm:pl-16">
      <!--
        Slide-over panel, show/hide based on slide-over state.

        Entering: "transform transition ease-in-out duration-500 sm:duration-700"
          From: "translate-x-full"
          To: "translate-x-0"
        Leaving: "transform transition ease-in-out duration-500 sm:duration-700"
          From: "translate-x-0"
          To: "translate-x-full"
      -->
      <div class="pointer-events-auto w-screen max-w-2xl">
        <div class="flex h-full flex-col overflow-y-scroll bg-white py-6 shadow-xl">
          <div class="px-4 sm:px-6">
            <div class="flex items-start justify-between">
              <h2 class="text-lg font-medium text-gray-900" id="slide-over-title">Panel title</h2>
              <div class="ml-3 flex h-7 items-center">
                <button type="button" class="rounded-md bg-white text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2" onclick="toggleSlider()">
                  <span class="sr-only">Close panel</span>
                  <!-- Heroicon name: outline/x -->
                  <svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
                  </svg>
                </button>
              </div>
            </div>
          </div>
          <div class="relative mt-6 flex-1 px-4 sm:px-6">
            <!-- Replace with your content -->
            <div class="absolute inset-0 px-4 sm:px-6">
              <div class="h-full border-2 border-dashed border-gray-200" aria-hidden="true"></div>
            </div>
            <!-- /End replace -->
          </div>
        </div>
      </div>
    </div>
  </div>
</div>
0

The easiest way to apply the transition effects without using Vue/React on tailwindui is to use Alpine.js, as explained in the docs:

  <div
    x-show="isOpen"
    x-transition:enter="transition ease-out duration-100 transform"
    x-transition:enter-start="opacity-0 scale-95"
    x-transition:enter-end="opacity-100 scale-100"
    x-transition:leave="transition ease-in duration-75 transform"
    x-transition:leave-start="opacity-100 scale-100"
    x-transition:leave-end="opacity-0 scale-95"
    class="origin-top-right absolute right-0 mt-2 w-56 rounded-md shadow-lg"
  >

If you want to write it by yourself, then you need to make sure that you first change the display (block/hidden) then you need to wait a frame before you apply the actual transition class. This is required, as changing the status from display:none to display:block does not trigger css transition animations (see Transitions on the CSS display property). Also you need to make sure that you hide your element after the transition has taken place (so ona duration-200 class, you would need to wait 200ms). Otherwise it would be hidden immediately without transition animation.

You could use "visibility" instead of "display", but that would not be a good option for the module, as the background goes over your entire page and makes other buttons unclickable.

To wait one frame before next repaint, you may use the window.requestAnimationFrame() method. Another possibility to implement it is using the setTimeout method, as done at https://stackoverflow.com/a/40447192

I actually found a github repository which implements only this x-transition from alpine with the aim to be used for tailwind-ui transitions https://github.com/CaptainCodeman/x-transition and its ~1kb gzipped, but alpine.js is ~7kb gzipped with a ton of other functionality, so I think it makes sense to go directly with alpine.js

In addition, when you implement it by your own, or use alpine.js, you still need to make sure that you meet the wai aria practices.

Adam
  • 25,960
  • 22
  • 158
  • 247