Dynamic style selection with heex

I’m wondering how I can translate this js example into liveview:

(I’ve simplified the example for the question)

import clsx from 'clsx'

const styles = {
  note: {
    container:
      'bg-sky-50 dark:bg-slate-800/60 dark:ring-1 dark:ring-slate-300/10',
  },
  warning: {
    container:
      'bg-amber-50 dark:bg-slate-800/60 dark:ring-1 dark:ring-slate-300/10',
  },
}

export function Callout({ type = 'note', children }) {

  return (
    <div className={clsx('my-8 flex rounded-3xl p-6', styles[type].container)}>
          {children}
    </div>
  )
}

  • Having a variable styles doesn’t work (liveview doesn’t like variables - Assigns and HEEx templates — Phoenix LiveView v0.18.3).
  • Putting the styles into assigns on every render feels like overkill.
  • Having a constant @styles defined at the top of the module doesn’t work. Because then the template searches for @styles in assigns instead of the constant.

The only option I’ve found so far is something like this:

 

  defp callout(assigns) do
    ~H"""
    <div class={["my-8 flex rounded-3xl p-6", @type == "note" && "", @type == "warning" && ""]}>
      
    </div>
    """
  end

This gets ugly fast as there are more types added and there are more styles (for a title, children,…)

Is there anything I’m missing?

attr :type, :string, values: ["note", "warning"], default: "note"
defp callout(assigns) do
  ~H"""
  <div class={["my-8 flex rounded-3xl p-6", callout_type_style(@type)]}>
  </div>
  """
end

defp callout_type_style("note"), do: "bg-sky-50 dark:bg-slate-800/60 dark:ring-1 dark:ring-slate-300/10"
defp callout_type_style("warning"), do: "bg-amber-50 dark:bg-slate-800/60 dark:ring-1 dark:ring-slate-300/10"
2 Likes

I’m experimenting right now on creating a lib for this usecase. I used to do the same like @LostKobrakai recommends (which is a good default) but if you have multiple variants this can get a little messy, too. The first release will be ready in the next couple of days.

4 Likes