Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
automatic (prefers-color-scheme) and manual CSS theme switcher with vanilla javascript snippet
.theme input + label {
border-bottom: 3px solid transparent;
}
.theme input:checked + label {
border-bottom: 3px solid #ccc;
}
.theme,
.theme input {
display: none;
}
<!-- <head> needs to contain the alternative style sheet -->
<style media="all">
# normal CSS
</style>
<style id="css_alt" media="speech">
# alt CSS
</style>
<!-- include this into the <body> to have controls -->
<form class="theme" aria-hidden="true">
<span>
🔆
</span>
<span>
<input name="colorscheme" value="dark" id="darkscheme" type="radio">
<label for="darkscheme">dark</label>
</span>
<span>
<input name="colorscheme" value="light" id="lightscheme" type="radio">
<label for="lightscheme">light</label>
</span>
</form>
var DEFAULT_THEME = 'dark';
var ALT_THEME = 'light';
var STORAGE_KEY = 'theme';
var colorscheme = document.getElementsByName('colorscheme');
function indicateTheme(mode) {
for(var i = colorscheme.length; i--; ) {
if(colorscheme[i].value == mode) {
colorscheme[i].checked = true;
}
}
}
function applyTheme(mode) {
var st = document.getElementById('css_alt');
if (mode == ALT_THEME) {
st.setAttribute('media', 'all');
}
else {
st.setAttribute('media', 'speech');
}
}
function setTheme(e) {
var mode = e.target.value;
var mql = window.matchMedia('(prefers-color-scheme: ' + ALT_THEME + ')');
/* user wants == mql match => remove storage */
if ((mode == DEFAULT_THEME && !mql.matches) || (mode == ALT_THEME && mql.matches)) {
localStorage.removeItem(STORAGE_KEY);
}
/* avoid needing cookie banners; besides, messages like this would make people understand that clicking on a thing
might do something to their computer */
else {
if(confirm("I\'ll need to store your theme of choice in your browser, in a place called localStorage.\n\nAre you OK with this?")) {
localStorage.setItem(STORAGE_KEY, mode);
}
}
autoTheme(mql);
}
function autoTheme(e) {
var mode = DEFAULT_THEME;
try {
var current = localStorage.getItem(STORAGE_KEY);
} catch(e) {
var current = DEFAULT_THEME;
}
if ( current != null) {
mode = current;
}
else if (e.matches) {
mode = ALT_THEME;
}
applyTheme(mode);
indicateTheme(mode);
}
/* at 2019-01 this only works in Dev Safari on Mojave */
var mql = window.matchMedia('(prefers-color-scheme: ' + ALT_THEME + ')');
autoTheme(mql);
mql.addListener(autoTheme);
/* only show controls if localstorage is available */
var test = 'ping';
try {
localStorage.setItem(test, test);
localStorage.removeItem(test);
/* add a listener for the controle */
for(var i = colorscheme.length; i--; ) {
colorscheme[i].onclick = setTheme;
}
/* show the control */
var themeforms = document.getElementsByClassName(STORAGE_KEY);
for(var i = themeforms.length; i--; ) {
themeforms[i].style.display = 'inline-block';
}
} catch(e) {
console.log('localStorage is not available, manual theme switching is disabled');
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.