From bb9f1a2f6ef4c22236cc7c092bc2d7cd486b07bb Mon Sep 17 00:00:00 2001 From: moh Date: Sun, 20 Sep 2026 18:53:48 +0200 Subject: [PATCH] STYLED - Add a layered ambient backdrop behind all heroes Replace the flat, empty hero background with a pure-CSS layered backdrop: faded grid, brand glow, drifting brand orbs, grain, and a bottom fade that blends the hero into page content. Applies to both the home hero and the compact page heroes via HeroMotionBackdrop; respects prefers-reduced-motion. --- app/globals.css | 127 +++++++++++++++++++++ components/layout/hero-motion-backdrop.tsx | 30 ++++- 2 files changed, 152 insertions(+), 5 deletions(-) diff --git a/app/globals.css b/app/globals.css index 0b49321..649f99f 100644 --- a/app/globals.css +++ b/app/globals.css @@ -295,6 +295,133 @@ html[lang="ar"] .eyebrow { display: none; } + /* ---- Layered hero backdrop (HeroMotionBackdrop) ---- */ + .hero-backdrop-base { + position: absolute; + inset: 0; + background: hsl(var(--background)); + } + + .hero-backdrop-grid { + position: absolute; + inset: 0; + background-image: + linear-gradient(hsl(var(--border) / 0.55) 1px, transparent 1px), + linear-gradient(90deg, hsl(var(--border) / 0.55) 1px, transparent 1px); + background-size: 30px 30px; + background-position: center top; + opacity: 0.55; + -webkit-mask-image: radial-gradient(ellipse 78% 58% at 50% 30%, #000 0%, transparent 72%); + mask-image: radial-gradient(ellipse 78% 58% at 50% 30%, #000 0%, transparent 72%); + } + + .dark .hero-backdrop-grid { + opacity: 0.4; + } + + .hero-backdrop-glow { + position: absolute; + inset: 0; + background: + radial-gradient(58% 46% at 50% 20%, hsl(var(--brand-primary) / 0.16), transparent 70%), + radial-gradient(46% 42% at 80% 10%, hsl(var(--brand-secondary) / 0.13), transparent 68%), + radial-gradient(48% 44% at 16% 18%, hsl(var(--brand-primary) / 0.1), transparent 66%); + } + + .hero-orb { + position: absolute; + border-radius: 9999px; + filter: blur(64px); + will-change: transform; + } + + .hero-orb-1 { + top: -10%; + inset-inline-start: -8%; + height: 32rem; + width: 32rem; + background: hsl(var(--brand-primary) / 0.18); + animation: hero-orb-drift-a 22s ease-in-out infinite; + } + + .hero-orb-2 { + top: -6%; + inset-inline-end: -10%; + height: 28rem; + width: 28rem; + background: hsl(var(--brand-secondary) / 0.15); + animation: hero-orb-drift-b 27s ease-in-out infinite; + } + + .hero-orb-3 { + bottom: -22%; + inset-inline-start: 24%; + height: 26rem; + width: 26rem; + background: hsl(var(--brand-primary) / 0.1); + animation: hero-orb-drift-a 31s ease-in-out infinite reverse; + } + + .hero-backdrop-noise { + position: absolute; + inset: -8%; + opacity: 0.26; + mix-blend-mode: soft-light; + } + + .dark .hero-backdrop-noise { + opacity: 0.1; + } + + .hero-backdrop-floor { + position: absolute; + inset-inline: 0; + bottom: 0; + height: 40%; + background: linear-gradient(to bottom, transparent, hsl(var(--background)) 92%); + } + + .hero-backdrop.is-compact .hero-backdrop-grid { + opacity: 0.32; + } + + .hero-backdrop.is-compact .hero-orb { + opacity: 0.7; + filter: blur(72px); + } + + @keyframes hero-orb-drift-a { + 0%, + 100% { + transform: translate3d(0, 0, 0) scale(1); + } + 33% { + transform: translate3d(70px, -48px, 0) scale(1.1); + } + 66% { + transform: translate3d(-42px, 26px, 0) scale(0.94); + } + } + + @keyframes hero-orb-drift-b { + 0%, + 100% { + transform: translate3d(0, 0, 0) scale(1); + } + 33% { + transform: translate3d(-80px, 42px, 0) scale(0.92); + } + 66% { + transform: translate3d(38px, -24px, 0) scale(1.08); + } + } + + @media (prefers-reduced-motion: reduce) { + .hero-orb { + animation: none; + } + } + .hero-title-line { display: inline-block; padding-inline: 0.02em; diff --git a/components/layout/hero-motion-backdrop.tsx b/components/layout/hero-motion-backdrop.tsx index 221a76b..843eb26 100644 --- a/components/layout/hero-motion-backdrop.tsx +++ b/components/layout/hero-motion-backdrop.tsx @@ -1,13 +1,33 @@ +import { cn } from "@/lib/utils"; + type HeroMotionBackdropProps = { compact?: boolean; }; -export function HeroMotionBackdrop({}: HeroMotionBackdropProps) { +/** + * Layered ambient backdrop rendered behind every hero (home + page). + * Pure CSS so it needs no client JS: a faded grid for structure, a soft + * brand glow, slowly drifting brand orbs, a grain overlay for texture, and + * a bottom fade that blends the hero into the page content. The orb drift is + * disabled under `prefers-reduced-motion` (see globals.css). + */ +export function HeroMotionBackdrop({ compact = false }: HeroMotionBackdropProps) { return ( -
-
-
-
+