@tsogtoodev/bongo
v1.0.0
Published
Animated theme switcher for Vue 3 and Nuxt — light/dark/system themes with GIF and View Transition effects.
Maintainers
Readme
Bongo 🥁
Animated theme switcher for Vue 3 and Nuxt — light/dark/system themes with GIF-masked and View Transition reveals.
- 🌗 Light · dark · system with live OS-preference tracking, persistence and cross-tab sync
- 🎞️ GIF transitions — the new theme revealed through a preloaded, cached GIF's alpha mask
- 🌀 View Transition reveals — circle, rectangle, polygon, blur; feature detected with instant fallback
- 🧩 Composable-first — use the component, the composable, or your own UI
- 🖥️ SSR-safe — no browser APIs on the server, anti-flash inline script, optional Nuxt layer
- ♿ Accessible — native button semantics, ARIA state,
prefers-reduced-motionrespected everywhere - 🌲 Library-grade — ESM + CJS, typed, tree-shakeable,
vueas the only peer dependency
Installation
npm install @tsogtoodev/bongoVue 3
// main.ts
import { createApp } from 'vue'
import { createBongo } from '@tsogtoodev/bongo'
import App from './App.vue'
const app = createApp(App)
app.use(createBongo({
defaultTheme: 'system',
attribute: 'class', // <html class="dark"> (or 'data-theme')
// circle defaults to start: 'origin' — it grows from the clicked element
transition: { type: 'view', variant: 'circle' },
}))
app.mount('#app')<script setup lang="ts">
import { BongoThemeToggle, useBongoTheme } from '@tsogtoodev/bongo'
const { theme, resolvedTheme, setTheme, toggleTheme } = useBongoTheme()
</script>
<template>
<!-- default toggle -->
<BongoThemeToggle />
<!-- GIF mask transition -->
<BongoThemeToggle transition="gif" gif-src="/theme-transition.gif" />
<!-- or fully custom UI -->
<button @click="toggleTheme()">{{ resolvedTheme }}</button>
</template>Optional default button styling: import '@tsogtoodev/bongo/styles.css'.
Prevent the SSR theme flash
import { bongoInitScript } from '@tsogtoodev/bongo'
// inject into <head> with your framework's head mechanism:
const script = bongoInitScript({ defaultTheme: 'system' })Nuxt
// plugins/bongo.ts
import { createBongoNuxtPlugin } from '@tsogtoodev/bongo/nuxt'
export default defineNuxtPlugin(
createBongoNuxtPlugin({ defaultTheme: 'system' }),
)// app.vue — anti-flash head script
import { bongoHeadScript } from '@tsogtoodev/bongo/nuxt'
useHead({ script: [bongoHeadScript({ defaultTheme: 'system' })] })The Nuxt layer is a thin wrapper over the core Vue plugin: it defers browser init until after hydration (no hydration mismatches) and never touches browser APIs on the server. The core package does not depend on Nuxt.
API sketch
const {
theme, // 'light' | 'dark' | 'system' (writable)
resolvedTheme, // 'light' | 'dark'
systemTheme, // OS preference
isDark,
setTheme, // setTheme('dark', { transition, trigger })
toggleTheme,
controller, // framework-independent controller
} = useBongoTheme()Transitions can be configured app-wide, per component, or per call:
toggleTheme({ transition: { type: 'gif', src: '/party.gif' } })
setTheme('dark', { transition: { type: 'view', variant: 'rectangle', start: 'bottom-up' } })
toggleTheme({ transition: false }) // instantFull documentation lives in docs/ — run npm run docs:dev for
the interactive playground.
Development
npm install # library deps
npm run test # vitest
npm run typecheck # vue-tsc
npm run lint # eslint
npm run build # dist/ (ESM + CJS + d.ts + styles)
cd docs && npm install # docs deps (links the library via file:..)
npm run docs:dev # playground / docs site
npm run docs:buildLicense
MIT
