Conditional css class names

How to set conditional class names in a template. Looking at the code below I’m wanting to toggle between bg-gray-900 and bg-gray-300 depending if the page is the active page. ie. `@page = “settings”?

<%= link "Account Settings", to: Routes.user_settings_path(@conn, :edit), class: "px-3 py-2 text-sm font-medium text-white bg-gray-900 rounded-md" %>
1 Like

Those could help

You just need to change the “active” class name to be the color you want and the nil or default value to be the other shade.

3 Likes

I ended up using this inline conditional for applying the class, works for my use case and I feel it keeps it simple. Just set a ‘page’ variable on the render/3 in the controller.

<%= link "Account Settings", to: Routes.user_settings_path(@conn, :edit), class: "#{if @page === "account_settings", do: "bg-gray-900 "}px-3 py-2 text-sm font-medium text-white bg-gray-900 rounded-md" %>

1 Like

I pinched this snippet from a previous dev working on my current project.

  def classes(classes) do
    classes
    |> Enum.filter(&elem(&1, 1))
    |> Enum.map(&elem(&1, 0))
    |> Enum.join(" ")
  end

Using it as

<%= some_tag "example", class: classes("is-selected": is_selected_or_something()) %>
<div class="<%= classes("is-selected": true) %>">
3 Likes

if it works for someone

<div class="w-full">
  <ol class="flex flex-col text-sm font-medium text-center text-gray-500 md:items-center md:flex-row flex-nowrap ">
    <li
      :for={step <- @steps}
      class={
        (if step.slug == @current_step,
          do: "text-blue-600 ",
          else: "") <>
        (if step.status != :last,
          do: "sm:after:content-[''] sm:after:w-full sm:after:h-1 sm:after:border-b sm:after:border-gray-400 sm:after:border-1 sm:after:mx-6 ",
          else: "") <>
              "flex items-center"
      }
    >
      <.icon name={step.icon} class="mr-2" />
      <%= step.title %>
    </li>
  </ol>
</div>

You should be able to write this as

<div class="w-full">
  <ol class="flex flex-col flex-nowrap text-center text-sm font-medium text-gray-500 md:flex-row md:items-center">
    <li
      :for={step <- @steps}
      class={[
        "flex items-center",
        step.slug == @current_step && "text-blue-600",
        step.status != :last &&
          "sm:after:content-[''] sm:after:w-full sm:after:h-1 sm:after:border-b sm:after:border-gray-400 sm:after:border-1 sm:after:mx-6 "
      ]}
    >
      <.icon name={step.icon} class="mr-2" />
      <%= step.title %>
    </li>
  </ol>
</div>
1 Like

I also moved away from using the classes/1 function I mentioned above to the condition && "classes" approach after someone noted the atom creation aspect of the keyword list being an additional unnecessary concern.

Hi

when I try your suggestion with something like:

    <.icon
              name="hero-check-circle"
              class={[
                "w-7 h-7",
                if(item.status == :completed, do: "bg-green-600", else: "bg-gray-300")
              ]}
            />

I get a warning:

 warning: attribute "class" in component DragWeb.CoreComponents.icon/1 must be a :string, got: [

I’m on phoenix 1.7.1 with live_view 0.18.16.

Does anyone have any idea why this isn’t working?

cheers

Dave

<.icon /> is defined in CoreComponents. Search for def icon( and change attr :class, :string, default: nil to attr :class, :any, default: nil.

That worked :-). Many thanks @sodapopcan.