Types

MFETheme

Gestione tema condiviso tra shell e microfrontend. Stato reattivo isDark, mode light/dark/system e API setTheme/toggleTheme.

Contratto

interface MFETheme {
  /** Stato corrente: true se tema scuro attivo */
  isDark: boolean

  /** Modalità selezionata dall'utente */
  mode: 'light' | 'dark' | 'system'

  /** Imposta la modalità tema */
  setTheme: (mode: 'light' | 'dark' | 'system') => void

  /** Alterna tra light e dark */
  toggleTheme: () => void
}

Campi

CampoTipoDescrizione
isDarkbooleantrue se il tema scuro è attivo. Risolve 'system' in base alla preferenza OS
mode'light' | 'dark' | 'system'Modalità scelta dall'utente (potrebbe differire da isDark quando è 'system')
setTheme(mode)functionImposta la modalità. La shell aggiorna isDark di conseguenza
toggleTheme()functionAlterna tra 'light' e 'dark'. Se la modalità corrente è 'system', parte da isDark corrente

Accesso dal MFE

import { useMfeContext } from '@pzeta/mfe-contracts/composables'

const ctx = useMfeContext()

if (ctx.theme?.isDark) {
  // applica varianti scure
}

console.log('Modalità utente:', ctx.theme?.mode) // 'system'

Reattività

MFETheme non è un Ref: i campi isDark e mode sono valori sincroni al momento della lettura. La shell deve fornire un meccanismo per reagire ai cambi tema, tipicamente via eventBus:

const ctx = useMfeContext()

function applyTheme() {
  document.documentElement.classList.toggle('dark', ctx.theme?.isDark ?? false)
}

onMounted(() => {
  applyTheme()
  ctx.eventBus?.on('theme-changed', applyTheme)
})

onBeforeUnmount(() => {
  ctx.eventBus?.off('theme-changed', applyTheme)
})

In alternativa, la shell può fornire un Ref<MFETheme> esteso nel proprio MFEContext custom.

Pattern: theme switcher nel MFE

<script setup lang="ts">
import { useMfeContext } from '@pzeta/mfe-contracts/composables'

const ctx = useMfeContext()

function setMode(mode: 'light' | 'dark' | 'system') {
  ctx.theme?.setTheme(mode)
}
</script>

<template>
  <div class="theme-switcher">
    <button @click="setMode('light')" :class="{ active: ctx.theme?.mode === 'light' }">
      <i class="pi pi-sun" />
    </button>
    <button @click="setMode('dark')" :class="{ active: ctx.theme?.mode === 'dark' }">
      <i class="pi pi-moon" />
    </button>
    <button @click="setMode('system')" :class="{ active: ctx.theme?.mode === 'system' }">
      <i class="pi pi-desktop" />
    </button>
  </div>
</template>

Implementazione lato shell

Esempio basato su un ref reattivo Vue:

import { ref, computed, watch } from 'vue'
import type { MFETheme } from '@pzeta/mfe-contracts'

const mode = ref<'light' | 'dark' | 'system'>('system')
const systemPrefersDark = ref(matchMedia('(prefers-color-scheme: dark)').matches)

matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
  systemPrefersDark.value = e.matches
})

const isDark = computed(() =>
  mode.value === 'dark' || (mode.value === 'system' && systemPrefersDark.value),
)

const theme: MFETheme = {
  get isDark() { return isDark.value },
  get mode() { return mode.value },
  setTheme(m) { mode.value = m },
  toggleTheme() {
    mode.value = isDark.value ? 'light' : 'dark'
  },
}

// Emette evento ai MFE quando il tema cambia
watch(isDark, () => eventBus.emit('theme-changed'))

Tipi correlati