LiveView gives us beautiful declarative ways to build interactive UIs without writing much JS. But one thing developers often want is this:
โ A flash message that shows up, waits a few seconds, fades away โ and shows a countdown line while it's visible.
Hereโs how to do it with almost no JS and a sprinkle of Tailwind + raw CSS.
๐ง 1. Add the Flash Component
In your LiveView or component module (core_components.ex
), define a flash like this if it doesn't exist already:
def flash(assigns) do
assigns = assign_new(assigns, :id, fn -> "flash-#{assigns.kind}" end)
~H"""
<div
:if={msg = render_slot(@inner_block) || Phoenix.Flash.get(@flash, @kind)}
id={@id}
phx-hook="AutoClearFlash"
phx-click={JS.push("lv:clear-flash", value: %{key: @kind}) |> hide("##{@id}")}
role="alert"
class={[
"fixed top-2 right-2 mr-2 w-80 sm:w-96 z-50 rounded-lg p-3 ring-1",
@kind == :info && "bg-sky-50 text-blue-500 ring-blue-500 fill-blue-900",
@kind == :success && "bg-emerald-50 text-emerald-800 ring-emerald-500 fill-cyan-900",
@kind == :warn && "bg-amber-50 text-amber-900 shadow-md ring-amber-500 fill-amber-900",
@kind == :error && "bg-rose-50 text-rose-900 shadow-md ring-rose-500 fill-rose-900"
]}
{@rest}
>
<p :if={@title} class="flex items-center gap-1.5 text-sm font-semibold leading-6">
<.icon :if={@kind == :info} name="hero-information-circle-mini" class="h-4 w-4" />
<.icon :if={@kind == :success} name="hero-check-circle-mini" class="h-4 w-4" />
<.icon :if={@kind == :warn} name="hero-exclamation-circle-mini" class="h-4 w-4" />
<.icon :if={@kind == :error} name="hero-exclamation-circle-mini" class="h-4 w-4" />
{@title}
</p>
<p class="mt-2 text-sm leading-5">{msg}</p>
<button type="button" class="group absolute top-1 right-1 p-2" aria-label={gettext("close")}>
<.icon name="hero-x-mark-solid" class="h-5 w-5 opacity-40 group-hover:opacity-70" />
</button>
</div>
"""
end
๐ง 2. Auto-dismiss via Hook
Add a small JS hook (hooks.js
):
let Hooks = {}
Hooks.AutoClearFlash = {
mounted() {
const ignoredIDs = ["client-error", "server-error"];
if (ignoredIDs.includes(this.el.id)) return;
setTimeout(() => this.el.click(), 2500);
}
};
export default Hooks;
Hook it up in your LiveSocket
config:
import Hooks from "./hooks";
let liveSocket = new LiveSocket("/live", Socket, { hooks: { AutoClearFlash }, ... });
๐จ 3. Visual Countdown with Raw CSS
Inside your app.css
, add this keyframes
animation
@keyframes countdown {
from { width: 100%; }
to { width: 0%; }
}
โณ 4. Countdown bar
Expand your flash component with this countdown bar for beautiful visual effect. Add it just at the end of your flash component.
<div class="mt-2 h-1 w-full overflow-hidden rounded-full bg-white/30">
<div class={[
"h-full animate-[countdown_2.5s_linear_forwards]",
@kind == :info && "bg-blue-500",
@kind == :success && "bg-emerald-500",
@kind == :warn && "bg-amber-500",
@kind == :error && "bg-rose-500"
]} />
</div>
โ That's It!
You now have:
- A flash that disappears after 2.5s
- A smooth fade-out animation
- A subtle countdown bar like a fuse burning out
Let LiveView do the heavy lifting โจ
๐ฌ Got ideas for variations (e.g. pause on hover, different timers per flash type)? Let me know in the comments!
Top comments (0)