Skip to content

Commit

Permalink
feat: support dark mode, close #710
Browse files Browse the repository at this point in the history
  • Loading branch information
hyoban committed Feb 11, 2024
1 parent 04303f6 commit 22b42cf
Show file tree
Hide file tree
Showing 13 changed files with 191 additions and 81 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"async-lock": "^1.4.1",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.0",
"foxact": "^0.2.31",
"he": "1.2.0",
"lodash": "^4.17.21",
"lucide-react": "^0.320.0",
Expand Down
32 changes: 28 additions & 4 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions src/lib/components/AppearanceSwitch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { useDark } from "~/lib/hooks/use-dark"

export function AppearanceSwitch({ className = "" }: { className?: string }) {
const { toggleDark } = useDark()

return (
<button type="button" onClick={toggleDark} className={"flex " + className}>
<div className="i-mingcute-sun-2-line scale-100 dark:scale-0 transition-transform duration-500 rotate-0 dark:-rotate-90" />
<div className="i-mingcute-moon-line absolute scale-0 dark:scale-100 transition-transform duration-500 rotate-90 dark:rotate-0" />
<span className="sr-only">Toggle theme</span>
</button>
)
}
2 changes: 1 addition & 1 deletion src/lib/components/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const buttonVariants = cva(
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
rss: "border border-orange-500 text-orange-500 bg-background hover:bg-orange-500 hover:text-white",
rss: "border border-primary text-primary bg-background hover:bg-primary hover:text-white",
},
size: {
default: "h-10 px-4 py-2",
Expand Down
67 changes: 67 additions & 0 deletions src/lib/hooks/use-dark.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { useLocalStorage } from "foxact/use-local-storage"
import { useEffect, useMemo, useSyncExternalStore } from "react"

const query = "(prefers-color-scheme: dark)"

function getSnapshot() {
return window.matchMedia(query).matches
}

function getServerSnapshot(): undefined {
return undefined
}

function subscribe(callback: () => void) {
const matcher = window.matchMedia(query)
matcher.addEventListener("change", callback)
return () => {
matcher.removeEventListener("change", callback)
}
}

function useSystemDark() {
return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot)
}

const themeOptions = ["system", "light", "dark"] as const
export type Theme = (typeof themeOptions)[number]

function isDarkMode(setting?: Theme | null, isSystemDark?: boolean) {
return setting === "dark" || (isSystemDark && setting !== "light")
}

export function useDark(themeKey = "use-dark") {
const [theme, setTheme] = useLocalStorage<Theme>(themeKey, "system")
const isSystemDark = useSystemDark()

const isDark = useMemo(
() => isDarkMode(theme, isSystemDark),
[isSystemDark, theme],
)

const toggleDark = () => {
if (theme === "system") {
setTheme(isSystemDark ? "light" : "dark")
} else {
setTheme("system")
}
}

useEffect(() => {
const isDark = isDarkMode(theme, isSystemDark)
if (isDark) {
document.documentElement.classList.toggle("dark", true)
} else {
document.documentElement.classList.toggle("dark", false)
}

if (
(theme === "dark" && isSystemDark) ||
(theme === "light" && !isSystemDark)
) {
setTheme("system")
}
}, [theme, isSystemDark, setTheme])

return { isDark, toggleDark }
}
108 changes: 54 additions & 54 deletions src/lib/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,62 +2,62 @@
@tailwind components;
@tailwind utilities;

@layer base {
:root {
--background: 0 0% 100%;
--foreground: 20 14.3% 4.1%;
--card: 0 0% 100%;
--card-foreground: 20 14.3% 4.1%;
--popover: 0 0% 100%;
--popover-foreground: 20 14.3% 4.1%;
--primary: 24.6 95% 53.1%;
--primary-foreground: 60 9.1% 97.8%;
--secondary: 60 4.8% 95.9%;
--secondary-foreground: 24 9.8% 10%;
--muted: 60 4.8% 95.9%;
--muted-foreground: 25 5.3% 44.7%;
--accent: 60 4.8% 95.9%;
--accent-foreground: 24 9.8% 10%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 60 9.1% 97.8%;
--border: 20 5.9% 90%;
--input: 20 5.9% 90%;
--ring: 24.6 95% 53.1%;
--radius: 0.5rem;
}
:root {
--background: 0 0% 100%;
--foreground: 20 14.3% 4.1%;
--card: 0 0% 100%;
--card-foreground: 20 14.3% 4.1%;
--popover: 0 0% 100%;
--popover-foreground: 20 14.3% 4.1%;
--primary: 24.6 95% 53.1%;
--primary-foreground: 60 9.1% 97.8%;
--secondary: 60 4.8% 95.9%;
--secondary-foreground: 24 9.8% 10%;
--muted: 60 4.8% 95.9%;
--muted-foreground: 25 5.3% 44.7%;
--accent: 60 4.8% 95.9%;
--accent-foreground: 24 9.8% 10%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 60 9.1% 97.8%;
--border: 20 5.9% 90%;
--input: 20 5.9% 90%;
--ring: 24.6 95% 53.1%;
--radius: 0.5rem;
}

.dark {
--background: 20 14.3% 4.1%;
--foreground: 60 9.1% 97.8%;
--card: 20 14.3% 4.1%;
--card-foreground: 60 9.1% 97.8%;
--popover: 20 14.3% 4.1%;
--popover-foreground: 60 9.1% 97.8%;
--primary: 20.5 90.2% 48.2%;
--primary-foreground: 60 9.1% 97.8%;
--secondary: 12 6.5% 15.1%;
--secondary-foreground: 60 9.1% 97.8%;
--muted: 12 6.5% 15.1%;
--muted-foreground: 24 5.4% 63.9%;
--accent: 12 6.5% 15.1%;
--accent-foreground: 60 9.1% 97.8%;
--destructive: 0 72.2% 50.6%;
--destructive-foreground: 60 9.1% 97.8%;
--border: 12 6.5% 15.1%;
--input: 12 6.5% 15.1%;
--ring: 20.5 90.2% 48.2%;
}

.dark {
--background: 20 14.3% 4.1%;
--foreground: 60 9.1% 97.8%;
--card: 20 14.3% 4.1%;
--card-foreground: 60 9.1% 97.8%;
--popover: 20 14.3% 4.1%;
--popover-foreground: 60 9.1% 97.8%;
--primary: 20.5 90.2% 48.2%;
--primary-foreground: 60 9.1% 97.8%;
--secondary: 12 6.5% 15.1%;
--secondary-foreground: 60 9.1% 97.8%;
--muted: 12 6.5% 15.1%;
--muted-foreground: 24 5.4% 63.9%;
--accent: 12 6.5% 15.1%;
--accent-foreground: 60 9.1% 97.8%;
--destructive: 0 72.2% 50.6%;
--destructive-foreground: 60 9.1% 97.8%;
--border: 12 6.5% 15.1%;
--input: 12 6.5% 15.1%;
--ring: 20.5 90.2% 48.2%;
}
.dark {
color-scheme: dark;
}

@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}

