🛠️ ThemePairToggle

@components/react/UI/ThemePairToggle.jsx

Theme Pair Toggle

A tiny, no-flicker light/dark switch that swaps between your chosen DaisyUI pair.
It shows the correct icon on first paint (no “sun flash” in dark mode) and animates the moon on toggle. You can swap out the existing ThemeSwitcherWithPreviews in src/components/navigation/NavBar.astro


Quick start (3 steps)

  1. Pick your pair (example below uses cupcake as light and dracula as dark).
  2. In the global CSS (/src/styles/global.css) loaded on every page simply update theme names to match your pair.
/* Show the correct glyph purely from <html data-theme> – no theme-name assumptions */
:root[data-theme="dracula"] .theme-toggle .sun-icon {
  opacity: 0;
  pointer-events: none;
  transform: rotate(180deg);
}
:root[data-theme="dracula"] .theme-toggle .moon-icon {
  opacity: 1;
  pointer-events: auto;
}

/* Any theme that is NOT your dark theme => treat as light */
:root:not([data-theme="dracula"]) .theme-toggle .sun-icon {
  opacity: 1;
  pointer-events: auto;
}
:root:not([data-theme="dracula"]) .theme-toggle .moon-icon {
  opacity: 0;
  pointer-events: none;
}

/* Replace "light"/"dracula" above with your actual pair, e.g. cupcake/dracula */

/* Moon entrance animation (only on explicit toggle to dark) */
@media (prefers-reduced-motion: no-preference) {
  @keyframes moonPop {
    0% {
      transform: scale(0.8) rotate(-12deg);
    }
    60% {
      transform: scale(1.05) rotate(0deg);
    }
    100% {
      transform: scale(1) rotate(0deg);
    }
  }
  /* No fill mode → when the class is removed, it won't “stick” */
  .theme-toggle .moon-animate {
    animation: moonPop 280ms ease-out;
    will-change: transform;
  }
}
  1. Import and add in place of the existing theme switcher in src/components/navigation/NavBar.astro
   <!-- <ThemeSwitcherWithPreviews client:load /> -->
    <ThemePairToggle client:load />
  1. (Recommended) Keep your early head script in the <head> that sets <html data-theme="…"> from localStorage("theme") or system preference. That prevents page flicker; the CSS above ensures the correct icon is visible immediately.

Usage (Astro)

Use the React component directly in .astro or .mdx with client:load:

Default pair (cupcake ↔ dracula)

        <ThemePairToggle lightTheme="cupcake" darkTheme="dracula" client:load />
      

Smaller / larger

        <div className="flex items-center gap-4">
<ThemePairToggle lightTheme="cupcake" darkTheme="dracula" size={5} client:load />
<ThemePairToggle lightTheme="cupcake" darkTheme="dracula" size={8} client:load />
</div>
      

Custom pair (light ↔ business)

        <ThemePairToggle lightTheme="light" darkTheme="business" client:load />
      

Inherit color from context

        <div className="p-4 rounded-xl bg-base-200 text-secondary">
<ThemePairToggle lightTheme="cupcake" darkTheme="dracula" client:load />
</div>
      

Props

  • lightTheme (string): DaisyUI theme used for light.
  • darkTheme (string): DaisyUI theme used for dark.
  • storageKey (string, default "theme"): persisted key (stays in sync with your full switcher).
  • size (number, default 6): Tailwind size for a square icon (h-{size} w-{size}).
  • className (string): extra classes (e.g., text-base-content).

The toggle:

  • reads the initial theme before first render from localStorage("theme") and <html data-theme>,
  • updates <html data-theme> and localStorage on click,
  • dispatches a theme:change event so multiple toggles stay in sync,
  • stacks Sun and Moon in a fixed square (no horizontal nudge),
  • animates Moon only when entering dark (no animation on page load).

Troubleshooting

  • Icon “disappears” on navigation: ensure the global CSS above is loaded on every page and the selectors use your exact pair names (replace cupcake/dracula if different).
  • Wrong icon on first paint: confirm your early head script sets <html data-theme="…"> before paint.
  • Color looks invisible: the icons inherit currentColor. Wrap the toggle in a container with a visible text color (e.g., text-base-content).

That’s it—drop the CSS in once, choose your pair, and use <ThemePairToggle … /> anywhere.


Free shipping over £60.00

Delivered with care. No code needed.