How to change the root path based on selected theme inside a view

I am currently using Phoenix.Template.render(ChatWeb.ThemeView, "chat_window_live_component", "html", assigns) to render the HTML for all my components. In my view class (ChatWeb.ThemeView), I have the following structure:

elixirCopy code

defmodule ChatWeb.ThemeView do
  use ChatWeb, :html
  embed_templates "**/*", root: "templates/default/"
end

I am facing an issue where I need to change the root path dynamically based on a selected theme. Unfortunately, I cannot find a way to add a condition based on an assign or a socket value.

Problem:

I need to change the root path dynamically in the embed_templates function based on a selected theme. For example, if the user selects the “dark” theme, I want the root path to be “templates/dark/”. If they select the “light” theme, I want it to be “templates/light/”.

Request for Support:

I need assistance in finding a solution to dynamically change the root path in the embed_templates function based on a selected theme. Is there a way to achieve this using assigns or socket values?

What you’re asking for is not possible. root is used to find templates to compile into the module. Once the module is compiled the template files are no longer needed, all their functionality has become functions on the module.

Therefore there’s no way to adjust “templates” at runtime. At runtime there’s just functions on modules.

To make theming work you need to look for different approaches.

Yeah to echo what @LostKobrakai said wholly changing out templates just for theming seems very heavy weight. Normally you can do this via a single CSS class addition at some root DOM node.

Is there a way to add a theme condition to a template?

I have a logo on my page that is usually black (on white background). Now that I have upgraded to 1.8 this theme selector has shown up. So I am trying to make an effort to support it starting with fixing my logo. But I can’t find how to get the current theme setting so I can conditionally adjust the logo to a white rendition.

The theme is only handled client side using css. You can e.g. use <picture> to provide distinct images per prefers-color-scheme. Or plain old hidden dark:block/block dark:hidden class pairs.

Tried both techniques. It always uses the same logo image regardless of theme selection. e.g. my code using picture:

      <picture>
        <source srcset="/images/nai-white.png" media="(prefers-color-scheme: dark)">
        <img src="/images/nai-black.png" class="w-42 pb-2">
      </picture>

The logo is always nai-white.png.

The theme selector doesn’t change the value seen that that media query.

You should be able to use the dark: variant.

References:

Using:

  <img src="/images/nai-white.png" class="hidden dark:block w-42 pb-2">
  <img src="/images/nai-black.png" class="block dark:hidden w-42 pb-2">

Still always “nai-white.png” is displayed regardless of the theme selector.

I tried changing themes: false to:

@plugin "../vendor/daisyui" {
  themes: true;
}

Just to see if that might make any difference. But got:

TypeError: themeArray[0].includes is not a function. (In 'themeArray[0].includes("--default")', 'themeArray[0].includes' is undefined)

Sigh. I hacked it.

<script>
  function changeLogo(theme) {
    ld = document.getElementById("logo-dark")
    ll = document.getElementById("logo-light")

    if (theme == "light") {
      ld.classList.add('hidden');    ld.hidden = true;
      ll.classList.remove('hidden'); ll.hidden = false;
    } else {
      ld.classList.remove('hidden'); ld.hidden = false;
      ll.classList.add('hidden');    ll.hidden = true;      
    }
  }
  
  window.addEventListener("phx:set-theme", ({ detail: { theme } }) => changeLogo(theme));
  
  changeLogo(localStorage.getItem("phx:theme") || "system")
</script>

Sucks, but it works.

AH, totally forgot that phoenix isn’t using light/dark because daisy allows for more than two themes. Here’s how you can add tailwind variants for themes: daisyUI themes — Tailwind CSS Components ( version 5 update is here )

1 Like