Theme Toggle (Store)

Shared theme state using a store, with multiple subscribing components.

Live Demo

Three independent components, all reacting to one store.

MyApp
Home About Contact

Dashboard

Welcome back. You have 3 new notifications.

Source

HTML + JavaScript + CSS
<!-- Three independent components, all reacting to one store -->
<div data-component="theme-button">
    <button data-action="toggle" data-bind="label"></button>
</div>

<div data-component="theme-navbar">
    <div data-bind-class="navClass">
        <strong data-bind-class="brandClass">MyApp</strong>
        <span data-bind-class="linkClass">Home</span>
        <span data-bind-class="linkClass">About</span>
    </div>
</div>

<div data-component="theme-card">
    <div data-bind-class="cardClass">
        <h4 data-bind-class="titleClass">Dashboard</h4>
        <p data-bind-class="textClass">Welcome back.</p>
    </div>
</div>

<script>
// Store: shared theme state with built-in persistence
wildflower.store('theme', {
    storageKey: 'app-theme',
    autoSave: true,
    state: {
        darkMode: true
    },
    toggle() {
        this.darkMode = !this.darkMode;
    }
});

// Component A: toggle button
wildflower.component('theme-button', {
    subscribe: { theme: ['darkMode'] },
    computed: {
        label() {
            return this.stores.theme?.darkMode ? 'Switch to Light' : 'Switch to Dark';
        }
    },
    toggle() {
        this.stores.theme.toggle();
    }
});

// Component B: navbar (subscribes to same store)
wildflower.component('theme-navbar', {
    subscribe: { theme: ['darkMode'] },
    computed: {
        navClass() {
            return this.stores.theme?.darkMode
                ? 'nav-dark' : 'nav-light';
        },
        brandClass() {
            return this.stores.theme?.darkMode
                ? 'brand-dark' : 'brand-light';
        },
        linkClass() {
            return this.stores.theme?.darkMode
                ? 'link-dark' : 'link-light';
        }
    }
});

// Component C: content card (subscribes to same store)
wildflower.component('theme-card', {
    subscribe: { theme: ['darkMode'] },
    computed: {
        cardClass() {
            return this.stores.theme?.darkMode
                ? 'card-dark' : 'card-light';
        },
        titleClass() {
            return this.stores.theme?.darkMode
                ? 'title-dark' : 'title-light';
        },
        textClass() {
            return this.stores.theme?.darkMode
                ? 'text-dark' : 'text-light';
        }
    }
});
</script>

<style>
/* Dark theme */
.nav-dark  { background: #1a1a1a; border: 1px solid #333; }
.brand-dark { color: #6b996a; }
.link-dark  { color: #a0a0a0; }
.card-dark  { background: #1a1a1a; border: 1px solid #333; }
.title-dark { color: #f0f0f0; }
.text-dark  { color: #a0a0a0; }

/* Light theme */
.nav-light  { background: #f8f9fa; border: 1px solid #dee2e6; }
.brand-light { color: #2d5a2d; }
.link-light  { color: #555; }
.card-light  { background: #ffffff; border: 1px solid #dee2e6; }
.title-light { color: #212529; }
.text-light  { color: #6c757d; }

/* Smooth transition between themes */
[class*="nav-"], [class*="card-"] { transition: all 0.3s; }
</style>

Key Points

  • wildflower.store() creates shared state; any component can subscribe to it
  • subscribe: { theme: ['darkMode'] } tells the component to re-render when darkMode changes
  • Components access store state via this.stores.theme.darkMode and call store methods via this.stores.theme.toggle()
  • Three independent components (the toggle button, the navbar, and the card) all react to the same store change with no event wiring or prop passing
  • storageKey + autoSave: true persists state to localStorage automatically. No manual setItem calls needed
  • This is the same pattern the WildflowerJS documentation site uses for its own theme toggle