neilberkman

neilberkman

I’m trying to implement some TailwindUI components using LiveView and some of these specify enter / leave transitions to be implemented in JS. As an example, if you go here and view the code you’ll see comments such as:

<!--
  'Solutions' flyout menu, show/hide based on flyout menu state.

  Entering: "transition ease-out duration-200"
    From: "opacity-0 translate-y-1"
    To: "opacity-100 translate-y-0"
  Leaving: "transition ease-in duration-150"
    From: "opacity-100 translate-y-0"
    To: "opacity-0 translate-y-1"
-->

(some background on why this can’t be done with CSS alone can be found here)

React, Vue and other frameworks have built-in support for such transitions, for example: Transition | Vue.js but it doesn’t appear that LiveView currently does, and I haven’t found an issue about this.

I was wondering if anyone has found a way to implement enter / leave transitions with LiveView. Thanks.

Showing Posts 1 to 10

Qqwy

Qqwy

TypeCheck Core Team

Do you mean transitions onmouseenter/onmouseout?

If so, these are not currently supported in LiveView yet. I hope this will change, c.f. Phoenix LiveView request/proposal: mouse events

neilberkman

neilberkman OP

Thanks, but the issue is also about the ability to change a property once the transition has completed. Here’s some further illustration from https://sebastiandedeyne.com/javascript-framework-diet/enter-leave-transitions/ :

Broadly speaking, there are two ways to implement enter & leave transitions:

  • Just use the transition CSS properties
  • Animations with JavaScript (either by modifying CSS properties, with the web animation API, or with third-party animation libraries)

Unfortunately, transition doesn’t cut it. Here’s what we need for our dropdown list transition:

  • When the dropdown opens, set it’s display property to block , then set transition opacity from 0 to 1
  • When the dropdown closes, transition opacity from 1 to 0 , then set it’s display property to none

We can’t build this with CSS transitions alone, because there’s no way to change the display value before or after the transition happened. I don’t like JavaScript animations for these things in JavaScript either, because it couples your JavaScript to CSS and vice-versa.

This is the scenario supported explicitly by Vue, React and other frameworks.

Actually I just noticed that you asked a similar question here: LiveView: Are there any clever techniques to do 'removal' animations?

why

why

I would also love to figure out how to do that without getting into Liveview’s hair :slightly_smiling_face:

My initial hope was to be able to use Alpine.js, as outlined here, but Alpine and Liveview appear to be incompatible as discussed here.

Have bumped into the same article you mentioned also and been studying it over the last couple days to see if this might be the way forward. But I’m not very experienced with javascript, and it’s a bit slow going :slight_smile:

Hoping to get it to work in conjunction with Liveview’s phx-update="ignore"…

outlog

outlog

doesn’t css keyframes work for these multi step animations? - not sure I agree with the article but I’m certainly no css/js expert at all..

you should be able to adapt below code to show/hide that nav thing in tailwind..

Shared with Zight

if you take the phoenix image example:

add this above the form in the render in the component:
<p class="my-class <%= @bg %>">updated</p><br><br>

then the css:

.my-class.blue
{
    animation: fadeInFromNone 2.5s ease-out;
}
.my-class.black
{
    animation-fill-mode: forwards !important;
    animation: fadeOut 2.5s ease-out;
}

@keyframes fadeInFromNone {
    0% {
        display: none;
        opacity: 0;
    }

    1% {
        display: block;
        opacity: 0;
    }

    100% {
        display: block;
        opacity: 1;
    }
}

@keyframes fadeOut {
    0% {
        opacity: 1;
    }

    99% {
        opacity: 0;
    }

    100% {
        display: none;
        opacity: 0;
    }
}
outlog

outlog

had some spare time:
add tailwindcss and ui to your app
copy the html from Tailwind CSS Headers - Official Tailwind UI Components
put a phx-click on the more button, and add my_more_nav <%= @more_status %> to the div class

        <div class="relative">
          <!-- Item active: "text-gray-900", Item inactive: "text-gray-500" -->
          <button  phx-click="toggle_more" type="button" class="text-gray-500 inline-flex items-center space-x-2 text-base leading-6 font-medium hover:text-gray-900 focus:outline-none focus:text-gray-900 transition ease-in-out duration-150">
            <span>More</span>
            <!-- Item active: "text-gray-600", Item inactive: "text-gray-400" -->
            <svg class="text-gray-400 h-5 w-5 group-hover:text-gray-500 group-focus:text-gray-500 transition ease-in-out duration-150" fill="currentColor" viewBox="0 0 20 20">
              <path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd"/>
            </svg>
          </button>

          <!--
            'More' flyout menu, show/hide based on flyout menu state.

            Entering: "transition ease-out duration-200"
              From: "opacity-0 translate-y-1"
              To: "opacity-100 translate-y-0"
            Leaving: "transition ease-in duration-150"
              From: "opacity-100 translate-y-0"
              To: "opacity-0 translate-y-1"
          -->
          <div class="my_more_nav <%= @more_status %> absolute left-1/2 transform -translate-x-1/2 mt-3 px-2 w-screen max-w-md sm:px-0">

