mirror of
https://github.com/liyunze-coding/coworking-website.git
synced 2026-11-06 13:13:34 +00:00
added theme toggle
This commit is contained in:
parent
21ddf2d621
commit
a42899e522
7 changed files with 256 additions and 17 deletions
|
|
@ -1,10 +1,11 @@
|
||||||
---
|
---
|
||||||
import VerticalNav from "./verticalNav.astro";
|
import VerticalNav from "./verticalNav.astro";
|
||||||
import SearchModal from "../ui/SearchModal.astro";
|
import SearchModal from "../ui/SearchModal.astro";
|
||||||
|
import ThemeToggle from "../ui/ThemeToggle.astro";
|
||||||
---
|
---
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="w-full backdrop-blur fixed top-0 text-white bg-secondary/75 z-[60] text-lg py-3 h-[var(--header-height)]"
|
class="site-nav w-full backdrop-blur fixed top-0 z-[60] text-lg py-3 h-[var(--header-height)]"
|
||||||
data-pagefind-ignore
|
data-pagefind-ignore
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
|
@ -21,7 +22,7 @@ import SearchModal from "../ui/SearchModal.astro";
|
||||||
<!-- Vertical Navbar -->
|
<!-- Vertical Navbar -->
|
||||||
<aside
|
<aside
|
||||||
id="vertical-navbar"
|
id="vertical-navbar"
|
||||||
class="sidebar fixed block overflow-y-auto duration-300 z-[60] lg:hidden bg-[#151515] py-8 px-8 h-[calc(100vh-var(--header-height))] top-[--header-height] left-0 w-full"
|
class="sidebar fixed block overflow-y-auto duration-300 z-[60] lg:hidden py-8 px-8 h-[calc(100vh-var(--header-height))] top-[--header-height] left-0 w-full"
|
||||||
transition:persist="vertical-nav-from-left"
|
transition:persist="vertical-nav-from-left"
|
||||||
>
|
>
|
||||||
<VerticalNav showHome={true} />
|
<VerticalNav showHome={true} />
|
||||||
|
|
@ -47,29 +48,32 @@ import SearchModal from "../ui/SearchModal.astro";
|
||||||
transition:name="navbar"
|
transition:name="navbar"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
class="no-target-blank hover:text-black hover:bg-white rounded-md transition-colors duration-300 px-2 py-1 text-white hover:no-underline"
|
class="no-target-blank nav-link rounded-md transition-colors duration-300 px-2 py-1 hover:no-underline"
|
||||||
href="/get-started/introduction">Get started</a
|
href="/get-started/introduction">Get started</a
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
class="no-target-blank hover:text-black hover:bg-white rounded-md transition-colors duration-300 px-2 py-1 text-white hover:no-underline"
|
class="no-target-blank nav-link rounded-md transition-colors duration-300 px-2 py-1 hover:no-underline"
|
||||||
href="/guide/general-tips">Guide</a
|
href="/guide/general-tips">Guide</a
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
class="no-target-blank hover:text-black hover:bg-white rounded-md transition-colors duration-300 px-2 py-1 text-white hover:no-underline"
|
class="no-target-blank nav-link rounded-md transition-colors duration-300 px-2 py-1 hover:no-underline"
|
||||||
href="/games">Games</a
|
href="/games">Games</a
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
class="no-target-blank hover:text-black hover:bg-white rounded-md transition-colors duration-300 px-2 py-1 text-white hover:no-underline"
|
class="no-target-blank nav-link rounded-md transition-colors duration-300 px-2 py-1 hover:no-underline"
|
||||||
href="/groups">Groups</a
|
href="/groups">Groups</a
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
class="no-target-blank hover:text-black hover:bg-white rounded-md transition-colors duration-300 px-2 py-1 text-white hover:no-underline"
|
class="no-target-blank nav-link rounded-md transition-colors duration-300 px-2 py-1 hover:no-underline"
|
||||||
href="/credits">Credits</a
|
href="/credits">Credits</a
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<!-- Search bar -->
|
<!-- Search bar -->
|
||||||
<div class="hidden lg:flex lg:flex-1 justify-end relative">
|
<div class="flex lg:flex-1 justify-end items-center gap-3 relative">
|
||||||
|
<div class="hidden lg:block">
|
||||||
<SearchModal id="searchModal" open="Open modal" />
|
<SearchModal id="searchModal" open="Open modal" />
|
||||||
|
</div>
|
||||||
|
<ThemeToggle />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -99,6 +103,34 @@ import SearchModal from "../ui/SearchModal.astro";
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.site-nav {
|
||||||
|
background: var(--nav-background);
|
||||||
|
color: var(--text-color);
|
||||||
|
border-bottom: 1px solid color-mix(in srgb, var(--border-color) 55%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-nav .sidebar {
|
||||||
|
background: var(--sidebar-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-nav a {
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-nav .nav-link:hover {
|
||||||
|
background: var(--control-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-theme="light"] {
|
||||||
|
--pagefind-ui-primary: #171717;
|
||||||
|
--pagefind-ui-text: #171717;
|
||||||
|
--pagefind-ui-background: #ffffff;
|
||||||
|
--pagefind-ui-border: #cbd5e1;
|
||||||
|
--pagefind-ui-tag: #171717;
|
||||||
|
--foreground: #171717;
|
||||||
|
--background: #171717;
|
||||||
|
}
|
||||||
|
|
||||||
.hamburger-menu {
|
.hamburger-menu {
|
||||||
--x-width: calc(var(--hamburger-height) * 1.41421356237);
|
--x-width: calc(var(--hamburger-height) * 1.41421356237);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<div class="text-center text-4xl lg:text-6xl font-bold">
|
<div class="text-center text-4xl lg:text-6xl font-bold">
|
||||||
<span class="text-slate-200">Getting started with</span>
|
<span class="text-[var(--text-color)]">Getting started with</span>
|
||||||
<div
|
<div
|
||||||
id="coworking-streams"
|
id="coworking-streams"
|
||||||
class="text-transparent animate-gradient py-1 lg:py-3 bg-[length:300%]"
|
class="text-transparent animate-gradient py-1 lg:py-3 bg-[length:300%]"
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ const { id } = Astro.props;
|
||||||
|
|
||||||
<button
|
<button
|
||||||
onclick={`window.${id}.showModal()`}
|
onclick={`window.${id}.showModal()`}
|
||||||
class="text-sm flex bg-[#101010] w-60 px-2 py-2 border border-gray-600 border-solid text-left rounded-lg group"
|
class="search-trigger text-sm flex w-60 px-2 py-2 border border-solid text-left rounded-lg group"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
|
@ -33,7 +33,7 @@ const { id } = Astro.props;
|
||||||
|
|
||||||
<dialog
|
<dialog
|
||||||
id=`${id}`
|
id=`${id}`
|
||||||
class="fixed left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 bg-[#202020] border-2 border-solid border-[#505050] text-white rounded-lg w-4/5 lg:w-1/2 h-5/6 px-10 py-10 animate-fadeIn"
|
class="search-dialog fixed left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 border-2 border-solid rounded-lg w-4/5 lg:w-1/2 h-5/6 px-10 py-10 animate-fadeIn"
|
||||||
>
|
>
|
||||||
<form method="dialog">
|
<form method="dialog">
|
||||||
<button class="absolute top-3 right-3"
|
<button class="absolute top-3 right-3"
|
||||||
|
|
@ -52,3 +52,17 @@ const { id } = Astro.props;
|
||||||
</form>
|
</form>
|
||||||
<div id=`${id}-search` class="mt-2"></div>
|
<div id=`${id}-search` class="mt-2"></div>
|
||||||
</dialog>
|
</dialog>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.search-trigger {
|
||||||
|
background: var(--control-background);
|
||||||
|
border-color: var(--border-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-dialog {
|
||||||
|
background: var(--surface-color);
|
||||||
|
border-color: var(--border-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
||||||
115
src/components/ui/ThemeToggle.astro
Normal file
115
src/components/ui/ThemeToggle.astro
Normal file
|
|
@ -0,0 +1,115 @@
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="theme-toggle"
|
||||||
|
aria-label="Switch to light theme"
|
||||||
|
title="Switch to light theme"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
class="sun-icon"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<circle cx="12" cy="12" r="4"></circle>
|
||||||
|
<path d="M12 2v2M12 20v2M4.93 4.93l1.42 1.42M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.42-1.42M17.66 6.34l1.41-1.41"></path>
|
||||||
|
</svg>
|
||||||
|
<svg
|
||||||
|
class="moon-icon"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79Z"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<script is:inline>
|
||||||
|
(() => {
|
||||||
|
const updateThemeToggle = () => {
|
||||||
|
const button = document.querySelector(".theme-toggle");
|
||||||
|
if (!button) return;
|
||||||
|
|
||||||
|
const isDark = document.documentElement.dataset.theme === "dark";
|
||||||
|
const nextTheme = isDark ? "light" : "dark";
|
||||||
|
button.setAttribute("aria-label", `Switch to ${nextTheme} theme`);
|
||||||
|
button.setAttribute("title", `Switch to ${nextTheme} theme`);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!window.__themeToggleReady) {
|
||||||
|
document.addEventListener("click", (event) => {
|
||||||
|
const button = event.target.closest(".theme-toggle");
|
||||||
|
if (!button) return;
|
||||||
|
|
||||||
|
const theme =
|
||||||
|
document.documentElement.dataset.theme === "dark"
|
||||||
|
? "light"
|
||||||
|
: "dark";
|
||||||
|
document.documentElement.dataset.theme = theme;
|
||||||
|
document.documentElement.style.colorScheme = theme;
|
||||||
|
localStorage.setItem("theme", theme);
|
||||||
|
updateThemeToggle();
|
||||||
|
});
|
||||||
|
window.__themeToggleReady = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener("astro:page-load", updateThemeToggle);
|
||||||
|
updateThemeToggle();
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.theme-toggle {
|
||||||
|
display: grid;
|
||||||
|
width: 2.25rem;
|
||||||
|
height: 2.25rem;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
place-items: center;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
background: var(--control-background);
|
||||||
|
color: var(--text-color);
|
||||||
|
cursor: pointer;
|
||||||
|
transition:
|
||||||
|
background-color 150ms ease,
|
||||||
|
border-color 150ms ease,
|
||||||
|
transform 150ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-toggle:hover {
|
||||||
|
background: var(--control-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-toggle:active {
|
||||||
|
transform: scale(0.94);
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-toggle:focus-visible {
|
||||||
|
outline: 2px solid #3f84d8;
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-toggle svg {
|
||||||
|
width: 1.25rem;
|
||||||
|
height: 1.25rem;
|
||||||
|
grid-area: 1 / 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sun-icon,
|
||||||
|
:global(html[data-theme="light"]) .moon-icon {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(html[data-theme="light"]) .sun-icon {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -67,7 +67,7 @@ export const Card = ({
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative z-20 h-full w-full overflow-hidden rounded-2xl border border-transparent bg-[#232323] p-4 group-hover:border-slate-700 dark:border-white/[0.2]",
|
"relative z-20 h-full w-full overflow-hidden rounded-2xl border border-[var(--card-border)] bg-[var(--card-background)] p-4 shadow-sm group-hover:border-[var(--card-border-hover)]",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|
@ -87,7 +87,7 @@ export const CardTitle = ({
|
||||||
return (
|
return (
|
||||||
<h4
|
<h4
|
||||||
className={cn(
|
className={cn(
|
||||||
"mt-4 font-bold tracking-wide text-zinc-100 text-xl",
|
"mt-4 font-bold tracking-wide text-[var(--text-color)] text-xl",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|
@ -105,7 +105,7 @@ export const CardDescription = ({
|
||||||
return (
|
return (
|
||||||
<p
|
<p
|
||||||
className={cn(
|
className={cn(
|
||||||
"mt-5 text-sm leading-relaxed tracking-wide text-zinc-400",
|
"mt-5 text-sm leading-relaxed tracking-wide text-[var(--muted-text)]",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ const { title, description } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en" data-theme="dark">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="description" content={description} />
|
<meta name="description" content={description} />
|
||||||
|
|
@ -48,6 +48,34 @@ const { title, description } = Astro.props;
|
||||||
|
|
||||||
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
||||||
<link href="/pagefind/pagefind-ui.css" rel="stylesheet" />
|
<link href="/pagefind/pagefind-ui.css" rel="stylesheet" />
|
||||||
|
<script is:inline>
|
||||||
|
(() => {
|
||||||
|
const applyTheme = (root) => {
|
||||||
|
const savedTheme = localStorage.getItem("theme");
|
||||||
|
const theme =
|
||||||
|
savedTheme === "light" || savedTheme === "dark"
|
||||||
|
? savedTheme
|
||||||
|
: window.matchMedia("(prefers-color-scheme: dark)")
|
||||||
|
.matches
|
||||||
|
? "dark"
|
||||||
|
: "light";
|
||||||
|
root.dataset.theme = theme;
|
||||||
|
root.style.colorScheme = theme;
|
||||||
|
};
|
||||||
|
|
||||||
|
applyTheme(document.documentElement);
|
||||||
|
|
||||||
|
if (!window.__themePersistenceReady) {
|
||||||
|
document.addEventListener("astro:before-swap", (event) => {
|
||||||
|
applyTheme(event.newDocument.documentElement);
|
||||||
|
});
|
||||||
|
document.addEventListener("astro:after-swap", () => {
|
||||||
|
applyTheme(document.documentElement);
|
||||||
|
});
|
||||||
|
window.__themePersistenceReady = true;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
<script src="/pagefind/pagefind-ui.js" is:inline></script>
|
<script src="/pagefind/pagefind-ui.js" is:inline></script>
|
||||||
<script is:inline src="/_vercel/insights/script.js"></script>
|
<script is:inline src="/_vercel/insights/script.js"></script>
|
||||||
<ClientRouter />
|
<ClientRouter />
|
||||||
|
|
@ -74,8 +102,8 @@ const { title, description } = Astro.props;
|
||||||
|
|
||||||
html {
|
html {
|
||||||
font-family: system-ui, sans-serif;
|
font-family: system-ui, sans-serif;
|
||||||
background-color: #171717;
|
background-color: var(--background-color);
|
||||||
color: rgb(255 255 255);
|
color: var(--text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
html,
|
html,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,55 @@
|
||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
|
|
||||||
|
:root,
|
||||||
|
html[data-theme="dark"] {
|
||||||
|
--background-color: #171717;
|
||||||
|
--surface-color: #202020;
|
||||||
|
--nav-background: rgb(23 23 23 / 0.82);
|
||||||
|
--sidebar-background: #151515;
|
||||||
|
--control-background: #101010;
|
||||||
|
--control-hover: #2f2f2f;
|
||||||
|
--text-color: #ffffff;
|
||||||
|
--muted-text: #9ca3af;
|
||||||
|
--border-color: #505050;
|
||||||
|
--card-background: #232323;
|
||||||
|
--card-border: rgb(255 255 255 / 0.2);
|
||||||
|
--card-border-hover: #334155;
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-theme="light"] {
|
||||||
|
--background-color: #f8fafc;
|
||||||
|
--surface-color: #ffffff;
|
||||||
|
--nav-background: rgb(255 255 255 / 0.86);
|
||||||
|
--sidebar-background: #f8fafc;
|
||||||
|
--control-background: #ffffff;
|
||||||
|
--control-hover: #e2e8f0;
|
||||||
|
--text-color: #171717;
|
||||||
|
--muted-text: #4b5563;
|
||||||
|
--border-color: #cbd5e1;
|
||||||
|
--card-background: #ffffff;
|
||||||
|
--card-border: #cbd5e1;
|
||||||
|
--card-border-hover: #94a3b8;
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
transition:
|
||||||
|
background-color 180ms ease,
|
||||||
|
color 180ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-theme="light"] .text-white {
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-theme="light"] .text-gray-400 {
|
||||||
|
color: var(--muted-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-theme="light"] .guide-content code {
|
||||||
|
background-color: #e2e8f0;
|
||||||
|
}
|
||||||
|
|
||||||
@theme {
|
@theme {
|
||||||
--color-primary: #fafafa;
|
--color-primary: #fafafa;
|
||||||
--color-secondary: #171717;
|
--color-secondary: #171717;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue