Introduce ThemeContext and apply theme tokens

Add a ThemeContext and provider, wrap the app with ThemeProvider, and sync user settings' theme into the context. Replace hardcoded color classes with design token classes (background, muted, foreground, border, card, etc.) across multiple UI components to centralize theming and enable consistent light/dark styling. Files updated include App.tsx (useTheme, setTheme, ThemeProvider, footer/background tokens), several views and components (AddMediaView, BrowseView, CastDetailView, CastView, MediaCard, MediaListItem, SettingsView, ImporterView) to use tokenized classes, and add new src/contexts/ThemeContext.tsx.
This commit is contained in:
Lars Behrends
2026-04-10 14:59:40 +02:00
parent 96593a6235
commit b29732a653
11 changed files with 392 additions and 304 deletions

View File

@@ -44,11 +44,11 @@ export default function MediaListItem({ media, onClick }: MediaListItemProps) {
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }}
className="group flex items-center gap-6 p-4 rounded-xl hover:bg-zinc-50 transition-colors cursor-pointer border border-transparent hover:border-zinc-200"
className="group flex items-center gap-6 p-4 rounded-xl hover:bg-muted/50 transition-colors cursor-pointer border border-transparent hover:border-border"
onClick={() => onClick(media)}
>
<div className={cn(
"relative rounded-lg overflow-hidden shrink-0 shadow-md bg-zinc-800 transition-all duration-300",
"relative rounded-lg overflow-hidden shrink-0 shadow-md bg-card transition-all duration-300",
aspectRatioClass
)}>
<img
@@ -67,32 +67,32 @@ export default function MediaListItem({ media, onClick }: MediaListItemProps) {
<div className="flex-1 min-w-0">
<div className="flex items-center gap-3 mb-1">
<h3 className="text-lg font-black text-zinc-900 truncate group-hover:text-[#6d28d9] transition-colors">
<h3 className="text-lg font-black text-foreground truncate group-hover:text-[#6d28d9] transition-colors">
{media.title}
</h3>
<span className="text-sm font-bold text-zinc-400">({media.year})</span>
<span className="text-sm font-bold text-muted-foreground">({media.year})</span>
</div>
<div className="flex items-center gap-4 mb-3">
<div className="flex items-center gap-1 text-xs font-bold text-zinc-500">
<div className="flex items-center gap-1 text-xs font-bold text-muted-foreground">
<Star size={14} className="text-yellow-500" fill="currentColor" />
{media.rating || 'N/A'}
</div>
<div className="text-xs font-bold text-zinc-400 uppercase tracking-wider">
<div className="text-xs font-bold text-muted-foreground uppercase tracking-wider">
{media.genres?.slice(0, 3).join(' • ') || 'Anime'}
</div>
</div>
<p className="text-sm text-zinc-500 line-clamp-2 max-w-2xl">
<p className="text-sm text-muted-foreground line-clamp-2 max-w-2xl">
{media.description || "No description available for this title."}
</p>
</div>
<div className="hidden md:flex items-center gap-2">
<Button size="icon" variant="ghost" className="rounded-full text-zinc-400 hover:text-[#6d28d9] hover:bg-[#6d28d9]/10">
<Button size="icon" variant="ghost" className="rounded-full text-muted-foreground hover:text-[#6d28d9] hover:bg-[#6d28d9]/10">
<Play size={18} fill="currentColor" />
</Button>
<Button size="icon" variant="ghost" className="rounded-full text-zinc-400 hover:text-[#6d28d9] hover:bg-[#6d28d9]/10">
<Button size="icon" variant="ghost" className="rounded-full text-muted-foreground hover:text-[#6d28d9] hover:bg-[#6d28d9]/10">
<Bookmark size={18} />
</Button>
</div>