366 words, 2 min read

Sometimes you just want to visualize a small list of numbers in your Phoenix app — without pulling in a charting library or writing any JavaScript.

Luckily, with a bit of TailwindCSS and a sprinkle of animation, you can create a clean, responsive, and animated bar chart right in your LiveView template.

Let’s say we have a list of values in Elixir:

values = [19, 21, 27, 16, 37, 34, 27, 32, 33, 10]

We’ll render those as vertical bars that grow smoothly into view when the component mounts.

To make sure all bars fit within the same visual scale, we’ll normalize each value to a percentage of the maximum:

<%
max_value = Enum.max(@values)
%>

This way, the tallest bar will always reach 100% of the container height, no matter what values you pass in.

The next step is to build the HTML structure.

Here’s the complete HEEx template for your bar chart:

<%
max_value = Enum.max(@values)
%>
<div class="flex items-end gap-2 h-48 sm:h-64 md:h-80 w-full max-w-xl mx-auto">
<%= for {value, index} <- Enum.with_index(@values) do %>
<% height = value / max_value * 100 %>
<div class="flex-1 flex flex-col items-center h-full justify-end">
<div
class={[
"bg-blue-500 w-full rounded-t text-center text-white text-xs font-bold pt-2",
"animate-grow transition-all duration-700 ease-out h-[#{height}%]"
]}
style={"height: #{height}%; animation-delay: #{50}ms; transform: scaleY(0)"}
title={"#{value}"}
></div>
<span class="text-xs mt-1 text-gray-600"><%= value %></span>
</div>
<% end %>
</div>
<style>
@keyframes grow {
from {
transform: scaleY(0);
}
to {
transform: scaleY(1);
}
}
.animate-grow {
animation: grow 0.8s ease-out forwards;
}
</style>

Next, let's add the animation.

The magic is in the CSS @keyframes grow animation:

@keyframes grow {
from {
transform: scaleY(0);
}
to {
transform: scaleY(1);
}
}

Each bar starts at scaleY(0) and smoothly grows to its normalized height.

The origin-bottom Tailwind class ensures that the bars expand upward instead of stretching from the middle.

The container uses Tailwind’s responsive height utilities:

h-48 sm:h-64 md:h-80

So it scales gracefully across screen sizes. Each bar also uses flex-1, making all bars equal width, automatically filling the space available.

You now have a fully responsive, zero-JavaScript bar chart that animates beautifully when rendered.

It’s small, fast, and easy to integrate in any LiveView or static page.