Mobile Dark Mode & Theme Switch

The mobile navigation has been wired up, and now includes a working theme switch button.
This commit is contained in:
Refringe 2024-06-24 16:35:43 -04:00
parent 8d2f6f31e6
commit 73c05a1b80
Signed by: Refringe
SSH Key Fingerprint: SHA256:t865XsQpfTeqPRBMN2G6+N8wlDjkgUCZF3WGW6O9N/k
3 changed files with 69 additions and 87 deletions

View File

@ -1,11 +1,11 @@
document.addEventListener("DOMContentLoaded", function() {
const themeToggleIcon = {
dark: document.getElementById("theme-toggle-dark-icon"),
light: document.getElementById("theme-toggle-light-icon")
const themeToggleIcons = {
dark: Array.from(document.querySelectorAll(".theme-toggle-dark-icon")),
light: Array.from(document.querySelectorAll(".theme-toggle-light-icon"))
};
// Make sure the theme toggle icons are available.
if (themeToggleIcon.dark === null || themeToggleIcon.light === null) {
if (themeToggleIcons.dark.length === 0 || themeToggleIcons.light.length === 0) {
console.log("Theme toggle icons not found.");
return;
}
@ -13,11 +13,11 @@ document.addEventListener("DOMContentLoaded", function() {
// Function to update the visibility of the theme icons based on the theme
function updateIconVisibility(theme) {
if (theme === "dark") {
themeToggleIcon.dark.classList.add("hidden");
themeToggleIcon.light.classList.remove("hidden");
themeToggleIcons.dark.forEach(icon => icon.classList.add("hidden"));
themeToggleIcons.light.forEach(icon => icon.classList.remove("hidden"));
} else {
themeToggleIcon.dark.classList.remove("hidden");
themeToggleIcon.light.classList.add("hidden");
themeToggleIcons.dark.forEach(icon => icon.classList.remove("hidden"));
themeToggleIcons.light.forEach(icon => icon.classList.add("hidden"));
}
}
@ -41,13 +41,15 @@ document.addEventListener("DOMContentLoaded", function() {
document.documentElement.classList.add(initialTheme); // Ensure the class is set
updateIconVisibility(initialTheme);
// Set up the theme toggle button
const themeToggleBtn = document.getElementById("theme-toggle");
themeToggleBtn.addEventListener("click", function() {
// Set up the theme toggle buttons
const themeToggleButtons = Array.from(document.querySelectorAll(".theme-toggle"));
themeToggleButtons.forEach(button => {
button.addEventListener("click", function() {
// Determine the current theme by checking the classList of documentElement
const currentTheme = document.documentElement.classList.contains("dark") ? "dark" : "light";
const newTheme = currentTheme === "light" ? "dark" : "light";
updateTheme(newTheme);
});
});
});

View File

@ -2,8 +2,8 @@
@php
$classes = ($active ?? false)
? 'block w-full ps-3 pe-4 py-2 border-l-4 border-grey-400 text-start text-base font-medium text-gray-700 bg-gray-50 dark:bg-gray-800 focus:outline-none focus:text-gray-800 dark:focus:text-gray-300 focus:bg-gray-100 dark:focus:bg-gray-700 focus:border-gray-700 dark:focus:border-gray-700 transition duration-150 ease-in-out'
: 'block w-full ps-3 pe-4 py-2 border-l-4 border-transparent text-start text-base font-medium text-gray-600 dark:text-gray-300 hover:text-gray-800 dark:hover:text-gray-500 hover:bg-gray-50 dark:hover:bg-gray-800 hover:border-gray-300 dark:hover:border-gray-700 focus:outline-none focus:text-gray-800 dark:focus:text-gray-300 focus:bg-gray-50 dark:focus:bg-gray-700 focus:border-gray-300 dark:focus:border-gray-700 transition duration-150 ease-in-out';
? 'block rounded-md px-3 py-2 text-base font-medium text-gray-700 dark:text-gray-100 bg-gray-100 dark:bg-gray-900 transition duration-150 ease-in-out'
: 'block rounded-md px-3 py-2 text-base font-medium text-gray-700 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-700 hover:text-gray-800 dark:hover:text-white transition duration-150 ease-in-out';
@endphp
<a {{ $attributes->merge(['class' => $classes]) }}>

View File

@ -1,4 +1,4 @@
<nav x-data="{ open: false }" class="bg-white dark:bg-gray-950 border-b border-gray-100 dark:border-gray-800">
<nav x-data="{ profileDropdownOpen: false, mobileMenuOpen: false }" class="bg-white dark:bg-gray-950 border-b border-gray-100 dark:border-gray-800">
<div class="mx-auto max-w-7xl px-2 sm:px-4 lg:px-8">
<div class="relative flex h-16 items-center justify-between">
<div class="flex items-center px-2 lg:px-0">
@ -12,6 +12,7 @@
@auth
<x-nav-link href="{{ route('dashboard') }}" :active="request()->routeIs('dashboard')">{{ __('Dashboard') }}</x-nav-link>
@endauth
{{-- additional menu links here --}}
</div>
</div>
</div>
@ -29,23 +30,13 @@
</div>
</div>
<div class="flex lg:hidden">
<!-- Mobile menu button -->
<button type="button" class="relative inline-flex items-center justify-center rounded-md p-2 text-gray-400 hover:bg-gray-700 hover:text-white focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white" aria-controls="mobile-menu" aria-expanded="false">
{{-- Mobile Menu Button --}}
<button @click="mobileMenuOpen = !mobileMenuOpen" type="button" class="relative inline-flex items-center justify-center rounded-md p-2 text-gray-400 hover:bg-gray-700 hover:text-white focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white" aria-controls="mobile-menu" :aria-expanded="mobileMenuOpen">
<span class="absolute -inset-0.5"></span>
<span class="sr-only">{{ __('Open main menu') }}</span>
<!--
Icon when menu is closed.
Menu open: "hidden", Menu closed: "block"
-->
<svg class="block h-6 w-6" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" />
</svg>
<!--
Icon when menu is open.
Menu open: "block", Menu closed: "hidden"
-->
<svg class="hidden h-6 w-6" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg>
@ -53,27 +44,27 @@
</div>
<div class="hidden lg:ml-4 lg:block">
<div class="flex items-center gap-4">
{{-- Theme Toggle --}}
<button id="theme-toggle" type="button" class="relative flex-shrink-0 rounded-full p-1 bg-white dark:bg-gray-800 text-gray-400 hover:text-gray-500 dark:hover:text-white focus:outline-none focus:ring-2 focus:ring-gray-500 dark:focus:ring-white focus:ring-offset-2 dark:focus:ring-offset-gray-800">
{{-- Desktop Theme Toggle --}}
<button type="button" class="theme-toggle relative flex-shrink-0 rounded-full p-1 bg-white dark:bg-gray-800 text-gray-400 hover:text-gray-500 dark:hover:text-white focus:outline-none focus:ring-2 focus:ring-gray-500 dark:focus:ring-white focus:ring-offset-2 dark:focus:ring-offset-gray-800">
<span class="absolute -inset-1.5"></span>
<span class="sr-only">{{ __('Toggle themes') }}</span>
<svg id="theme-toggle-dark-icon" class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20" aria-hidden="true">
<svg class="theme-toggle-dark-icon w-6 h-6" fill="currentColor" viewBox="0 0 20 20" aria-hidden="true">
<path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z"></path>
</svg>
<svg id="theme-toggle-light-icon" class="hidden w-6 h-6" fill="currentColor" viewBox="0 0 20 20" aria-hidden="true">
<svg class="theme-toggle-light-icon hidden w-6 h-6" fill="currentColor" viewBox="0 0 20 20" aria-hidden="true">
<path d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z" fill-rule="evenodd" clip-rule="evenodd"></path>
</svg>
</button>
@auth
{{-- Profile Dropdown --}}
<div x-data="{ isOpen: false, openedWithKeyboard: false }" @keydown.esc.window="isOpen = false, openedWithKeyboard = false" class="relative">
<button id="user-menu-button" type="button" @click="isOpen = ! isOpen" @keydown.space.prevent="openedWithKeyboard = true" @keydown.enter.prevent="openedWithKeyboard = true" @keydown.down.prevent="openedWithKeyboard = true" class="relative flex rounded-full bg-gray-800 text-sm text-white focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800" :class="isOpen || openedWithKeyboard ? 'text-black dark:text-white' : 'text-slate-700 dark:text-slate-300'" :aria-expanded="isOpen || openedWithKeyboard" aria-haspopup="true">
<div x-data="{ profileDropdownOpen: false, openedWithKeyboard: false }" @keydown.esc.window="profileDropdownOpen = false, openedWithKeyboard = false" class="relative">
<button id="user-menu-button" type="button" @click="profileDropdownOpen = ! profileDropdownOpen" @keydown.space.prevent="openedWithKeyboard = true" @keydown.enter.prevent="openedWithKeyboard = true" @keydown.down.prevent="openedWithKeyboard = true" class="relative flex rounded-full bg-gray-800 text-sm text-white focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800" :class="profileDropdownOpen || openedWithKeyboard ? 'text-black dark:text-white' : 'text-slate-700 dark:text-slate-300'" :aria-expanded="profileDropdownOpen || openedWithKeyboard" aria-haspopup="true">
<span class="absolute -inset-1.5"></span>
<span class="sr-only">{{ __('Open user menu') }}</span>
<img class="h-8 w-8 rounded-full" src="{{ auth()->user()->profile_photo_url }}" alt="{{ auth()->user()->name }}">
</button>
<div x-cloak x-show="isOpen || openedWithKeyboard" x-transition x-trap="openedWithKeyboard" @click.outside="isOpen = false, openedWithKeyboard = false" @keydown.down.prevent="$focus.wrap().next()" @keydown.up.prevent="$focus.wrap().previous()" class="absolute top-11 right-0 z-10 flex w-full min-w-[12rem] flex-col divide-y divide-slate-300 overflow-hidden rounded-xl border border-slate-300 bg-slate-100 dark:divide-slate-700 dark:border-slate-700 dark:bg-slate-800" role="menu">
<div x-cloak x-show="profileDropdownOpen || openedWithKeyboard" x-transition x-trap="openedWithKeyboard" @click.outside="profileDropdownOpen = false, openedWithKeyboard = false" @keydown.down.prevent="$focus.wrap().next()" @keydown.up.prevent="$focus.wrap().previous()" class="absolute top-11 right-0 z-10 flex w-full min-w-[12rem] flex-col divide-y divide-slate-300 overflow-hidden rounded-xl border border-slate-300 bg-slate-100 dark:divide-slate-700 dark:border-slate-700 dark:bg-slate-800" role="menu">
<div class="flex flex-col py-1.5">
<a href="{{ route('dashboard') }}" class="flex items-center gap-2 bg-slate-100 px-4 py-2 text-sm text-slate-700 hover:bg-slate-800/5 hover:text-black focus-visible:bg-slate-800/10 focus-visible:text-black focus-visible:outline-none dark:bg-slate-800 dark:text-slate-300 dark:hover:bg-slate-100/5 dark:hover:text-white dark:focus-visible:bg-slate-100/10 dark:focus-visible:text-white" role="menuitem">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-4 h-4">
@ -113,74 +104,63 @@
</div>
</div>
@endauth
@guest
<div>
<a href="{{ route('login') }}" class="text-sm font-semibold leading-6 text-gray-900 dark:text-gray-100 whitespace-nowrap">Log in <span aria-hidden="true">&rarr;</span></a>
</div>
@endguest
{{--
<div class="relative ml-4 flex-shrink-0">
<div>
<button type="button" class="relative flex rounded-full bg-gray-800 text-sm text-white focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800" id="user-menu-button" aria-expanded="false" aria-haspopup="true">
<span class="absolute -inset-1.5"></span>
<span class="sr-only">{{ __('Open user menu') }}</span>
<img class="h-8 w-8 rounded-full" src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt="">
</button>
</div>
<!--
Dropdown menu, show/hide based on menu state.
Entering: "transition ease-out duration-100"
From: "transform opacity-0 scale-95"
To: "transform opacity-100 scale-100"
Leaving: "transition ease-in duration-75"
From: "transform opacity-100 scale-100"
To: "transform opacity-0 scale-95"
-->
<div class="absolute right-0 z-10 mt-2 w-48 origin-top-right rounded-md bg-white py-1 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none" role="menu" aria-orientation="vertical" aria-labelledby="user-menu-button" tabindex="-1">
<!-- Active: "bg-gray-100", Not Active: "" -->
<a href="#" class="block px-4 py-2 text-sm text-gray-700" role="menuitem" tabindex="-1" id="user-menu-item-0">Your Profile</a>
<a href="#" class="block px-4 py-2 text-sm text-gray-700" role="menuitem" tabindex="-1" id="user-menu-item-1">Settings</a>
<a href="#" class="block px-4 py-2 text-sm text-gray-700" role="menuitem" tabindex="-1" id="user-menu-item-2">Sign out</a>
</div>
</div>
--}}
</div>
</div>
</div>
</div>
<!-- Mobile menu, show/hide based on menu state. -->
<div class="lg:hidden" id="mobile-menu">
{{-- Mobile Menu --}}
<div class="lg:hidden" x-show="mobileMenuOpen" id="mobile-menu">
<div class="space-y-1 px-2 pb-3 pt-2">
<!-- Current: "bg-gray-900 text-white", Default: "text-gray-300 hover:bg-gray-700 hover:text-white" -->
<a href="#" class="block rounded-md bg-gray-900 px-3 py-2 text-base font-medium text-white">Dashboard</a>
<a href="#" class="block rounded-md px-3 py-2 text-base font-medium text-gray-300 hover:bg-gray-700 hover:text-white">Team</a>
<a href="#" class="block rounded-md px-3 py-2 text-base font-medium text-gray-300 hover:bg-gray-700 hover:text-white">Projects</a>
<a href="#" class="block rounded-md px-3 py-2 text-base font-medium text-gray-300 hover:bg-gray-700 hover:text-white">Calendar</a>
@auth
<x-responsive-nav-link href="{{ route('dashboard') }}" :active="request()->routeIs('dashboard')">{{ __('Dashboard') }}</x-responsive-nav-link>
@endauth
{{-- Additional menu links here --}}
</div>
<div class="border-t border-gray-700 pb-3 pt-4">
<div class="border-t border-gray-300 dark:border-gray-700 pb-3 pt-4">
<div class="flex items-center px-5">
@auth
<div class="flex-shrink-0">
<img class="h-10 w-10 rounded-full" src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80" alt="">
<img class="h-10 w-10 rounded-full" src="{{ auth()->user()->profile_photo_url }}" alt="{{ auth()->user()->name }}">
</div>
<div class="ml-3">
<div class="text-base font-medium text-white">Tom Cook</div>
<div class="text-sm font-medium text-gray-400">tom@example.com</div>
<div class="text-base font-medium text-gray-900 dark:text-gray-100">{{ auth()->user()->name }}</div>
<div class="text-sm font-medium text-gray-700 dark:text-gray-300">{{ auth()->user()->email }}</div>
</div>
<button type="button" class="relative ml-auto flex-shrink-0 rounded-full bg-gray-800 p-1 text-gray-400 hover:text-white focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800">
@endauth
{{-- Mobile Theme Toggle --}}
<button type="button" class="theme-toggle relative ml-auto flex-shrink-0 rounded-full p-1 bg-white dark:bg-gray-800 text-gray-400 hover:text-gray-500 dark:hover:text-white focus:outline-none focus:ring-2 focus:ring-gray-500 dark:focus:ring-white focus:ring-offset-2 dark:focus:ring-offset-gray-800">
<span class="absolute -inset-1.5"></span>
<span class="sr-only">View notifications</span>
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M14.857 17.082a23.848 23.848 0 005.454-1.31A8.967 8.967 0 0118 9.75v-.7V9A6 6 0 006 9v.75a8.967 8.967 0 01-2.312 6.022c1.733.64 3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 01-5.714 0m5.714 0a3 3 0 11-5.714 0" />
<span class="sr-only">{{ __('Toggle themes') }}</span>
<svg class="theme-toggle-dark-icon w-6 h-6" fill="currentColor" viewBox="0 0 20 20" aria-hidden="true">
<path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z"></path>
</svg>
<svg class="theme-toggle-light-icon hidden w-6 h-6" fill="currentColor" viewBox="0 0 20 20" aria-hidden="true">
<path d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z" fill-rule="evenodd" clip-rule="evenodd"></path>
</svg>
</button>
</div>
<div class="mt-3 space-y-1 px-2">
<a href="#" class="block rounded-md px-3 py-2 text-base font-medium text-gray-400 hover:bg-gray-700 hover:text-white">Your Profile</a>
<a href="#" class="block rounded-md px-3 py-2 text-base font-medium text-gray-400 hover:bg-gray-700 hover:text-white">Settings</a>
<a href="#" class="block rounded-md px-3 py-2 text-base font-medium text-gray-400 hover:bg-gray-700 hover:text-white">Sign out</a>
@auth
<x-responsive-nav-link href="{{ route('profile.show') }}" :active="request()->routeIs('profile.show')">{{ __('Profile') }}</x-responsive-nav-link>
<x-responsive-nav-link href="{{ route('api-tokens.index') }}" :active="request()->routeIs('api-tokens.index')">{{ __('API Token') }}</x-responsive-nav-link>
<form method="POST" action="{{ route('logout') }}" x-data>
@csrf
<x-responsive-nav-link href="{{ route('logout') }}" @click.prevent="$root.submit();" :active="request()->routeIs('logout')">{{ __('Log Out') }}</x-responsive-nav-link>
</form>
@endauth
@guest
<x-responsive-nav-link href="{{ route('login') }}" :active="request()->routeIs('login')">{{ __('Log in') }}</x-responsive-nav-link>
<x-responsive-nav-link href="{{ route('register') }}" :active="request()->routeIs('register')">{{ __('Register') }}</x-responsive-nav-link>
@endguest
</div>
</div>
</div>