Add light/dark mode with toggle, fix all theme-aware text colors

This commit is contained in:
Andy
2026-03-21 12:18:14 +00:00
parent 4741546adf
commit 75e1d0083f
9 changed files with 211 additions and 91 deletions

View File

@@ -22,7 +22,7 @@ function formatDate(iso: string) {
function RatingBar({ rating }: { rating: number }) {
return (
<div className="flex items-center gap-3">
<div className="flex-1 h-1.5 bg-white/[0.08] rounded-full overflow-hidden">
<div className="flex-1 h-1.5 bg-black/[0.08] dark:bg-white/[0.08] rounded-full overflow-hidden">
<motion.div
className="h-full bg-[#f59e0b] rounded-full"
initial={{ width: 0 }}
@@ -46,15 +46,15 @@ function VisitCard({ visit, index }: { visit: Visit; index: number }) {
className="relative pl-6"
>
{/* Timeline line */}
<div className="absolute left-0 top-0 bottom-0 w-px bg-white/[0.08]" />
<div className="absolute left-0 top-0 bottom-0 w-px bg-black/[0.08] dark:bg-white/[0.08]" />
{/* Timeline dot */}
<div className="absolute left-[-4px] top-3 w-2 h-2 rounded-full bg-[#f59e0b]" />
<div className="rounded-2xl border border-white/[0.06] bg-white/[0.03] p-5 mb-4">
<div className="rounded-2xl border border-black/[0.07] dark:border-white/[0.06] bg-white dark:bg-white/[0.03] p-5 mb-4 shadow-sm dark:shadow-none">
<div className="flex items-start justify-between gap-4 mb-4">
<p className="text-sm text-white/40">{formatDate(visit.date)}</p>
<p className="text-sm text-black/40 dark:text-white/40">{formatDate(visit.date)}</p>
{visit.price_paid && (
<span className="text-xs text-white/30 shrink-0">
<span className="text-xs text-black/30 dark:text-white/30 shrink-0">
{visit.price_paid}
</span>
)}
@@ -63,12 +63,12 @@ function VisitCard({ visit, index }: { visit: Visit; index: number }) {
<RatingBar rating={visit.rating} />
<div className="mt-4 space-y-1.5">
<p className="text-xs font-semibold uppercase tracking-wider text-white/30">
<p className="text-xs font-semibold uppercase tracking-wider text-black/30 dark:text-white/30">
Dishes
</p>
<ul className="space-y-1">
{visit.dishes.map((dish, i) => (
<li key={i} className="text-sm text-[#f5f5f5] flex items-start gap-2">
<li key={i} className="text-sm text-[#111] dark:text-[#f5f5f5] flex items-start gap-2">
<span className="text-[#f59e0b] mt-1 shrink-0">·</span>
{dish}
</li>
@@ -77,8 +77,8 @@ function VisitCard({ visit, index }: { visit: Visit; index: number }) {
</div>
{visit.notes && (
<div className="mt-4 pt-4 border-t border-white/[0.06]">
<p className="text-sm text-white/60 leading-relaxed italic">
<div className="mt-4 pt-4 border-t border-black/[0.06] dark:border-white/[0.06]">
<p className="text-sm text-black/50 dark:text-white/60 leading-relaxed italic">
&ldquo;{visit.notes}&rdquo;
</p>
</div>
@@ -134,7 +134,7 @@ export default function RestaurantPage() {
if (notFound || !restaurant) {
return (
<div className="flex flex-col items-center justify-center min-h-[60vh] gap-4">
<p className="text-white/40">Restaurant not found</p>
<p className="text-black/40 dark:text-white/40">Restaurant not found</p>
<Link href="/" className="text-[#f59e0b] text-sm font-medium hover:underline">
Back home
</Link>
@@ -161,7 +161,7 @@ export default function RestaurantPage() {
>
<button
onClick={() => router.back()}
className="text-sm text-white/40 hover:text-white/70 transition-colors mb-8 flex items-center gap-2"
className="text-sm text-black/40 dark:text-white/40 hover:text-black/70 dark:hover:text-white/70 transition-colors mb-8 flex items-center gap-2"
>
back
</button>
@@ -174,18 +174,18 @@ export default function RestaurantPage() {
transition={{ duration: 0.5 }}
>
<div className="flex items-start justify-between gap-4 mb-2">
<h1 className="text-4xl md:text-5xl font-black tracking-tight text-[#f5f5f5] leading-none">
<h1 className="text-4xl md:text-5xl font-black tracking-tight text-[#111] dark:text-[#f5f5f5] leading-none">
{restaurant.name}
</h1>
<div className="shrink-0 text-right">
<div className="text-[#f59e0b] font-black text-3xl leading-none">
{avgRating.toFixed(1)}
</div>
<div className="text-white/25 text-xs mt-1">avg</div>
<div className="text-black/25 dark:text-white/25 text-xs mt-1">avg</div>
</div>
</div>
<p className="text-white/40 text-base mt-2">
<p className="text-black/40 dark:text-white/40 text-base mt-2">
{restaurant.address} · {restaurant.city}, {restaurant.country}
</p>
@@ -193,33 +193,32 @@ export default function RestaurantPage() {
{restaurant.cuisine.map((tag) => (
<span
key={tag}
className="text-xs font-semibold uppercase tracking-wider text-[#f59e0b]/70 bg-[#f59e0b]/10 rounded-full px-3 py-1"
className="text-xs font-semibold uppercase tracking-wider text-[#f59e0b] bg-[#f59e0b]/10 rounded-full px-3 py-1"
>
{tag}
</span>
))}
<span className="text-white/30 text-sm font-medium">
<span className="text-black/30 dark:text-white/30 text-sm font-medium">
{priceLabel(restaurant.price_range)}
</span>
<span className="text-white/25 text-sm">
<span className="text-black/25 dark:text-white/25 text-sm">
{restaurant.visits.length} {restaurant.visits.length === 1 ? "visit" : "visits"}
</span>
</div>
</motion.div>
{/* Divider */}
<div className="my-8 border-t border-white/[0.06]" />
<div className="my-8 border-t border-black/[0.06] dark:border-white/[0.06]" />
{/* Visits timeline */}
<div>
<h2 className="text-lg font-bold mb-6 text-white/70">Visits</h2>
<h2 className="text-lg font-bold mb-6 text-black/60 dark:text-white/70">Visits</h2>
<div className="space-y-0">
{sortedVisits.map((visit, i) => (
<VisitCard key={visit.id} visit={visit} index={i} />
))}
</div>
</div>
</div>
)
}