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:
@@ -17,7 +17,7 @@ export default function CastDetailView({ person, relatedMedia }: CastDetailViewP
|
||||
navigate(`/media/${mediaId}`);
|
||||
};
|
||||
return (
|
||||
<div className="min-h-screen bg-white pb-20">
|
||||
<div className="min-h-screen bg-background pb-20">
|
||||
{/* Hero Section */}
|
||||
<div className="relative h-[40vh] md:h-[50vh] overflow-hidden bg-zinc-900">
|
||||
<img
|
||||
@@ -26,14 +26,14 @@ export default function CastDetailView({ person, relatedMedia }: CastDetailViewP
|
||||
className="w-full h-full object-cover opacity-40 blur-xl scale-110"
|
||||
referrerPolicy="no-referrer"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-white via-transparent to-transparent" />
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-background via-transparent to-transparent" />
|
||||
|
||||
<div className="absolute inset-0 flex items-end px-6 pb-12">
|
||||
<div className="max-w-[1200px] mx-auto w-full flex flex-col md:flex-row items-center md:items-end gap-8">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
className="w-48 h-48 md:w-64 md:h-64 rounded-2xl overflow-hidden border-4 border-white shadow-2xl shrink-0"
|
||||
className="w-48 h-48 md:w-64 md:h-64 rounded-2xl overflow-hidden border-4 border-background shadow-2xl shrink-0"
|
||||
>
|
||||
<img
|
||||
src={person.photo}
|
||||
@@ -49,7 +49,7 @@ export default function CastDetailView({ person, relatedMedia }: CastDetailViewP
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ delay: 0.1 }}
|
||||
>
|
||||
<h1 className="text-4xl md:text-6xl font-black text-zinc-900 mb-4 drop-shadow-sm">
|
||||
<h1 className="text-4xl md:text-6xl font-black text-foreground mb-4 drop-shadow-sm">
|
||||
{person.name}
|
||||
</h1>
|
||||
<div className="flex flex-wrap justify-center md:justify-start gap-3">
|
||||
@@ -78,90 +78,90 @@ export default function CastDetailView({ person, relatedMedia }: CastDetailViewP
|
||||
<div className="max-w-[1200px] mx-auto px-6 mt-12 grid grid-cols-1 lg:grid-cols-3 gap-12">
|
||||
{/* Sidebar Info */}
|
||||
<div className="space-y-8">
|
||||
<div className="bg-zinc-50 rounded-3xl p-8 space-y-6">
|
||||
<h3 className="text-xl font-black text-zinc-900">Personal Info</h3>
|
||||
<div className="bg-muted/50 rounded-3xl p-8 space-y-6 border border-border">
|
||||
<h3 className="text-xl font-black text-foreground">Personal Info</h3>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="w-10 h-10 rounded-xl bg-white flex items-center justify-center text-[#6d28d9] shadow-sm">
|
||||
<div className="w-10 h-10 rounded-xl bg-background flex items-center justify-center text-[#6d28d9] shadow-sm border border-border">
|
||||
<Calendar size={20} />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-[10px] font-black text-zinc-400 uppercase tracking-widest">Birth Date</p>
|
||||
<p className="font-bold text-zinc-700">{person.birthDate || 'Unknown'}</p>
|
||||
<p className="text-[10px] font-black text-muted-foreground uppercase tracking-widest">Birth Date</p>
|
||||
<p className="font-bold text-foreground">{person.birthDate || 'Unknown'}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="w-10 h-10 rounded-xl bg-white flex items-center justify-center text-[#6d28d9] shadow-sm">
|
||||
<div className="w-10 h-10 rounded-xl bg-background flex items-center justify-center text-[#6d28d9] shadow-sm border border-border">
|
||||
<MapPin size={20} />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-[10px] font-black text-zinc-400 uppercase tracking-widest">Birth Place</p>
|
||||
<p className="font-bold text-zinc-700">{person.birthPlace || 'Unknown'}</p>
|
||||
<p className="text-[10px] font-black text-muted-foreground uppercase tracking-widest">Birth Place</p>
|
||||
<p className="font-bold text-foreground">{person.birthPlace || 'Unknown'}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="w-10 h-10 rounded-xl bg-white flex items-center justify-center text-[#6d28d9] shadow-sm">
|
||||
<div className="w-10 h-10 rounded-xl bg-background flex items-center justify-center text-[#6d28d9] shadow-sm border border-border">
|
||||
<Briefcase size={20} />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-[10px] font-black text-zinc-400 uppercase tracking-widest">Known For</p>
|
||||
<p className="font-bold text-zinc-700">{person.role}</p>
|
||||
<p className="text-[10px] font-black text-muted-foreground uppercase tracking-widest">Known For</p>
|
||||
<p className="font-bold text-foreground">{person.role}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{(person.ethnicity || person.adult_specifics?.ethnicity) && (
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="w-10 h-10 rounded-xl bg-white flex items-center justify-center text-[#6d28d9] shadow-sm">
|
||||
<div className="w-10 h-10 rounded-xl bg-background flex items-center justify-center text-[#6d28d9] shadow-sm border border-border">
|
||||
<User size={20} />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-[10px] font-black text-zinc-400 uppercase tracking-widest">Ethnicity</p>
|
||||
<p className="font-bold text-zinc-700">{person.adult_specifics?.ethnicity || person.ethnicity}</p>
|
||||
<p className="text-[10px] font-black text-muted-foreground uppercase tracking-widest">Ethnicity</p>
|
||||
<p className="font-bold text-foreground">{person.adult_specifics?.ethnicity || person.ethnicity}</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-zinc-50 rounded-3xl p-8 space-y-6">
|
||||
<h3 className="text-xl font-black text-zinc-900">Measurements</h3>
|
||||
<div className="bg-muted/50 rounded-3xl p-8 space-y-6 border border-border">
|
||||
<h3 className="text-xl font-black text-foreground">Measurements</h3>
|
||||
|
||||
<div className="space-y-4">
|
||||
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="w-10 h-10 rounded-xl bg-white flex items-center justify-center text-[#6d28d9] shadow-sm">
|
||||
<div className="w-10 h-10 rounded-xl bg-background flex items-center justify-center text-[#6d28d9] shadow-sm border border-border">
|
||||
<Ruler size={20} />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-[10px] font-black text-zinc-400 uppercase tracking-widest">Height</p>
|
||||
<p className="font-bold text-zinc-700">{person.adult_specifics?.height || person.height} cm</p>
|
||||
<p className="text-[10px] font-black text-muted-foreground uppercase tracking-widest">Height</p>
|
||||
<p className="font-bold text-foreground">{person.adult_specifics?.height || person.height} cm</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{(person.weight || person.adult_specifics?.weight) && (
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="w-10 h-10 rounded-xl bg-white flex items-center justify-center text-[#6d28d9] shadow-sm">
|
||||
<div className="w-10 h-10 rounded-xl bg-background flex items-center justify-center text-[#6d28d9] shadow-sm border border-border">
|
||||
<Ruler size={20} />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-[10px] font-black text-zinc-400 uppercase tracking-widest">Weight</p>
|
||||
<p className="font-bold text-zinc-700">{person.adult_specifics?.weight || person.weight} kg</p>
|
||||
<p className="text-[10px] font-black text-muted-foreground uppercase tracking-widest">Weight</p>
|
||||
<p className="font-bold text-foreground">{person.adult_specifics?.weight || person.weight} kg</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{(person.adult_specifics?.measurements || person.bust_size || person.cup_size || person.waist_size || person.hip_size) && (
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="w-10 h-10 rounded-xl bg-white flex items-center justify-center text-[#6d28d9] shadow-sm">
|
||||
<div className="w-10 h-10 rounded-xl bg-background flex items-center justify-center text-[#6d28d9] shadow-sm border border-border">
|
||||
<Ruler size={20} />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-[10px] font-black text-zinc-400 uppercase tracking-widest">Measurements</p>
|
||||
<p className="font-bold text-zinc-700">
|
||||
<p className="text-[10px] font-black text-muted-foreground uppercase tracking-widest">Measurements</p>
|
||||
<p className="font-bold text-foreground">
|
||||
{person.adult_specifics?.measurements || (
|
||||
<>
|
||||
{person.bust_size && `${person.bust_size}`}
|
||||
@@ -179,48 +179,48 @@ export default function CastDetailView({ person, relatedMedia }: CastDetailViewP
|
||||
|
||||
{(person.hair_color || person.adult_specifics?.hair_color) && (
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="w-10 h-10 rounded-xl bg-white flex items-center justify-center text-[#6d28d9] shadow-sm">
|
||||
<div className="w-10 h-10 rounded-xl bg-background flex items-center justify-center text-[#6d28d9] shadow-sm border border-border">
|
||||
<Palette size={20} />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-[10px] font-black text-zinc-400 uppercase tracking-widest">Hair Color</p>
|
||||
<p className="font-bold text-zinc-700">{person.adult_specifics?.hair_color || person.hair_color}</p>
|
||||
<p className="text-[10px] font-black text-muted-foreground uppercase tracking-widest">Hair Color</p>
|
||||
<p className="font-bold text-foreground">{person.adult_specifics?.hair_color || person.hair_color}</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{(person.eye_color || person.adult_specifics?.eye_color) && (
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="w-10 h-10 rounded-xl bg-white flex items-center justify-center text-[#6d28d9] shadow-sm">
|
||||
<div className="w-10 h-10 rounded-xl bg-background flex items-center justify-center text-[#6d28d9] shadow-sm border border-border">
|
||||
<Eye size={20} />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-[10px] font-black text-zinc-400 uppercase tracking-widest">Eye Color</p>
|
||||
<p className="font-bold text-zinc-700">{person.adult_specifics?.eye_color || person.eye_color}</p>
|
||||
<p className="text-[10px] font-black text-muted-foreground uppercase tracking-widest">Eye Color</p>
|
||||
<p className="font-bold text-foreground">{person.adult_specifics?.eye_color || person.eye_color}</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{person.adult_specifics?.tattoos && (
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="w-10 h-10 rounded-xl bg-white flex items-center justify-center text-[#6d28d9] shadow-sm">
|
||||
<div className="w-10 h-10 rounded-xl bg-background flex items-center justify-center text-[#6d28d9] shadow-sm border border-border">
|
||||
<Palette size={20} />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-[10px] font-black text-zinc-400 uppercase tracking-widest">Tattoos</p>
|
||||
<p className="font-bold text-zinc-700">{person.adult_specifics.tattoos}</p>
|
||||
<p className="text-[10px] font-black text-muted-foreground uppercase tracking-widest">Tattoos</p>
|
||||
<p className="font-bold text-foreground">{person.adult_specifics.tattoos}</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{person.adult_specifics?.piercings && (
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="w-10 h-10 rounded-xl bg-white flex items-center justify-center text-[#6d28d9] shadow-sm">
|
||||
<div className="w-10 h-10 rounded-xl bg-background flex items-center justify-center text-[#6d28d9] shadow-sm border border-border">
|
||||
<Palette size={20} />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-[10px] font-black text-zinc-400 uppercase tracking-widest">Piercings</p>
|
||||
<p className="font-bold text-zinc-700">{person.adult_specifics.piercings}</p>
|
||||
<p className="text-[10px] font-black text-muted-foreground uppercase tracking-widest">Piercings</p>
|
||||
<p className="font-bold text-foreground">{person.adult_specifics.piercings}</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@@ -232,10 +232,10 @@ export default function CastDetailView({ person, relatedMedia }: CastDetailViewP
|
||||
<div className="lg:col-span-2 space-y-12">
|
||||
{person.bio && (
|
||||
<section>
|
||||
<h2 className="text-2xl font-black text-zinc-900 mb-6 flex items-center gap-3">
|
||||
<h2 className="text-2xl font-black text-foreground mb-6 flex items-center gap-3">
|
||||
Biography
|
||||
</h2>
|
||||
<p className="text-zinc-600 leading-relaxed text-lg">
|
||||
<p className="text-foreground leading-relaxed text-lg">
|
||||
{person.bio}
|
||||
</p>
|
||||
</section>
|
||||
@@ -243,7 +243,7 @@ export default function CastDetailView({ person, relatedMedia }: CastDetailViewP
|
||||
|
||||
{person.filmography && person.filmography.length > 0 && (
|
||||
<section>
|
||||
<h2 className="text-2xl font-black text-zinc-900 mb-6 flex items-center gap-3">
|
||||
<h2 className="text-2xl font-black text-foreground mb-6 flex items-center gap-3">
|
||||
<User className="text-[#6d28d9]" />
|
||||
Characters
|
||||
</h2>
|
||||
@@ -251,9 +251,9 @@ export default function CastDetailView({ person, relatedMedia }: CastDetailViewP
|
||||
{person.filmography.map(item => (
|
||||
<div
|
||||
key={`${item.id}-char`}
|
||||
className="flex items-center gap-4 p-4 rounded-2xl bg-zinc-50 border border-zinc-100"
|
||||
className="flex items-center gap-4 p-4 rounded-2xl bg-muted/50 border border-border"
|
||||
>
|
||||
<div className="w-20 h-20 rounded-xl overflow-hidden shrink-0 shadow-sm border-2 border-white">
|
||||
<div className="w-20 h-20 rounded-xl overflow-hidden shrink-0 shadow-sm border-2 border-background">
|
||||
<img
|
||||
src={item.poster || person.photo}
|
||||
alt={item.title}
|
||||
@@ -262,8 +262,8 @@ export default function CastDetailView({ person, relatedMedia }: CastDetailViewP
|
||||
/>
|
||||
</div>
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="text-[10px] font-black text-zinc-400 uppercase tracking-widest mb-1">Character</p>
|
||||
<h4 className="font-black text-zinc-900 truncate">{item.characterName || item.role}</h4>
|
||||
<p className="text-[10px] font-black text-muted-foreground uppercase tracking-widest mb-1">Character</p>
|
||||
<h4 className="font-black text-foreground truncate">{item.characterName || item.role}</h4>
|
||||
<button
|
||||
onClick={() => handleMediaClick(item.id.toString())}
|
||||
className="text-xs font-bold text-[#6d28d9] hover:underline mt-1 text-left"
|
||||
@@ -279,7 +279,7 @@ export default function CastDetailView({ person, relatedMedia }: CastDetailViewP
|
||||
|
||||
{person.filmography && person.filmography.length > 0 && (
|
||||
<section>
|
||||
<h2 className="text-2xl font-black text-zinc-900 mb-6 flex items-center gap-3">
|
||||
<h2 className="text-2xl font-black text-foreground mb-6 flex items-center gap-3">
|
||||
<Film className="text-[#6d28d9]" />
|
||||
Filmography
|
||||
</h2>
|
||||
@@ -288,7 +288,7 @@ export default function CastDetailView({ person, relatedMedia }: CastDetailViewP
|
||||
<div
|
||||
key={item.id}
|
||||
onClick={() => handleMediaClick(item.id.toString())}
|
||||
className="group flex items-center gap-4 p-4 rounded-2xl bg-white border border-zinc-100 hover:border-[#6d28d9]/30 hover:shadow-lg transition-all cursor-pointer"
|
||||
className="group flex items-center gap-4 p-4 rounded-2xl bg-card border border-border hover:border-[#6d28d9]/30 hover:shadow-lg transition-all cursor-pointer"
|
||||
>
|
||||
<div className="w-16 h-20 rounded-lg overflow-hidden shrink-0 shadow-sm">
|
||||
<img
|
||||
@@ -299,14 +299,14 @@ export default function CastDetailView({ person, relatedMedia }: CastDetailViewP
|
||||
/>
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<h4 className="font-black text-zinc-900 truncate group-hover:text-[#6d28d9] transition-colors">
|
||||
<h4 className="font-black text-foreground truncate group-hover:text-[#6d28d9] transition-colors">
|
||||
{item.title}
|
||||
</h4>
|
||||
<p className="text-xs font-bold text-zinc-400 uppercase tracking-wider mb-1">
|
||||
<p className="text-xs font-bold text-muted-foreground uppercase tracking-wider mb-1">
|
||||
{item.year || 'Unknown'}
|
||||
</p>
|
||||
<div className="flex items-center gap-2">
|
||||
<Badge variant="outline" className="text-[10px] font-bold py-0 h-5 border-zinc-200">
|
||||
<Badge variant="outline" className="text-[10px] font-bold py-0 h-5 border-border">
|
||||
{item.role}
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user