fix(ui): polish health page background and priority card layout

Iteration on top of the health redesign:
- layout.tsx: drop backdrop-blur transparency on the global header
  (interfered with the new health page colored backdrop)
- health.tsx: switch from fixed full-viewport background to scoped
  absolute positioning; replace unreliable external grain SVG (vercel
  app) with an inline data URI that always loads
- health-priority-card.tsx: refine spacing, expansion behavior, and
  visual hierarchy
This commit is contained in:
Tam Nhu Tran
2026-04-25 16:29:16 -04:00
parent 4d314ef2b7
commit 90a3d330df
3 changed files with 45 additions and 23 deletions
@@ -1,4 +1,12 @@
import { AlertCircle, AlertTriangle, Copy, Terminal, Wrench, ChevronRight } from 'lucide-react';
import {
AlertCircle,
AlertTriangle,
Copy,
Terminal,
Wrench,
ChevronRight,
RefreshCw,
} from 'lucide-react';
import { Button } from '@/components/ui/button';
import { motion, AnimatePresence } from 'framer-motion';
import { useFixHealth, type HealthCheck } from '@/hooks/use-health';
@@ -14,7 +22,8 @@ interface HealthPriorityCardProps {
export function HealthPriorityCard({ check }: HealthPriorityCardProps) {
const { t } = useTranslation();
const fixMutation = useFixHealth();
const [isExpanded, setIsExpanded] = useState(true);
const hasContent = !!(check.details || check.fix || check.fixable);
const [isExpanded, setIsExpanded] = useState(false);
const isError = check.status === 'error';
const Icon = isError ? AlertCircle : AlertTriangle;
@@ -44,7 +53,10 @@ export function HealthPriorityCard({ check }: HealthPriorityCardProps) {
<div className="absolute inset-0 rounded-[calc(2rem-1px)] shadow-[inset_0_1px_1px_rgba(255,255,255,0.1)] pointer-events-none" />
<div className="p-6">
<div className="flex items-start gap-4">
<div
className={cn('flex items-start gap-4', hasContent && 'cursor-pointer select-none')}
onClick={() => hasContent && setIsExpanded(!isExpanded)}
>
{/* Status Icon with Ring */}
<div
className={cn(
@@ -58,19 +70,20 @@ export function HealthPriorityCard({ check }: HealthPriorityCardProps) {
<div className="flex-1 min-w-0 space-y-1">
<div className="flex items-center justify-between">
<h3 className="text-lg font-bold tracking-tight">{check.name}</h3>
<button
onClick={() => setIsExpanded(!isExpanded)}
className="p-1 hover:bg-muted rounded-full transition-colors"
>
<ChevronRight
className={cn(
'w-5 h-5 text-muted-foreground transition-transform',
isExpanded && 'rotate-90'
)}
/>
</button>
{hasContent && (
<div className="p-1 hover:bg-muted rounded-full transition-colors">
<ChevronRight
className={cn(
'w-5 h-5 text-muted-foreground transition-transform',
isExpanded && 'rotate-90'
)}
/>
</div>
)}
</div>
<p className="text-sm font-medium leading-relaxed">{check.message}</p>
<p className="text-sm font-medium leading-relaxed text-muted-foreground/80">
{check.message}
</p>
</div>
</div>
@@ -118,7 +131,11 @@ export function HealthPriorityCard({ check }: HealthPriorityCardProps) {
: 'bg-amber-500 hover:bg-amber-600'
)}
>
<Wrench className="w-4 h-4 mr-2" />
{fixMutation.isPending ? (
<RefreshCw className="w-4 h-4 mr-2 animate-spin" />
) : (
<Wrench className="w-4 h-4 mr-2" />
)}
{t('health.applyFix')}
</Button>
)}
+1 -1
View File
@@ -41,7 +41,7 @@ export function Layout() {
<SidebarProvider>
<AppSidebar />
<main className="flex-1 flex flex-col min-h-0 overflow-hidden bg-background">
<header className="flex h-14 items-center justify-between px-6 border-b shrink-0 bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
<header className="flex h-14 items-center justify-between px-6 border-b shrink-0 bg-background shadow-sm z-20">
<div className="flex items-center gap-3">
<ClaudeKitBadge />
<SponsorButton />
+11 -6
View File
@@ -47,23 +47,28 @@ export function HealthPage() {
const hasIssues = priorityChecks.length > 0;
return (
<div className="relative min-h-[100dvh] pb-20">
<div className="relative pb-20 overflow-hidden">
{/* Dynamic Background */}
<div className="fixed inset-0 pointer-events-none overflow-hidden">
<div className="absolute inset-0 pointer-events-none">
<div
className={cn(
'absolute -top-[20%] -left-[10%] w-[70%] h-[70%] blur-[120px] rounded-full opacity-[0.08] transition-colors duration-1000',
'absolute top-[5%] -left-[5%] w-[60%] h-[60%] blur-[100px] rounded-full opacity-[0.08] transition-colors duration-1000',
hasIssues ? 'bg-rose-500' : 'bg-emerald-500'
)}
/>
<div
className={cn(
'absolute -bottom-[20%] -right-[10%] w-[60%] h-[60%] blur-[120px] rounded-full opacity-[0.05] transition-colors duration-1000',
'absolute -bottom-[5%] -right-[5%] w-[50%] h-[50%] blur-[100px] rounded-full opacity-[0.06] transition-colors duration-1000',
hasIssues ? 'bg-amber-500' : 'bg-blue-500'
)}
/>
{/* Grain overlay */}
<div className="absolute inset-0 opacity-[0.02] mix-blend-overlay bg-[url('https://grainy-gradients.vercel.app/noise.svg')]" />
{/* Grain overlay - Local SVG data URI for reliability */}
<div
className="absolute inset-0 opacity-[0.03] mix-blend-overlay"
style={{
backgroundImage: `url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.65' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)'/%3E%3C/svg%3E")`,
}}
/>
</div>
<div className="relative p-6 max-w-6xl mx-auto space-y-12">