460 words, 3 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.