Skip to content

Instantly share code, notes, and snippets.

@Haeyzed
Created December 22, 2021 04:36
Show Gist options
  • Save Haeyzed/f8788b642b55537625bb5db6fda79ffd to your computer and use it in GitHub Desktop.
Save Haeyzed/f8788b642b55537625bb5db6fda79ffd to your computer and use it in GitHub Desktop.
Light/Dark Theme Toggle with CSS Variables (CSS, JS)
<div class="wrapper">
<div class="circle"></div>
<article>
<h2>Too bright?</h2>
<p>Look no further. Here we have a buttony twist on the classic checkbox toggler to carelessly toggle site theme between <span id="shift">lighter and darker<span>.</p><p>Go ahead and toggle a toggler below.</p>
<p>&#128071 &#128071 &#128071</p>
</article>
<button class="toggle" id="toggle"></button>
</div>

Light/Dark Theme Toggle with CSS Variables (CSS, JS)

Little theme switcher between bright and dark mode built with CSS custom properties and JavaScript which changes them on toggle click. Inspired by Florin Pop's coding challenges you should definitely check.

A Pen by mi-0-na on CodePen.

License.

/*
Inspired by Florin Pop's coding challenges, you can check them here: https://www.florin-pop.com/blog/2019/03/weekly-coding-challenge/
*/
// UI Elements
const toggle = document.getElementById("toggle"),
circle = document.querySelector(".circle"),
body = document.querySelector("body");
let span = document.getElementById("shift");
let theme = "bright";
// Helper function for setting theme parameters
// "params" needs to be written like object {"key1": "value1", "key2": "value2", ...}
function setStyles(element, params) {
for(let i in params) {
element.style.setProperty(i, params[i]);
}
}
// Main logic
function themeChange() {
theme = theme === "bright" ? "dark" : "bright";
document.querySelector("article > h2").textContent = `Too ${theme}?`;
if(theme === "bright") {
setStyles(body, {"--background": "#FBFFF1", "--text": "#3C3744", "--accent": "#F49F6E"});
toggle.classList.remove("clicked");
circle.classList.remove("clicked");
span.textContent = `brighter and darker.`;
} else {
setStyles(body, {"--background": "#3C3744", "--text": "#FBFFF1", "--accent": "#C41E3D"});
toggle.classList.add("clicked");
circle.classList.add("clicked");
span.textContent = `darker and brighter.`;
}
}
// Event listener for toggle button click
toggle.addEventListener("click", themeChange);
/* Imports */
@import url('https://fonts.googleapis.com/css?family=Roboto|Viga&display=swap');
/* Resets */
*, *::after, *::before {
box-sizing: border-box;
margin: 0;
padding: 0;
}
/* Button resets & styles */
button {
display: block;
border: none;
color: inherit;
padding: 0;
margin: 0;
cursor: pointer;
}
button:focus, button:active {
outline: none;
}
button:active {
transform: scale(1);
}
/* General styles */
body {
--background: #FBFFF1;
--text: #3C3744;
--accent: #F49D6E;
background-color: var(--background);
font-family: 'Roboto', sans-serif;
color: var(--text);
height: 100vh;
transition: color ease-out 250ms;
overflow-x: hidden;
}
.wrapper {
margin: auto;
height: 100%;
max-width: 540px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 1rem;
}
article {
margin-bottom: 1rem;
text-align: center;
}
h2 {
font-size: 2.2em;
font-family: 'Viga', sans-serif;
}
h2, * + p {
margin-bottom: 1rem;
}
article > p:nth-of-type(2) {
margin-top: -1rem;
}
/* Emoji custom styles */
article > p:last-of-type {
color: transparent;
text-shadow: 0 0 0 var(--text);
transition: text-shadow ease-out 250ms;
}
/* Toggle button styles */
.toggle {
background-color: var(--text);
min-width: 80px;
min-height: 40px;
margin-bottom: 1rem;
position: relative;
border: 2px solid;
border-radius: 30px;
cursor: pointer;
transition: background-color ease-out 250ms;
}
.toggle::after {
content: "";
position: absolute;
background-color: var(--background);
top: 0;
left: 0;
width: 50%;
height: 100%;
border-radius: 30px;
transition: transform ease-out 250ms;
}
/* Active (dark mode) class for animating transition on toggle button*/
.toggle.clicked::after {
transform: translateX(100%);
}
/* "Sun to moon" graphics and animation */
.circle, .circle::after {
background-color: var(--accent);
min-width: 300px;
min-height: 300px;
border-radius: 50%;
margin-bottom: 2rem;
}
.circle {
transform: rotate(-25deg);
transition: background-color ease-out 250ms;
}
.circle::after {
content: "";
position: absolute;
top: 0;
left: 0;
transform: translateY(-100%);
background-color: var(--background);
transition: transform ease-out 250ms;
transition-delay: 20ms;
}
.circle.clicked::after {
transform: translateY(-30%);
}
@media screen and (max-height: 560px) {
.circle, .circle::after {
margin-bottom: calc(-6rem - 12vh);
z-index: -1;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment