feat(site): drifting cloud puffs across the public background
CI / check (push) Successful in 2m40s
CI / deploy (push) Successful in 2m13s
CI / publish-container (push) Successful in 2m10s

Add a fixed, decorative layer of soft clouds that slowly float across
the Habbo sky behind the content:
- Five clouds at staggered sizes, heights, opacities and loop timings
  (60s–190s) so the drift feels organic.
- Dimmed further in dark mode; frozen by prefers-reduced-motion.
- Pure decoration: aria-hidden, pointer-events: none, no color
  utilities in markup (cloud shapes live in globals.css).
This commit is contained in:
openhands committed 2026-09-12 17:12:55 +02:00
1 parent efd3b00075
commit 97012a78f6
3 files changed
+108

No files matched your search

+2
View File
@@ -1,5 +1,6 @@
import dynamic from "next/dynamic";
import type { ReactNode } from "react";
import { CloudsField } from "@/components/clouds-field";
import MotionPageWrapper from "@/components/motion-page-wrapper";
import { Navigation } from "@/components/navigation";
import { SiteFooter } from "@/components/site-footer";
@@ -29,6 +30,7 @@ export default async function SiteLayout({
return (
<>
<CloudsField />
{session?.user?.id ? (
<>
<div data-theme-block="top_header">
+91
View File
@@ -342,6 +342,97 @@ summary,
transparent 100%
);
}
/* Floating cloud puffs drifting across the public background. */
.clouds {
position: fixed;
inset: 0;
z-index: -1;
pointer-events: none;
overflow: hidden;
}
.cloud {
position: absolute;
width: clamp(140px, 22vw, 260px);
height: clamp(40px, 7vw, 70px);
left: 0;
opacity: 0.7;
transform: scale(var(--cloud-scale, 1));
filter: blur(0.5px);
will-change: transform;
animation-name: cloud-drift;
animation-timing-function: linear;
animation-iteration-count: infinite;
background:
radial-gradient(
ellipse 62% 55% at 30% 50%,
rgba(255, 255, 255, 0.6) 0%,
transparent 70%
),
radial-gradient(
ellipse 70% 60% at 55% 45%,
rgba(255, 255, 255, 0.55) 0%,
transparent 72%
),
radial-gradient(
ellipse 60% 55% at 78% 58%,
rgba(255, 255, 255, 0.45) 0%,
transparent 72%
);
}
.cloud-1 {
--cloud-scale: 1.15;
top: 7%;
animation-duration: 95s;
animation-delay: -30s;
}
.cloud-2 {
--cloud-scale: 0.75;
top: 15%;
opacity: 0.45;
animation-duration: 130s;
animation-delay: -85s;
}
.cloud-3 {
--cloud-scale: 0.9;
top: 24%;
opacity: 0.5;
animation-duration: 160s;
animation-delay: -60s;
}
.cloud-4 {
--cloud-scale: 0.6;
top: 34%;
opacity: 0.3;
animation-duration: 110s;
animation-delay: -105s;
}
.cloud-5 {
--cloud-scale: 1.35;
top: 45%;
opacity: 0.22;
animation-duration: 190s;
animation-delay: -140s;
}
@keyframes cloud-drift {
0% {
transform: translateX(clamp(-30vw, -220px, -140px))
scale(var(--cloud-scale, 1));
}
100% {
transform: translateX(calc(100vw + 260px)) scale(var(--cloud-scale, 1));
}
}
html.dark .cloud {
opacity: 0.28;
}
@media (prefers-reduced-motion: reduce) {
.cloud {
animation: none;
transform: translateX(clamp(-30vw, -220px, -140px))
scale(var(--cloud-scale, 1));
}
}
/* Subtle primary glow */
.site-bg::after {
content: "";
+15
View File
@@ -0,0 +1,15 @@
/**
* Drifting cloud puffs that float across the public background.
* Pure decoration: fixed, behind the content, no pointer events.
*/
export function CloudsField() {
return (
<div className="clouds" aria-hidden="true">
<span className="cloud cloud-1" />
<span className="cloud cloud-2" />
<span className="cloud cloud-3" />
<span className="cloud cloud-4" />
<span className="cloud cloud-5" />
</div>
);
}