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
+17 -8
View File
@@ -17,11 +17,13 @@ import SettingsView from './components/SettingsView';
import { MOCK_MEDIA, DETAIL_MEDIA } from './data';
import { Media, Staff, MediaCategory, UserSettings } from './types';
import { fetchAllMedia, fetchMediaById, fetchCastById, convertApiCastToStaff, fetchSettings, updateSettings } from './api';
import { ThemeProvider, useTheme } from './contexts/ThemeContext';
function AppContent() {
const navigate = useNavigate();
const location = useLocation();
const [searchParams, setSearchParams] = useSearchParams();
const { setTheme } = useTheme();
const [activeCategory, setActiveCategory] = useState<MediaCategory>(
(searchParams.get('category') as MediaCategory) || 'Anime'
);
@@ -43,6 +45,8 @@ function AppContent() {
if (loadedSettings) {
setSettings(loadedSettings);
setEnabledCategories(loadedSettings.enabledCategories);
// Sync theme with theme context
setTheme(loadedSettings.theme);
}
} catch (error) {
console.error('Failed to load settings from API:', error);
@@ -50,7 +54,7 @@ function AppContent() {
};
loadSettingsFromApi();
}, []);
}, [setTheme]);
const reloadSettings = async () => {
try {
@@ -58,6 +62,8 @@ function AppContent() {
if (loadedSettings) {
setSettings(loadedSettings);
setEnabledCategories(loadedSettings.enabledCategories);
// Sync theme with theme context
setTheme(loadedSettings.theme);
}
} catch (error) {
console.error('Failed to reload settings from API:', error);
@@ -270,7 +276,7 @@ function AppContent() {
};
return (
<div className="min-h-screen bg-white font-sans selection:bg-[#6d28d9]/20 selection:text-[#6d28d9]">
<div className="min-h-screen bg-background font-sans selection:bg-[#6d28d9]/20 selection:text-[#6d28d9]">
<Header
onSearch={handleSearch}
activeCategory={activeCategory}
@@ -315,6 +321,7 @@ function AppContent() {
<Route path="/add" element={
<AddMediaView
activeCategory={activeCategory}
enabledCategories={enabledCategories}
onAddComplete={handleAddMedia}
/>
} />
@@ -329,18 +336,18 @@ function AppContent() {
</main>
{/* Footer */}
<footer className="py-12 px-6 border-t border-zinc-100 bg-zinc-50">
<footer className="py-12 px-6 border-t border-border bg-muted/50">
<div className="max-w-[1600px] mx-auto flex flex-col md:flex-row items-center justify-between gap-6">
<div className="flex items-center gap-2 text-xl font-black text-zinc-400">
<div className="w-5 h-5 bg-zinc-300 rounded-full" />
<div className="flex items-center gap-2 text-xl font-black text-muted-foreground">
<div className="w-5 h-5 bg-muted rounded-full" />
kyoo
</div>
<div className="flex items-center gap-8 text-sm font-bold text-zinc-400">
<div className="flex items-center gap-8 text-sm font-bold text-muted-foreground">
<a href="#" className="hover:text-[#6d28d9] transition-colors">Terms</a>
<a href="#" className="hover:text-[#6d28d9] transition-colors">Privacy</a>
<a href="#" className="hover:text-[#6d28d9] transition-colors">Contact</a>
</div>
<p className="text-xs font-medium text-zinc-400">
<p className="text-xs font-medium text-muted-foreground">
© 2026 Kyoo Media Discovery. All rights reserved.
</p>
</div>
@@ -428,7 +435,9 @@ function CastDetailRoute({ selectedPerson, setSelectedPerson }: any) {
export default function App() {
return (
<BrowserRouter>
<AppContent />
<ThemeProvider>
<AppContent />
</ThemeProvider>
</BrowserRouter>
);
}