Vanilla JS
Framework-free theme toggle for any project — plain JavaScript, no dependencies.
createToggle
The core toggle function manages theme state, localStorage persistence, and DOM attribute updates:
theme-setup.js
import { createToggle } from 'nightfall-css/script'
const toggle = createToggle({
storageKey: 'nightfall-theme', // localStorage key
attribute: 'data-theme', // HTML attribute to set
defaultTheme: 'system', // 'light' | 'dark' | 'system'
onChange: (theme) => {
console.log('Theme changed to:', theme)
},
})
// Toggle between light and dark
document.querySelector('#toggle-btn')
.addEventListener('click', toggle.toggle)
// Get current theme
console.log(toggle.getTheme()) // 'light' | 'dark' | 'system'
console.log(toggle.getResolved()) // 'light' | 'dark'
// Set explicitly
toggle.setTheme('dark')
toggle.setTheme('light')
toggle.setTheme('system')
// Listen for system preference changes
// (handled automatically, but you can add custom logic)
toggle.onSystemChange((systemTheme) => {
console.log('OS preference changed to:', systemTheme)
})getToggleHTML
Get a ready-to-use HTML string for a theme toggle button. Drop it into any page:
inject-toggle.js
import { getToggleHTML } from 'nightfall-css/script'
// Get the HTML for a toggle button
const html = getToggleHTML({
size: 'md', // 'sm' | 'md' | 'lg'
iconStyle: 'svg', // 'svg' | 'emoji' | 'text'
ariaLabel: 'Toggle dark mode',
})
// Inject it into your page
document.querySelector('#theme-toggle-container').innerHTML = html
// The HTML output looks like:
// <button
// id="nightfall-toggle"
// aria-label="Toggle dark mode"
// class="nightfall-toggle nightfall-toggle--md"
// >
// <svg class="nightfall-icon-sun">...</svg>
// <svg class="nightfall-icon-moon">...</svg>
// </button>getToggleCSS
Get the minimal CSS needed for the toggle button. Inject it as a style tag or include it in your build:
inject-styles.js
import { getToggleCSS } from 'nightfall-css/script'
// Get the CSS string
const css = getToggleCSS()
// Inject as a style tag
const style = document.createElement('style')
style.textContent = css
document.head.appendChild(style)
// Or include in your build pipeline
// The CSS handles:
// - Sun/moon icon visibility based on current theme
// - Smooth rotation/fade transition between icons
// - Hover and focus states
// - Size variants (sm, md, lg)Full Example
A complete vanilla JS setup with no framework:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="nightfall-theme.css" />
<script>
// FOUC prevention (inline, runs before paint)
(function(){
var t = localStorage.getItem('nightfall-theme');
if (t === 'dark' || (!t && matchMedia('(prefers-color-scheme:dark)').matches)) {
document.documentElement.setAttribute('data-theme', 'dark');
}
})();
</script>
</head>
<body>
<header>
<h1>My Site</h1>
<div id="toggle-container"></div>
</header>
<script type="module">
import { createToggle, getToggleHTML, getToggleCSS } from 'nightfall-css/script'
// Inject toggle CSS
const style = document.createElement('style')
style.textContent = getToggleCSS()
document.head.appendChild(style)
// Inject toggle button
document.querySelector('#toggle-container').innerHTML = getToggleHTML()
// Initialize toggle logic
const toggle = createToggle({ storageKey: 'nightfall-theme' })
document.querySelector('#nightfall-toggle')
.addEventListener('click', toggle.toggle)
</script>
</body>
</html>CDN Usage
For projects without a build step, use the CDN version:
<script src="https://unpkg.com/nightfall-css/dist/script.umd.js"></script>
<script>
const toggle = NightfallCSS.createToggle({ storageKey: 'theme' })
document.querySelector('#btn').addEventListener('click', toggle.toggle)
</script>