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
-8
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user