feat: implement dark mode support with theme provider and toggle component

This commit is contained in:
암냥 2026-04-21 22:26:20 +09:00
commit cf70aa4a67
No known key found for this signature in database
12 changed files with 248 additions and 31 deletions

View file

@ -29,6 +29,7 @@ type GalleryPhoto = {
key: string;
alt: string;
author: string;
source: string;
};
type Me = {
@ -414,6 +415,7 @@ export default function App() {
key: upload._id,
alt: `tweet ${upload.tweetId} media ${upload.mediaIndex + 1}`,
author: upload.author?.trim() || "unknown",
source: upload.tweet?.url || "",
});
};
image.onerror = () => {
@ -424,6 +426,7 @@ export default function App() {
key: upload._id,
alt: `tweet ${upload.tweetId} media ${upload.mediaIndex + 1}`,
author: upload.author?.trim() || "unknown",
source: upload.tweet?.url || "",
});
};
}),
@ -443,7 +446,7 @@ export default function App() {
}, [items]);
return (
<div className="min-h-screen bg-[radial-gradient(circle_at_top,rgba(255,255,255,0.9),rgba(247,229,236,0.85)_45%,rgba(244,240,243,1)_100%)] text-foreground">
<div className="min-h-screen bg-main-gradient text-foreground transition-colors duration-500">
<div className="sticky top-0 z-50">
<Header />
<div className="border-b border-border bg-background/85 backdrop-blur px-6 py-2">
@ -482,7 +485,7 @@ export default function App() {
{Array.from({ length: 12 }).map((_, index) => (
<div
key={index}
className="aspect-4/5 animate-pulse bg-black/8"
className="aspect-4/5 animate-pulse bg-muted/50"
/>
))}
</div>
@ -515,10 +518,19 @@ export default function App() {
decoding="async"
className="block w-full"
/>
<div className="pointer-events-none absolute inset-x-3 bottom-3 translate-y-3 opacity-0 transition-all duration-200 group-hover:translate-y-0 group-hover:opacity-100">
<div className="inline-flex max-w-full items-center rounded-t-sm rounded-b-lg bg-black/72 px-3 py-1.5 text-xs text-white backdrop-blur-sm">
<div className="absolute inset-x-3 bottom-3 translate-y-3 opacity-0 transition-all duration-200 group-hover:translate-y-0 group-hover:opacity-100">
<button
type="button"
onClick={(event) => {
event.preventDefault();
event.stopPropagation();
window.open(photo.source, "_blank", "noopener,noreferrer");
}}
className="inline-flex max-w-full items-center rounded-t-sm rounded-b-lg bg-black/72 px-3 py-1.5 text-xs text-white backdrop-blur-sm pointer-events-auto"
title="원본 이미지 열기"
>
<span className="truncate">© {photo.author}</span>
</div>
</button>
</div>
</a>
),
@ -564,7 +576,7 @@ export default function App() {
) : null}
<button
type="button"
className="block w-full rounded px-3 py-2 text-left text-sm hover:bg-black/5"
className="block w-full rounded px-3 py-2 text-left text-sm hover:bg-accent"
onClick={async () => {
await copyImage(contextMenu.photo);
setContextMenu(null);
@ -574,7 +586,7 @@ export default function App() {
</button>
<button
type="button"
className="block w-full rounded px-3 py-2 text-left text-sm hover:bg-black/5"
className="block w-full rounded px-3 py-2 text-left text-sm hover:bg-accent"
onClick={() => {
saveImage(contextMenu.photo);
setContextMenu(null);
@ -591,7 +603,7 @@ export default function App() {
{Array.from({ length: 5 }).map((_, index) => (
<div
key={`loading-more-${index}`}
className="aspect-4/5 animate-pulse bg-black/8"
className="aspect-4/5 animate-pulse bg-muted/50"
/>
))}
</div>
@ -615,7 +627,7 @@ export default function App() {
<div className="mt-5 flex justify-end gap-2">
<button
type="button"
className="rounded px-4 py-2 text-sm text-foreground/70 hover:bg-black/5 disabled:opacity-50"
className="rounded px-4 py-2 text-sm text-foreground/70 hover:bg-accent disabled:opacity-50"
onClick={() => setDeleteTarget(null)}
disabled={isDeleting}
>