Implement design system: blob background, glassmorphism, softer corners

- globals.css: add blob keyframe animation (9s ease-in-out infinite)
- layout.tsx: add fixed background layer with 24px grid pattern and two
  animated blurred blobs (primary/5 color) that drift behind all content
- Navigation.tsx: bg-background/80 + backdrop-blur-md + border-border/50
- Sidebar.tsx: same glassmorphism treatment
- card.tsx: rounded-2xl (up from rounded-lg), bg-card/80 + backdrop-blur-sm
  so the blob color bleeds subtly through card surfaces

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-11 01:38:35 -04:00
parent 1e4704ed3f
commit fe52916d84
5 changed files with 26 additions and 11 deletions
+7
View File
@@ -44,6 +44,13 @@ export default function RootLayout({ children }: React.PropsWithChildren) {
/> />
)} )}
{/* Living background */}
<div className="pointer-events-none fixed inset-0 -z-10 overflow-hidden">
<div className="absolute inset-0 bg-[linear-gradient(to_right,#80808010_1px,transparent_1px),linear-gradient(to_bottom,#80808010_1px,transparent_1px)] bg-[size:24px_24px]" />
<div className="animate-blob absolute left-[45%] top-[30%] h-[700px] w-[700px] rounded-full bg-primary/5 blur-3xl" />
<div className="animate-blob absolute right-[20%] top-[60%] h-[500px] w-[500px] rounded-full bg-primary/4 blur-3xl [animation-delay:3s]" />
</div>
<BreadcrumbProvider> <BreadcrumbProvider>
<Navigation /> <Navigation />
<div className="flex flex-1 flex-col pt-16 lg:flex-row"> <div className="flex flex-1 flex-col pt-16 lg:flex-row">
+2 -3
View File
@@ -34,8 +34,7 @@ export function Navigation() {
return ( return (
<> <>
<nav <nav
className={`fixed left-4 right-4 top-4 z-[51] rounded-2xl border bg-background/80 shadow-sm backdrop-blur-md transition-all duration-200 ${isOpen ? "border-transparent" : "border-border/60" className="fixed left-0 right-0 top-0 z-[51] border-b border-border/50 bg-background/80 backdrop-blur-md"
}`}
> >
<div className="relative px-8"> <div className="relative px-8">
<div className="flex h-16 items-center justify-between"> <div className="flex h-16 items-center justify-between">
@@ -87,7 +86,7 @@ export function Navigation() {
aria-hidden="true" aria-hidden="true"
/> />
<div <div
className={`fixed left-4 right-4 top-24 z-50 overflow-hidden rounded-2xl border border-border/50 bg-background/80 shadow-sm backdrop-blur-md transition-all duration-300 lg:hidden ${isOpen ? "max-h-[calc(100vh-8rem)] opacity-100" : "max-h-0 opacity-0" className={`fixed left-0 right-0 top-16 z-50 overflow-hidden border-t border-border/50 bg-background/80 backdrop-blur-md transition-all duration-300 lg:hidden ${isOpen ? "max-h-[calc(100vh-8rem)] opacity-100" : "max-h-0 opacity-0"
}`} }`}
> >
<div className="flex flex-col space-y-2 p-4"> <div className="flex flex-col space-y-2 p-4">
+5 -5
View File
@@ -13,14 +13,14 @@ export function Sidebar() {
<> <>
{/* Mobile layout - horizontal intro bar only on home page */} {/* Mobile layout - horizontal intro bar only on home page */}
{isHomePage && ( {isHomePage && (
<div className="w-full space-y-4 px-6 pb-2 pt-6 sm:px-8 lg:hidden"> <div className="w-full space-y-3 px-4 pb-2 pt-4 sm:px-6 lg:hidden">
<div className="flex items-center space-x-4"> <div className="flex items-center gap-4">
<ImageWithSkeleton <ImageWithSkeleton
src="/headshot.png" src="/headshot.png"
alt={`${name[0]?.first}&nbsp;${name[0]?.last}`} alt={`${name[0]?.first}&nbsp;${name[0]?.last}`}
width={240} width={240}
height={240} height={240}
containerClassName="h-24 w-24 rounded-2xl border border-border/50 shadow-sm" containerClassName="h-20 w-20 rounded-xl border border-border/50 shadow-sm"
className="object-cover" className="object-cover"
priority priority
/> />
@@ -69,8 +69,8 @@ export function Sidebar() {
</div> </div>
)} )}
{/* Desktop layout - clean and elegant sidebar */} {/* Desktop layout - pinned sidebar */}
<div className="fixed bottom-4 left-4 top-24 hidden w-80 overflow-hidden rounded-3xl border border-border/60 bg-background/80 backdrop-blur-xl lg:block"> <div className="fixed left-0 top-16 bottom-0 w-80 border-r border-border/50 bg-background/80 backdrop-blur-md hidden lg:block">
<div className="flex h-full flex-col"> <div className="flex h-full flex-col">
{/* Profile Section */} {/* Profile Section */}
<div className="flex-shrink-0 border-b border-border/50 px-8 py-8"> <div className="flex-shrink-0 border-b border-border/50 px-8 py-8">
+2 -2
View File
@@ -12,7 +12,7 @@ function Card({
data-slot="card" data-slot="card"
data-size={size} data-size={size}
className={cn( className={cn(
"group/card flex flex-col gap-4 overflow-hidden rounded-lg bg-card py-4 text-sm text-card-foreground ring-1 ring-foreground/10 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 *:[img:first-child]:rounded-t-lg *:[img:last-child]:rounded-b-lg", "group/card flex flex-col gap-4 overflow-hidden rounded-2xl bg-card/80 py-4 text-sm text-card-foreground ring-1 ring-foreground/10 backdrop-blur-sm has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 *:[img:first-child]:rounded-t-2xl *:[img:last-child]:rounded-b-2xl",
className className
)} )}
{...props} {...props}
@@ -25,7 +25,7 @@ function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
<div <div
data-slot="card-header" data-slot="card-header"
className={cn( className={cn(
"group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-lg px-4 group-data-[size=sm]/card:px-3 has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3", "group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-2xl px-4 group-data-[size=sm]/card:px-3 has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3",
className className
)} )}
{...props} {...props}
+10 -1
View File
@@ -118,4 +118,13 @@
.animate-fade-in-up-delay-1 { animation: fade-in-up 0.5s ease-out 0.1s both; } .animate-fade-in-up-delay-1 { animation: fade-in-up 0.5s ease-out 0.1s both; }
.animate-fade-in-up-delay-2 { animation: fade-in-up 0.5s ease-out 0.2s both; } .animate-fade-in-up-delay-2 { animation: fade-in-up 0.5s ease-out 0.2s both; }
.animate-fade-in-up-delay-3 { animation: fade-in-up 0.5s ease-out 0.3s both; } .animate-fade-in-up-delay-3 { animation: fade-in-up 0.5s ease-out 0.3s both; }
.animate-fade-in-up-delay-4 { animation: fade-in-up 0.5s ease-out 0.4s both; } .animate-fade-in-up-delay-4 { animation: fade-in-up 0.5s ease-out 0.4s both; }
@keyframes blob {
0%, 100% { transform: scale(1) translate(0px, 0px); }
25% { transform: scale(1.08) translate(40px, -30px); }
50% { transform: scale(0.94) translate(-25px, 50px); }
75% { transform: scale(1.04) translate(35px, 30px); }
}
.animate-blob { animation: blob 9s ease-in-out infinite; }