Logo

SudoVersity

SudoMistress
Sep 27, 2023

TailwindCSS Theme Switching

I just wanted to quickly document the steps I took to make a theme toggle for a client.

Note: This is just one of many methods to do this, and I just found my task a lot easier once I implimented a few intersting tools.

βœ” Watch this Video and Understand the Implications of Dark Mode Color Selections.

Be sure to plan for dark mode first. Also, watch anything and everything that Juxtopposed puts out. Totally worth it!

VIDEO

Title: Rise of the Dark mode

Play

Plan for Dark Mode first!

βœ” Get your Realtime Colors On

Head over to Realtime Colors, from Juxtopposed and hit dark mode. Plan and swap back and forth till you are happy.

If you need help, use this video.

VIDEO

Title: I made Realtime Colors 100x Better (v3)

Play

βœ” Grab your TailwindCSS vars from Realtime Colors

  1. Place variables in a global.css file.
CODE
// Random Color Selection just for example
@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
  :root {
    --text: #101823;
    --background: #f7f7f7;
    --primary: #cd71f4;
    --secondary: #fb9518;
    --accent: #f27d7d;
  }
  .dark {
    --text: #dce4ef;
    --background: #080808;
    --primary: #670b8e;
    --secondary: #e78104;
    --accent: #800d0d;
  }
}
  1. Modify tailwind.config.cjs to use the variables. Seeting the darkMode: class export, it makes it very easy to simply swap themes based on CSS toggling with minimal JS.
CODE
// Ensure this content is present

module.exports = {
	// ...
	darkMode: 'class',
	theme: {
		fontSize: {
			sm: '0.750rem',
			base: '1rem',
			xl: '1.333rem',
			'2xl': '1.777rem',
			'3xl': '2.369rem',
			'4xl': '3.158rem',
			'5xl': '4.210rem'
		},
		colors: {
			text: 'var(--text)',
			background: 'var(--background)',
			primary: 'var(--primary)',
			secondary: 'var(--secondary)',
			accent: 'var(--accent)'
		},
	// ...
};
  1. Create Toggle components
CODE
<div class="flex w-[70px] items-center justify-end">
	<label class="relative inline-block h-[34px] w-[65px]" for="checkbox">
		<input class="hidden" type="checkbox" id="checkbox" />
		<div
			class="slider bg-accent checked:bg-accent absolute bottom-0 left-0 right-0 top-0 cursor-pointer rounded-full transition-transform"
		>
			<svg
				xmlns="http://www.w3.org/2000/svg"
				width="16"
				height="16"
				viewBox="0 0 24 24"
				fill="var(--background)"
				stroke="currentColor"
				stroke-width="2"
				stroke-linecap="round"
				stroke-linejoin="round"
				class="lightsun bottom-[9px] right-[9px] translate-x-0 opacity-100"
			>
				<circle cx="12" cy="12" r="5"></circle>
				<line x1="12" y1="1" x2="12" y2="3"></line>
				<line x1="12" y1="21" x2="12" y2="23"></line>
				<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
				<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
				<line x1="1" y1="12" x2="3" y2="12"></line>
				<line x1="21" y1="12" x2="23" y2="12"></line>
				<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
				<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
			</svg>
			<svg
				xmlns="http://www.w3.org/2000/svg"
				width="16"
				height="16"
				viewBox="0 0 24 24"
				fill="var(--background)"
				stroke="currentColor"
				stroke-width="2"
				stroke-linecap="round"
				stroke-linejoin="round"
				class="darkmoon bottom-[9px] left-[9px] translate-x-[4px] opacity-0"
			>
				<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
			</svg>
		</div>
	</label>
</div>

<style>
	/* I'm still working on shifting everything to tailwind, but at least it's here!  */

	.slider:before {
		background-color: var(--background);
		bottom: 4px;
		content: '';
		height: 26px;
		left: 4px;
		position: absolute;
		transition: 0.4s;
		width: 26px;
		border-radius: 50%;
	}

	input:checked + .slider:before {
		transform: translateX(26px);
	}

	.slider svg {
		color: var(--text);
		position: absolute;
		transition:
			opacity 0.2s ease 0s,
			transform 0.35s ease 0s;
		pointer-events: none;
	}

	input:checked + .slider .darkmoon {
		opacity: 1;
		transform: translateX(0);
	}

	input:checked + .slider .lightsun {
		opacity: 0;
		transform: translateX(-4px);
	}
</style>

Place the code below in a <script> tag in the .astro component.

CODE
const themeToggle = document.querySelector('input[type="checkbox"]')
const currentTheme = localStorage.getItem('theme')

if (currentTheme) {
	document.documentElement.classList.add(currentTheme)

	if (currentTheme === 'dark') {
		themeToggle.checked = true
	}
}

function switchTheme(e) {
	if (e.target.checked) {
		document.documentElement.classList.add('dark')
		document.documentElement.classList.remove('light')
		localStorage.setItem('theme', 'dark')
	} else {
		document.documentElement.classList.add('light')
		document.documentElement.classList.remove('dark')
		localStorage.setItem('theme', 'light')
	}
}

themeToggle.addEventListener('change', switchTheme)

Import the component where ya want and enjoy!

πŸš€ Explaination

By having the variables set in the global.css file, it’s insanely easy to have all the values for the variables change simply by adding the class dark to the html DOM element.