feat(toolrate): light/dark/system theme toggle with FOUC guard
Build & Push Docker Image / build (push) Successful in 2m30s
Build & Push Docker Image / build (push) Successful in 2m30s
- use-theme hook: localStorage (toolrate-theme, default system), matchMedia change listener, shared singleton listener, sets .dark + color-scheme - ThemeToggle: lightbulb quick toggle (light/dark) + dropdown (light/dark/system) - Sidebar footer + mobile header + standalone login page - inline script in index.html to apply theme pre-render (no FOUC) - recharts axis ticks use hsl(var(--foreground)) for dark-mode readability
This commit is contained in:
@@ -3,6 +3,7 @@ import { LayoutDashboard, Wrench, PlusCircle, BarChart3, LogIn, LogOut, User, Sh
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { ThemeToggle } from "@/components/theme-toggle";
|
||||
|
||||
export function Layout({ children }: { children: React.ReactNode }) {
|
||||
const [location] = useLocation();
|
||||
@@ -43,7 +44,10 @@ export function Layout({ children }: { children: React.ReactNode }) {
|
||||
})}
|
||||
</nav>
|
||||
|
||||
<div className="p-4 border-t">
|
||||
<div className="p-4 border-t space-y-3">
|
||||
<div className="flex justify-end">
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
{isLoading ? (
|
||||
<div className="flex items-center gap-3 px-3 py-2">
|
||||
<Skeleton className="w-8 h-8 rounded-full" />
|
||||
@@ -97,18 +101,21 @@ export function Layout({ children }: { children: React.ReactNode }) {
|
||||
<Wrench className="w-5 h-5" />
|
||||
<span>ToolRate</span>
|
||||
</div>
|
||||
{!isLoading && (
|
||||
isAuthenticated ? (
|
||||
<Button variant="ghost" size="sm" onClick={logout} data-testid="button-logout-mobile">
|
||||
<LogOut className="w-4 h-4" />
|
||||
</Button>
|
||||
) : (
|
||||
<Button variant="outline" size="sm" onClick={() => login(location)} data-testid="button-login-mobile">
|
||||
<LogIn className="w-4 h-4 mr-1" />
|
||||
Sign in
|
||||
</Button>
|
||||
)
|
||||
)}
|
||||
<div className="flex items-center gap-1">
|
||||
<ThemeToggle />
|
||||
{!isLoading && (
|
||||
isAuthenticated ? (
|
||||
<Button variant="ghost" size="sm" onClick={logout} data-testid="button-logout-mobile">
|
||||
<LogOut className="w-4 h-4" />
|
||||
</Button>
|
||||
) : (
|
||||
<Button variant="outline" size="sm" onClick={() => login(location)} data-testid="button-login-mobile">
|
||||
<LogIn className="w-4 h-4 mr-1" />
|
||||
Sign in
|
||||
</Button>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</header>
|
||||
<div className="flex-1 p-6 md:p-8 overflow-auto">
|
||||
{children}
|
||||
|
||||
Reference in New Issue
Block a user