Minimal flat redesign: white accent, remove card backgrounds

Replace yellow accent (#f5a623) with white (#ffffff) across the entire
app. Flatten VideoCard grid variant by removing zinc-900 card background
so content sits directly on the page. Simplify active states, badges,
progress bars, and hover effects throughout.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-26 21:50:44 +02:00
parent 366a2ff183
commit 52279752e4
10 changed files with 34 additions and 35 deletions

View File

@@ -80,7 +80,7 @@ export default function ChannelCard({ channel }) {
className={`text-xs font-medium px-4 py-2 rounded-lg transition-colors ${
isFollowed || followMut.isSuccess
? "bg-zinc-700 text-zinc-300 hover:bg-zinc-600"
: "bg-accent text-black hover:bg-yellow-300"
: "bg-accent text-black hover:bg-zinc-100"
}`}
>
{isFollowed || followMut.isSuccess ? "Following" : "Follow"}

View File

@@ -41,7 +41,7 @@ function BottomNav({ newCount }) {
end={tab.end}
className={({ isActive }) =>
`relative flex-1 flex flex-col items-center justify-center gap-0.5 transition-colors outline-none ${
isActive ? "text-accent" : "text-zinc-500"
isActive ? "text-zinc-100" : "text-zinc-500"
}`
}
>
@@ -49,13 +49,13 @@ function BottomNav({ newCount }) {
<>
<div className="relative">
{isActive && (
<span className="absolute -inset-2 rounded-xl bg-accent/10" />
<span className="absolute -inset-2 rounded-xl bg-white/10" />
)}
<svg className="w-[18px] h-[18px] relative" fill="none" stroke="currentColor" viewBox="0 0 24 24">
{tab.icon}
</svg>
{tab.badge > 0 && (
<span className="absolute -top-1 -right-1.5 min-w-[13px] h-3 bg-accent text-black text-[8px] font-bold rounded-full flex items-center justify-center px-0.5 leading-none">
<span className="absolute -top-1 -right-1.5 min-w-[13px] h-3 bg-zinc-200 text-zinc-900 text-[8px] font-bold rounded-full flex items-center justify-center px-0.5 leading-none">
{tab.badge > 99 ? "99+" : tab.badge}
</span>
)}
@@ -98,7 +98,7 @@ function DownloadIndicator() {
className="flex items-center gap-1.5 px-2 py-1 rounded-lg bg-zinc-800 hover:bg-zinc-700 transition-colors text-xs text-zinc-300 shrink-0"
title={`${active.length} download${active.length > 1 ? "s" : ""} in progress`}
>
<svg className="w-3 h-3 animate-spin text-accent shrink-0" fill="none" viewBox="0 0 24 24">
<svg className="w-3 h-3 animate-spin text-zinc-400 shrink-0" fill="none" viewBox="0 0 24 24">
<circle className="opacity-20" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="3" />
<path className="opacity-80" fill="currentColor" d="M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z" />
</svg>
@@ -125,7 +125,7 @@ function NavItem({ to, children, badge }) {
>
{children}
{badge > 0 && (
<span className="absolute -top-1 -right-1 min-w-[16px] h-4 bg-accent text-black text-[10px] font-bold rounded-full flex items-center justify-center px-1 leading-none">
<span className="absolute -top-1 -right-1 min-w-[16px] h-4 bg-zinc-200 text-zinc-900 text-[10px] font-bold rounded-full flex items-center justify-center px-1 leading-none">
{badge > 99 ? "99+" : badge}
</span>
)}
@@ -147,7 +147,7 @@ function DropItem({ to, children, badge }) {
>
<span>{children}</span>
{badge > 0 && (
<span className="min-w-[18px] h-[18px] bg-accent text-black text-[10px] font-bold rounded-full flex items-center justify-center px-1 leading-none shrink-0">
<span className="min-w-[18px] h-[18px] bg-zinc-200 text-zinc-900 text-[10px] font-bold rounded-full flex items-center justify-center px-1 leading-none shrink-0">
{badge > 99 ? "99+" : badge}
</span>
)}
@@ -214,7 +214,7 @@ export default function Layout() {
{/* Logo */}
<button
onClick={() => navigate("/")}
className="font-display font-bold text-base sm:text-lg text-accent shrink-0"
className="font-display font-bold text-base sm:text-lg text-zinc-100 shrink-0 tracking-tight"
>
YT
</button>

View File

@@ -42,8 +42,8 @@ function IconBtn({ onClick, title, active, pending, children }) {
onClick={(e) => { e.stopPropagation(); onClick(e); }}
title={title}
className={clsx(
"flex items-center justify-center w-7 h-7 rounded-full transition-all duration-150",
active ? "text-accent" : "text-zinc-600 hover:text-zinc-200",
"flex items-center justify-center w-7 h-7 rounded-md transition-all duration-150",
active ? "text-zinc-100" : "text-zinc-600 hover:text-zinc-300",
pending && "opacity-60 cursor-default",
)}
>
@@ -130,13 +130,13 @@ function ThumbnailBlock({ video, isWatched, duration, calmMode, onDismiss, class
)}
{video.download_resolution && (
<span className="absolute bottom-1.5 left-1.5 text-[10px] font-medium px-1.5 py-0.5 rounded font-mono text-accent bg-black/75">
<span className="absolute bottom-1.5 left-1.5 text-[10px] font-medium px-1.5 py-0.5 rounded font-mono text-white/80 bg-black/75">
{video.download_resolution}
</span>
)}
{isWatched && (
<span className="absolute top-2 left-2 w-2 h-2 rounded-full bg-accent shadow-[0_0_6px_rgba(var(--color-accent-rgb),0.8)]" />
<span className="absolute top-2 left-2 w-2 h-2 rounded-full bg-white/60" />
)}
<div className="absolute inset-0 bg-black/25 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center">
@@ -148,9 +148,9 @@ function ThumbnailBlock({ video, isWatched, duration, calmMode, onDismiss, class
</div>
{video.watch_progress_seconds > 0 && video.duration_seconds > 0 && (
<div className="absolute bottom-0 inset-x-0 h-[3px] bg-white/10">
<div className="absolute bottom-0 inset-x-0 h-[2px] bg-white/10">
<div
className="h-full bg-accent"
className="h-full bg-white/70"
style={{ width: `${Math.min(video.watch_progress_seconds / video.duration_seconds, 1) * 100}%` }}
/>
</div>
@@ -259,7 +259,7 @@ export default function VideoCard({ video, size = "md", onDismiss, variant = "gr
return (
<div
onClick={() => navigate(`/watch/${video.youtube_video_id}`)}
className="group flex gap-3 sm:gap-4 px-2 sm:px-3 py-2 sm:py-3 rounded-xl cursor-pointer hover:bg-zinc-800/50 transition-colors duration-150"
className="group flex gap-3 sm:gap-4 px-2 sm:px-3 py-2 sm:py-3 rounded-lg cursor-pointer hover:bg-zinc-900/70 transition-colors duration-150"
>
{/* Thumbnail — compact on mobile, wide on desktop */}
<ThumbnailBlock
@@ -332,8 +332,7 @@ export default function VideoCard({ video, size = "md", onDismiss, variant = "gr
<div
onClick={() => navigate(`/watch/${video.youtube_video_id}`)}
className={clsx(
"group relative flex flex-col cursor-pointer rounded-2xl",
"bg-zinc-900 hover:bg-zinc-800/80 transition-colors duration-150",
"group relative flex flex-col cursor-pointer",
size === "sm" && "text-xs",
)}
>
@@ -343,10 +342,10 @@ export default function VideoCard({ video, size = "md", onDismiss, variant = "gr
duration={duration}
calmMode={calmMode}
onDismiss={() => dismissMut.mutate()}
className="aspect-video rounded-t-2xl overflow-hidden"
className="aspect-video rounded-lg overflow-hidden"
/>
<div className="flex gap-2 sm:gap-2.5 p-2 sm:p-3 flex-1">
<div className="flex gap-2 sm:gap-2.5 pt-2 sm:pt-2.5 pb-1 flex-1">
{/* Channel avatar */}
<div
className="shrink-0 mt-0.5"
@@ -356,10 +355,10 @@ export default function VideoCard({ video, size = "md", onDismiss, variant = "gr
<img
src={avatarUrl}
alt=""
className="w-7 h-7 sm:w-8 sm:h-8 rounded-full object-cover hover:ring-2 hover:ring-accent/50 transition-all"
className="w-7 h-7 sm:w-8 sm:h-8 rounded-full object-cover hover:opacity-80 transition-opacity"
/>
) : (
<div className="w-7 h-7 sm:w-8 sm:h-8 rounded-full bg-zinc-700 flex items-center justify-center text-[11px] font-bold text-zinc-400 hover:ring-2 hover:ring-accent/50 transition-all">
<div className="w-7 h-7 sm:w-8 sm:h-8 rounded-full bg-zinc-800 flex items-center justify-center text-[11px] font-bold text-zinc-400 hover:opacity-80 transition-opacity">
{avatarLetter}
</div>
)}

View File

@@ -112,7 +112,7 @@ export default function ChannelPage() {
{isFollowed ? "Following" : "Follow"}
</button>
<button onClick={() => dlMut.mutate()} disabled={dlMut.isPending}
className="text-sm font-medium px-4 py-1.5 rounded-lg bg-accent text-black hover:bg-yellow-300 transition-colors disabled:opacity-60">
className="text-sm font-medium px-4 py-1.5 rounded-lg bg-accent text-black hover:bg-zinc-100 transition-colors disabled:opacity-60">
{dlMut.isPending ? "Queuing…" : "Download all"}
</button>
<button onClick={() => indexMut.mutate()} disabled={indexMut.isPending || indexMut.isSuccess}
@@ -130,7 +130,7 @@ export default function ChannelPage() {
{isFollowed ? "Following ✓" : "Follow"}
</button>
<button onClick={() => dlMut.mutate()} disabled={dlMut.isPending}
className="flex-1 text-sm font-medium py-2 rounded-lg bg-accent text-black hover:bg-yellow-300 transition-colors disabled:opacity-60">
className="flex-1 text-sm font-medium py-2 rounded-lg bg-accent text-black hover:bg-zinc-100 transition-colors disabled:opacity-60">
{dlMut.isPending ? "Queuing…" : "Download all"}
</button>
<button onClick={() => indexMut.mutate()} disabled={indexMut.isPending || indexMut.isSuccess}

View File

@@ -160,7 +160,7 @@ function ChannelCard({ item }) {
<button
onClick={() => followMut.mutate()}
disabled={busy}
className="w-full py-1.5 rounded-lg bg-accent text-black text-xs font-semibold hover:bg-yellow-300 transition-colors disabled:opacity-50"
className="w-full py-1.5 rounded-lg bg-accent text-black text-xs font-semibold hover:bg-zinc-100 transition-colors disabled:opacity-50"
>
Follow
</button>
@@ -292,7 +292,7 @@ export default function DiscoveryPage() {
<button
onClick={() => refreshMut.mutate()}
disabled={refreshMut.isPending}
className="mt-2 px-6 py-2.5 rounded-xl bg-accent text-black font-semibold text-sm hover:bg-yellow-300 transition-colors disabled:opacity-60"
className="mt-2 px-6 py-2.5 rounded-xl bg-accent text-black font-semibold text-sm hover:bg-zinc-100 transition-colors disabled:opacity-60"
>
{refreshMut.isPending ? "Searching…" : "Find channels"}
</button>

View File

@@ -765,7 +765,7 @@ export default function Following() {
<button
onClick={() => dlAllMut.mutate()}
disabled={dlAllMut.isPending}
className="flex items-center gap-2 px-4 py-2 rounded-xl bg-accent text-black text-sm font-semibold hover:bg-yellow-300 transition-colors disabled:opacity-60"
className="flex items-center gap-2 px-4 py-2 rounded-xl bg-accent text-black text-sm font-semibold hover:bg-zinc-100 transition-colors disabled:opacity-60"
>
{dlAllMut.isPending ? <><Spinner /> Queuing</> : "Download all new"}
</button>

View File

@@ -153,7 +153,7 @@ export default function Home() {
>
{m.label}
{m.value === "inbox" && inboxCount > 0 && (
<span className="absolute -top-1 -right-1 min-w-[13px] h-3 bg-accent text-black text-[8px] font-bold rounded-full flex items-center justify-center px-0.5 leading-none">
<span className="absolute -top-1 -right-1 min-w-[13px] h-3 bg-zinc-200 text-zinc-900 text-[8px] font-bold rounded-full flex items-center justify-center px-0.5 leading-none">
{inboxCount > 99 ? "99+" : inboxCount}
</span>
)}
@@ -252,7 +252,7 @@ export default function Home() {
<button
onClick={() => surpriseMut.mutate()}
disabled={surpriseMut.isPending}
className="bg-accent text-black font-display font-bold text-lg px-8 py-4 rounded-2xl hover:scale-105 active:scale-95 transition-all disabled:opacity-60 shadow-lg shadow-accent/20"
className="bg-accent text-black font-display font-bold text-lg px-8 py-4 rounded-2xl hover:scale-105 active:scale-95 transition-all disabled:opacity-60 shadow-lg"
>
<span className="flex items-center gap-2">
<span className="text-2xl"></span>

View File

@@ -413,7 +413,7 @@ function SubscriptionImportSection() {
</div>
<button
onClick={() => setShowPaste((v) => !v)}
className="shrink-0 px-4 py-2 rounded-lg bg-accent text-black text-sm font-medium hover:bg-yellow-300 transition-colors"
className="shrink-0 px-4 py-2 rounded-lg bg-accent text-black text-sm font-medium hover:bg-zinc-100 transition-colors"
>
{showPaste ? "Cancel" : "Paste list"}
</button>
@@ -430,7 +430,7 @@ function SubscriptionImportSection() {
<button
onClick={handlePaste}
disabled={loading || !pasteText.trim()}
className="self-end px-4 py-2 rounded-lg bg-accent text-black text-sm font-medium hover:bg-yellow-300 transition-colors disabled:opacity-50"
className="self-end px-4 py-2 rounded-lg bg-accent text-black text-sm font-medium hover:bg-zinc-100 transition-colors disabled:opacity-50"
>
{loading ? "Importing…" : `Import ${(pasteText.match(/@[\w.-]+(?=•)/g) || []).length} channels`}
</button>

View File

@@ -230,7 +230,7 @@ function Placeholder({ video, dlStatus, onPlay, onDownloadAndPlay, isDownloading
) : onDownloadAndPlay ? (
<button
onClick={onDownloadAndPlay}
className="flex items-center gap-2 px-6 py-3 rounded-full bg-accent text-black font-bold text-sm hover:bg-yellow-300 transition-colors"
className="flex items-center gap-2 px-6 py-3 rounded-full bg-accent text-black font-bold text-sm hover:bg-zinc-100 transition-colors"
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2}
@@ -1046,7 +1046,7 @@ export default function Watch() {
<button
onClick={() => addSubsMut.mutate()}
disabled={addSubsMut.isPending}
className="flex items-center gap-1 px-3 py-1.5 rounded-full text-xs font-medium bg-accent text-black hover:bg-yellow-300 transition-colors disabled:opacity-50"
className="flex items-center gap-1 px-3 py-1.5 rounded-full text-xs font-medium bg-accent text-black hover:bg-zinc-100 transition-colors disabled:opacity-50"
>
{addSubsMut.isPending && <span className="w-3 h-3 border-2 border-black/40 border-t-transparent rounded-full animate-spin inline-block" />}
{addSubsMut.isPending ? "Fetching…" : "Add subtitles"}

View File

@@ -10,9 +10,9 @@ export default {
},
colors: {
accent: {
DEFAULT: "#f5a623",
light: "#fbbf45",
dark: "#d4891a",
DEFAULT: "#ffffff",
light: "#f4f4f5",
dark: "#d4d4d8",
},
},
aspectRatio: {