Detecting System Theme (Fully Responsive)

After searching everywhere online without finding a solid solution, I’ve decided to share a simple and effective way to detect the system theme in Bubble.

Goals:

  1. Automatically detect whether the user’s system is in light or dark mode and apply the corresponding theme.
  2. If the user manually selects a theme (light or dark), the automatic system detection will be disabled.
  3. If the user switches their device theme while using the site, the change will be detected and applied instantly.

Demo
Let’s walk through how to make this happen!

:hammer_and_wrench: Setup

Step 1:
Install the Toolbox plugin — this allows you to run JavaScript in your Bubble app.

Step 2:
Create an Option Set called Theme with three options:

  • System
  • Light
  • Dark

Step 3:
In your User data type, add two new fields:

  • Theme (type: Theme) — to store the user’s selected theme
  • Dark_mode (type: yes/no) — to track whether dark mode should be applied

:wrench: Building It Out

Step 1:
Add a “JavaScript to Bubble” element to your page:

  • Set the bubble_fn_suffix to theme_change
  • Enable Trigger event and Publish value
  • Set value type to yes/no

Step 2:
Create a workflow on the “JavaScript to Bubble” element:

  • Action: Make changes to Current User
  • Set Dark_mode = This JavaScript to Bubble's value
  • Only run this when Current User’s Theme is “System”

Step 3:
On Page Load, run the following JavaScript:

if (!window._themeListenerAttached) {
  window._themeListenerAttached = true;

  const checkAndSendTheme = () => {
    // ✅ Check if matchMedia is supported and usable
    if (window.matchMedia && typeof window.matchMedia('(prefers-color-scheme: dark)').matches === 'boolean') {
      const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
      bubble_fn_theme_change(isDark ? 'yes' : 'no'); // 👈 output "yes" or "no"
    } else {
      // ❌ Fallback to light mode if not supported
      bubble_fn_theme_change('no');
    }
  };

  // Initial check
  checkAndSendTheme();

  // Watch for theme changes
  if (
    window.matchMedia &&
    typeof window.matchMedia('(prefers-color-scheme: dark)').addEventListener === 'function'
  ) {
    window
      .matchMedia('(prefers-color-scheme: dark)')
      .addEventListener('change', () => {
        checkAndSendTheme();
      });
  }
}

Step 4:
Create a UI element (dropdown, repeating group, etc.) to let users choose between:

  • System
  • Light
  • Dark

Step 5:
Add conditional workflows based on user selection:

  • If Light is selected:
    → Set User's Theme = Light
    → Set Dark_mode = no
  • If Dark is selected:
    → Set User's Theme = Dark
    → Set Dark_mode = yes
  • If System is selected:
    → Set User's Theme = System
    → Run this JavaScript to re-check the system theme:
const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
bubble_fn_theme_change(isDark ? 'yes' : 'no');

How Does This Compare to Zeroqode’s Dark Mode Detector?

Zeroqode’s dark mode plugin is popular — but it lacks live detection. You need to manually run their action every time you want to check the system theme. That means if you want real-time responsiveness, you’d have to run a workflow on a loop, say every second — not ideal for performance or UX.


Editor
And that’s about it! With a few lines of JavaScript and Bubble logic, you’ve now got a system-aware theme engine that’s reactive, lightweight, and user-customizable.

Enjoy your fully responsive dark/light mode — perfect for those dedicated users who appreciate the extra polish! :crescent_moon::sun:

3 Likes

Thanks for this! I’ll surely use it at some point