css is: (probably some unneeded !important and what not)

div.my_more_nav:not(.show_more):not(.hide_more) {
  display: none;
}

div.show_more
{
    animation-fill-mode: forwards !important;
    animation: fadeInFromNone 0.2s ease-out;
}



div.hide_more
{ 

    animation-fill-mode: forwards !important;
    animation: fadeOut 0.15s ease-in;
}

@keyframes fadeInFromNone {
    0% {
        display: none;
        opacity: 0;
       transform: translateY(4px) translateX(-50%);
        
    }

    1% {
        display: block;
        opacity: 0;
        transform: translateY(4px) translateX(-50%);
    }

    100% {
        display: block !important;
        opacity: 1;
       transform: translateY(0px) translateX(-50%);
    }
}

@keyframes fadeOut {
    0% {
        display: block;
        opacity: 1;
        transform: translateY(0px) translateX(-50%);
    }

    99% {
        opacity: 0;
        transform: translateY(4px) translateX(-50%);
    }

    100% {
        display: none;
        opacity: 0;
        transform: translateY(4px) translateX(-50%);
    }
}

then in the liveview mount assign more_status: "" on the socket..

and have a toggle_more:

  @impl true
  def handle_event("toggle_more", _query, socket) do

   value = if socket.assigns.more_status == "show_more" do
       "hide_more"
     else
      "show_more"
   end
   {:noreply,
         socket
         |> assign(results: %{}, more_status: value)}
  end

most likely I would handle navbar clicks client side (do a phx-hook and addeventlistener on click) - I would also underlay the entire div - and catch clicks outside the navbar thing..

neilberkman

neilberkman OP

@outlog thanks so much! I’ll give it a try.

idi527

idi527

:waving_hand:

I think doing it the same way as it is done in alpine.js would work fine in live view: https://github.com/alpinejs/alpine/blob/master/src/utils.js#L132-L379

why

why

@outlog: Thanks a ton, this looks great! Working through it now…

@idi527: Thanks also. I have looked at the way alpine.js does it, but it’s a bit over my head in terms of js :blush:

why

why

Just to help me understand: Did you mean that you would have a full screen underlay (transparent), so that if somebody clicked outside of the open menu (navbar element), you’d be able to close it again?

Edit: Oops, meant to reply to @outlog, apologies…

outlog

outlog

exactly - all depends on your UI/UX preferences of course..

Where Next? Top

Trending in Questions Top

katta
I having some trouble figuring out if I have set myself too strict of standards for my production server. Currently I can handle 75% of r...
New
achenet
Hello, I’m trying to build a basic Phoenix web-app, and I’d like to use Tailwind. However, when I launch mix phx.server, I get an error...
New
bradley
I really like the adapter patterns that ecto, nebulex, waffle, etc. use and would love find something similar for a key management servic...
New
unaware8150
Hello folks! So at work, we are seeing some situations where we have to define some “fixed” strings that are used across the codebase in...
New
Cxx-mlr
I’m working on a small exercise involving update_in/3, and I came up with this solution: data = %{ name: "Periodic Table", category:...
New
Alvinkariuki
How Can I Optimise Compile Time Dependencies I have been building an elixir application for about 2 years now. Many modules and files ha...
New
dillonoconnor
Is there any way to avoid the Hologram compiler running when using iex? It seems like the front-end code could potentially be disregarded...
New

Other Trending Topics Top

GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
budgie
A little off-topic, but I feel like people here have a good head on their shoulders. I used to be quite good at making software. Was luc...
New
KristerV
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
New
mudasobwa
I fully migrated to my own harness from Anthropic/Gemini and I think it’s time to share it. Welcome DSH, the DeepSeek Harness, fully writ...
New
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews