Theme Controller

The Theme Controller component provides a plethora of pre-built sub-components that can be used to build a custom theme switcher.

Tip

The ThemeController component is a Builder component rather than your standard Slotable ViewComponent.

This means that instead of the component providing slots, and handling where your custom components are inserted, the developer utilizes helper methods on the component to choose where the sub-components are rendered.

In the examples below, we use the HAML = syntax to output the rendered sub-components, whereas typical components use the HAML - syntax to "pass" the component to a slot.

Warning

To prevent a flash of content when the page loads with a non-default theme, add = theme_preload_script to your layout's <head> section. This inline script runs before the page renders to set the theme from localStorage.

See GitHub Issue #49 and the ThemeHelper API Docs for more details.

Theme Preview Icons

One of the most basic sub-components is the theme preview icon. It shows a visual representation of the theme using the base, primary, secondary, and accent colors as little dots inside of a rounded square.

Preview
Light
Dark
Synthwave
Retro
Cyberpunk
Wireframe
Code
= daisy_theme_controller do |theme_controller|
  .flex.flex-col.sm:flex-row.gap-4.items-start.sm:items-center
    - theme_controller.themes.each do |theme|
      .flex.flex-row.gap-2.items-center
        = theme_controller.build_theme_preview(theme)
        = theme.humanize

Theme Radio Inputs

When a theme radio input is selected, the page's theme will change to the value of the input.

Tip

You do not need to wire up any setTheme action. The ThemeController automatically watches its inputs for change events, saves the selection to localStorage, and keeps every other theme selector on the page (including the header switcher) in sync.

We provide a custom name of docs-radio-theme so these inputs form their own radio group, but selecting one still syncs everywhere.

Preview
Code
= daisy_theme_controller do |theme_controller|
  .flex.flex-col.sm:flex-row.gap-4.items-start.sm:items-center
    - theme_controller.themes.each do |theme|
      %label.flex.flex-row.gap-2.items-center
        = theme_controller.build_radio_input(theme, name: "docs-radio-theme")
        = theme.humanize

Custom Switcher

You can also build a custom switcher by using all of the sub-components in combination.

Warning

Note that the daisy_theme_controller may need to be the parent of your custom switcher to ensure that it appropriately sets up the Stimulus controller.

In the example below, the dropdown doesn't render custom content when using the with_item helper, so the Stimulus controller wouldn't be applied if the order were switched.

Preview
Code
= daisy_theme_controller do |tc|
  = daisy_dropdown do |dropdown|
    - dropdown.with_button(css: "btn-error", icon: "swatch", title: "Themes", right_icon: "chevron-down")

    - tc.themes.each do |theme|
      - dropdown.with_item do
        = daisy_link(href: "#", css: "no-underline", html: { data: { action: "click->loco-theme#setTheme" } }) do
          = tc.build_radio_input(theme, name: "docs-custom-theme", css: "hidden peer")
          = tc.build_theme_preview(theme)
          = theme.humanize
          = hero_icon "check", css: "size-4 invisible peer-checked:visible"
Made with by Profoundry .
Copyright © 2023-2026 Profoundry LLC.
All rights reserved.