feat(web): toggle footer images with theme
|
|
@ -3,6 +3,7 @@ import { Geist, Geist_Mono } from "next/font/google";
|
|||
import "./globals.css";
|
||||
import { ThemeProvider } from "@/components/theme-provider";
|
||||
import { SiteHeader } from "@/components/site-header";
|
||||
import { SiteFooter } from "@/components/site-footer";
|
||||
|
||||
const geistSans = Geist({
|
||||
variable: "--font-geist-sans",
|
||||
|
|
@ -37,7 +38,10 @@ export default function RootLayout({
|
|||
disableTransitionOnChange
|
||||
>
|
||||
<SiteHeader />
|
||||
{children}
|
||||
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
|
||||
{children}
|
||||
<SiteFooter />
|
||||
</div>
|
||||
</ThemeProvider>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -1,54 +1,5 @@
|
|||
import Image from "next/image";
|
||||
import { FileJson2 } from "lucide-react";
|
||||
import CollinMBarrettIcon from "../assets/collinmbarrett-icon.png";
|
||||
import GitHubMark from "../assets/github-mark.svg";
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
|
||||
<main className="flex flex-col gap-[32px] row-start-2 items-center sm:items-start"></main>
|
||||
<footer className="row-start-3 flex gap-[24px] flex-wrap items-center justify-center">
|
||||
{/* TODO: toggle images in dark mode */}
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://collinmbarrett.com/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src={CollinMBarrettIcon}
|
||||
alt="Collin M. Barrett logo"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
by Collin M. Barrett
|
||||
</a>
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://api.filterlists.com/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<FileJson2 width={16} height={16} />
|
||||
API
|
||||
</a>
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://github.com/collinbarrett/FilterLists"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src={GitHubMark}
|
||||
alt="GitHub Logomark"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
GitHub
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
<main className="flex flex-col gap-[32px] row-start-2 items-center sm:items-start"></main>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
BIN
web/src/assets/collinmbarrett-icon-dark.png
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
web/src/assets/collinmbarrett-icon-light.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 138 KiB |
|
Before Width: | Height: | Size: 89 KiB |
|
Before Width: | Height: | Size: 960 B After Width: | Height: | Size: 960 B |
|
Before Width: | Height: | Size: 963 B After Width: | Height: | Size: 963 B |
54
web/src/components/site-footer.tsx
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
import { FileJson2 } from "lucide-react";
|
||||
import CollinMBarrettIconLight from "../assets/collinmbarrett-icon-light.png";
|
||||
import CollinMBarrettIconDark from "../assets/collinmbarrett-icon-dark.png";
|
||||
import GitHubMarkLight from "../assets/github-mark-light.svg";
|
||||
import GitHubMarkDark from "../assets/github-mark-dark.svg";
|
||||
import { ThemeImage } from "@/components/theme-image";
|
||||
|
||||
export function SiteFooter() {
|
||||
return (
|
||||
<footer className="row-start-3 flex gap-[24px] flex-wrap items-center justify-center">
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://collinmbarrett.com/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<ThemeImage
|
||||
aria-hidden
|
||||
srcLight={CollinMBarrettIconLight}
|
||||
srcDark={CollinMBarrettIconDark}
|
||||
alt="Collin M. Barrett logo"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
by Collin M. Barrett
|
||||
</a>
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://api.filterlists.com/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<FileJson2 width={16} height={16} />
|
||||
API
|
||||
</a>
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://github.com/collinbarrett/FilterLists"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<ThemeImage
|
||||
aria-hidden
|
||||
srcLight={GitHubMarkLight}
|
||||
srcDark={GitHubMarkDark}
|
||||
alt="GitHub Logomark"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
GitHub
|
||||
</a>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
"use client";
|
||||
|
||||
import { ThemeModeToggle } from "./theme-mode-toggle";
|
||||
|
||||
export function SiteHeader() {
|
||||
|
|
|
|||
33
web/src/components/theme-image.tsx
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
"use client";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import Image, { type ImageProps, type StaticImageData } from "next/image";
|
||||
import { useTheme } from "next-themes";
|
||||
|
||||
type ThemedImageProps = Omit<ImageProps, "src" | "alt"> & {
|
||||
srcLight: string | StaticImageData;
|
||||
srcDark: string | StaticImageData;
|
||||
alt: string;
|
||||
};
|
||||
|
||||
export function ThemeImage({
|
||||
srcLight,
|
||||
srcDark,
|
||||
alt,
|
||||
...props
|
||||
}: ThemedImageProps) {
|
||||
const { resolvedTheme } = useTheme();
|
||||
const [mounted, setMounted] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setMounted(true);
|
||||
}, []);
|
||||
|
||||
const src = resolvedTheme === "dark" ? srcDark : srcLight;
|
||||
|
||||
if (!mounted) {
|
||||
return <Image src={srcLight} alt={alt} {...props} unoptimized />;
|
||||
}
|
||||
|
||||
return <Image src={src} alt={alt} {...props} />;
|
||||
}
|
||||