.content a {
@apply underline text-orange-500;
}
.content a {
@apply underline text-primary;
}
10 changes: 6 additions & 4 deletions src/options/Siderbar.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import RSSHubIcon from "data-base64:~/assets/icon.png"
import { Link, useLocation } from "react-router-dom"

import { AppearanceSwitch } from "~/lib/components/AppearanceSwitch"
import { cn } from "~/lib/utils"

import info from "../../package.json"
Expand Down Expand Up @@ -28,7 +29,7 @@ function Siderbar() {
return (
<div className="flex flex-col h-[calc(100vh-80px)] sticky top-10">
<div>
<div className="px-4 flex items-center space-x-2 text-orange-500 text-xl font-bold mb-8">
<div className="px-4 flex items-center space-x-2 text-primary text-xl font-bold mb-8">
<img className="w-10 h-10" src={RSSHubIcon} />
<span>RSSHub Radar</span>
</div>
Expand All @@ -40,9 +41,9 @@ function Siderbar() {
to={link.path}
className={cn(
location.pathname === link.path
? "bg-orange-50 text-orange-500"
? "bg-primary/10 text-primary"
: "",
"px-4 py-3 hover:bg-orange-50 transition-colors rounded-lg flex items-center space-x-2",
"px-4 py-3 hover:bg-primary/10 transition-colors rounded-lg flex items-center space-x-2",
)}
>
<i className={link.icon + " w-5 h-5"}></i>
Expand All @@ -52,10 +53,11 @@ function Siderbar() {
))}
</ul>
<footer className="text-zinc-500 text-center text-sm">
<AppearanceSwitch className="mx-auto mb-2 text-2xl" />
<p>Version v{info.version}</p>
<p>
Made with <span className="text-red-500"></span> by{" "}
<a className="underline text-orange-500" href="https://diygod.cc/">
<a className="underline text-primary" href="https://diygod.cc/">
DIYgod
</a>
</p>
Expand Down
13 changes: 8 additions & 5 deletions src/options/index.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import { MemoryRouter } from "react-router-dom"

import { Routing } from "~/options/routes"

import "~/lib/style.css"

import { Toaster } from "react-hot-toast"
import { MemoryRouter } from "react-router-dom"

import { Routing } from "~/options/routes"

import Siderbar from "./Siderbar"

function Options() {
return (
<div className="max-w-screen-lg mx-auto text-base py-10">
<Toaster />
<Toaster
toastOptions={{
className: "!bg-secondary !text-secondary-foreground",
}}
/>
<MemoryRouter>
<div className="flex space-x-20 h-full">
<Siderbar />
Expand Down
9 changes: 3 additions & 6 deletions src/options/routes/About.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ function About() {

return (
<div>
<h1 className="text-3xl font-medium leading-10 mb-6 text-orange-500 border-b pb-4">
<h1 className="text-3xl font-medium leading-10 mb-6 text-primary border-b pb-4">
{chrome.i18n.getMessage("about")}
</h1>
<div className="space-y-4">
<Card>
<CardContent className="space-y-4 content text-zinc-700 py-10">
<CardContent className="space-y-4 content text-zinc-700 dark:text-zinc-300 py-10">
<p
dangerouslySetInnerHTML={{
__html: chrome.i18n.getMessage("RSSHubRadarInfo"),
Expand Down Expand Up @@ -60,10 +60,7 @@ function About() {
<p>&nbsp;</p>
<p>
Made with <span className="text-red-500"></span> by{" "}
<a
className="underline text-orange-500"
href="https://diygod.cc/"
>
<a className="underline text-primary" href="https://diygod.cc/">
DIYgod
</a>
</p>
Expand Down
2 changes: 1 addition & 1 deletion src/options/routes/General.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ function General() {

return (
<div>
<h1 className="text-3xl font-medium leading-10 mb-6 text-orange-500 border-b pb-4">
<h1 className="text-3xl font-medium leading-10 mb-6 text-primary border-b pb-4">
{chrome.i18n.getMessage("general")}
</h1>
<div className="space-y-4">
Expand Down

0 comments on commit 22b42cf

Please sign in to comment.