Add Christmas decorations and theme styles
Introduced a ChristmasDecorations component with animated snowflakes, twinkling stars, and festive emojis. Updated global CSS and Tailwind config to include Christmas-themed animations, colors, and effects. Integrated the decorations into the main layout for a seasonal appearance.
This commit is contained in:
146
app/globals.css
146
app/globals.css
@@ -241,6 +241,122 @@ body {
|
|||||||
.bg-muted {
|
.bg-muted {
|
||||||
background-color: hsl(var(--muted) / 0.8);
|
background-color: hsl(var(--muted) / 0.8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Christmas-themed animations */
|
||||||
|
@keyframes twinkle {
|
||||||
|
0%, 100% { opacity: 1; transform: scale(1); }
|
||||||
|
50% { opacity: 0.3; transform: scale(0.8); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes snowflake {
|
||||||
|
0% {
|
||||||
|
transform: translateY(-100vh) rotate(0deg);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translateY(100vh) rotate(360deg);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes sparkle {
|
||||||
|
0%, 100% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0) rotate(0deg);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1) rotate(180deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes glow {
|
||||||
|
0%, 100% {
|
||||||
|
box-shadow: 0 0 5px hsl(var(--christmas-red)), 0 0 10px hsl(var(--christmas-red)), 0 0 15px hsl(var(--christmas-red));
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
box-shadow: 0 0 10px hsl(var(--christmas-green)), 0 0 20px hsl(var(--christmas-green)), 0 0 30px hsl(var(--christmas-green));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Christmas decorative elements */
|
||||||
|
.christmas-twinkle {
|
||||||
|
animation: twinkle 2s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.christmas-snowflake {
|
||||||
|
position: absolute;
|
||||||
|
color: white;
|
||||||
|
font-size: 1rem;
|
||||||
|
animation: snowflake 10s linear infinite;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.christmas-sparkle {
|
||||||
|
animation: sparkle 1.5s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.christmas-glow {
|
||||||
|
animation: glow 2s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Subtle Christmas gradient backgrounds */
|
||||||
|
.christmas-gradient {
|
||||||
|
background: linear-gradient(135deg,
|
||||||
|
hsl(var(--christmas-red) / 0.1) 0%,
|
||||||
|
hsl(var(--christmas-green) / 0.1) 50%,
|
||||||
|
hsl(var(--christmas-gold) / 0.1) 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Christmas-themed borders */
|
||||||
|
.christmas-border {
|
||||||
|
border: 2px solid;
|
||||||
|
border-image: linear-gradient(45deg,
|
||||||
|
hsl(var(--christmas-red)),
|
||||||
|
hsl(var(--christmas-green)),
|
||||||
|
hsl(var(--christmas-gold))) 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Subtle Christmas pattern overlay */
|
||||||
|
body::before {
|
||||||
|
content: '';
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-image:
|
||||||
|
radial-gradient(circle at 20% 50%, hsl(var(--christmas-red) / 0.03) 0%, transparent 50%),
|
||||||
|
radial-gradient(circle at 80% 80%, hsl(var(--christmas-green) / 0.03) 0%, transparent 50%),
|
||||||
|
radial-gradient(circle at 40% 20%, hsl(var(--christmas-gold) / 0.03) 0%, transparent 50%);
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Christmas-themed card hover effects */
|
||||||
|
.card:hover {
|
||||||
|
border-color: hsl(var(--christmas-red) / 0.3);
|
||||||
|
transition: border-color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Christmas-themed button hover effects */
|
||||||
|
button.bg-primary:hover {
|
||||||
|
background: linear-gradient(135deg,
|
||||||
|
hsl(var(--christmas-red)),
|
||||||
|
hsl(var(--christmas-green)));
|
||||||
|
transition: background 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Christmas-themed link colors */
|
||||||
|
a:hover {
|
||||||
|
color: hsl(var(--christmas-red));
|
||||||
|
transition: color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Subtle Christmas border on focus */
|
||||||
|
*:focus-visible {
|
||||||
|
outline-color: hsl(var(--christmas-red));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
@@ -270,6 +386,10 @@ body {
|
|||||||
--chart-4: 43 74% 66%;
|
--chart-4: 43 74% 66%;
|
||||||
--chart-5: 27 87% 67%;
|
--chart-5: 27 87% 67%;
|
||||||
--radius: 0.5rem;
|
--radius: 0.5rem;
|
||||||
|
/* Christmas colors */
|
||||||
|
--christmas-red: 0 84% 50%;
|
||||||
|
--christmas-green: 142 76% 36%;
|
||||||
|
--christmas-gold: 43 96% 56%;
|
||||||
}
|
}
|
||||||
.dark {
|
.dark {
|
||||||
--background: 0 0% 3.9%;
|
--background: 0 0% 3.9%;
|
||||||
@@ -278,24 +398,28 @@ body {
|
|||||||
--card-foreground: 0 0% 98%;
|
--card-foreground: 0 0% 98%;
|
||||||
--popover: 0 0% 3.9%;
|
--popover: 0 0% 3.9%;
|
||||||
--popover-foreground: 0 0% 98%;
|
--popover-foreground: 0 0% 98%;
|
||||||
--primary: 0 0% 98%;
|
--primary: 0 84% 50%;
|
||||||
--primary-foreground: 0 0% 9%;
|
--primary-foreground: 0 0% 98%;
|
||||||
--secondary: 0 0% 14.9%;
|
--secondary: 142 76% 36%;
|
||||||
--secondary-foreground: 0 0% 98%;
|
--secondary-foreground: 0 0% 98%;
|
||||||
--muted: 0 0% 14.9%;
|
--muted: 0 0% 14.9%;
|
||||||
--muted-foreground: 0 0% 63.9%;
|
--muted-foreground: 0 0% 63.9%;
|
||||||
--accent: 0 0% 14.9%;
|
--accent: 142 76% 36%;
|
||||||
--accent-foreground: 0 0% 98%;
|
--accent-foreground: 0 0% 98%;
|
||||||
--destructive: 0 62.8% 30.6%;
|
--destructive: 0 62.8% 30.6%;
|
||||||
--destructive-foreground: 0 0% 98%;
|
--destructive-foreground: 0 0% 98%;
|
||||||
--border: 0 0% 14.9%;
|
--border: 142 76% 20%;
|
||||||
--input: 0 0% 14.9%;
|
--input: 0 0% 14.9%;
|
||||||
--ring: 0 0% 83.1%;
|
--ring: 0 84% 50%;
|
||||||
--chart-1: 220 70% 50%;
|
--chart-1: 0 84% 50%;
|
||||||
--chart-2: 160 60% 45%;
|
--chart-2: 142 76% 36%;
|
||||||
--chart-3: 30 80% 55%;
|
--chart-3: 43 96% 56%;
|
||||||
--chart-4: 280 65% 60%;
|
--chart-4: 0 84% 50%;
|
||||||
--chart-5: 340 75% 55%;
|
--chart-5: 142 76% 36%;
|
||||||
|
/* Christmas colors */
|
||||||
|
--christmas-red: 0 84% 50%;
|
||||||
|
--christmas-green: 142 76% 36%;
|
||||||
|
--christmas-gold: 43 96% 56%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import type React from "react"
|
|||||||
import { NotificationProvider } from "@/lib/notification-context"
|
import { NotificationProvider } from "@/lib/notification-context"
|
||||||
import { Metadata, Viewport } from "next"
|
import { Metadata, Viewport } from "next"
|
||||||
import KeepOnlineWrapper from "@/components/layout/KeepOnlineWrapper"
|
import KeepOnlineWrapper from "@/components/layout/KeepOnlineWrapper"
|
||||||
|
import { ChristmasDecorations } from "@/components/christmas-decorations"
|
||||||
|
|
||||||
const inter = Inter({ subsets: ["latin"] })
|
const inter = Inter({ subsets: ["latin"] })
|
||||||
|
|
||||||
@@ -74,6 +75,7 @@ export default function RootLayout({
|
|||||||
<body className={inter.className}>
|
<body className={inter.className}>
|
||||||
<ThemeProvider attribute="class" defaultTheme="dark" enableSystem disableTransitionOnChange>
|
<ThemeProvider attribute="class" defaultTheme="dark" enableSystem disableTransitionOnChange>
|
||||||
<NotificationProvider>
|
<NotificationProvider>
|
||||||
|
<ChristmasDecorations />
|
||||||
<Toaster
|
<Toaster
|
||||||
theme="dark"
|
theme="dark"
|
||||||
richColors
|
richColors
|
||||||
|
|||||||
60
components/christmas-decorations.tsx
Normal file
60
components/christmas-decorations.tsx
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { useEffect, useState } from "react"
|
||||||
|
|
||||||
|
export function ChristmasDecorations() {
|
||||||
|
const [snowflakes, setSnowflakes] = useState<Array<{ id: number; left: number; delay: number; duration: number }>>([])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Generate snowflakes
|
||||||
|
const flakes = Array.from({ length: 30 }, (_, i) => ({
|
||||||
|
id: i,
|
||||||
|
left: Math.random() * 100,
|
||||||
|
delay: Math.random() * 5,
|
||||||
|
duration: 8 + Math.random() * 4,
|
||||||
|
}))
|
||||||
|
setSnowflakes(flakes)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="fixed inset-0 pointer-events-none z-50 overflow-hidden">
|
||||||
|
{/* Snowflakes */}
|
||||||
|
{snowflakes.map((flake) => (
|
||||||
|
<div
|
||||||
|
key={flake.id}
|
||||||
|
className="absolute top-0 text-white/60 text-xs animate-snowflake"
|
||||||
|
style={{
|
||||||
|
left: `${flake.left}%`,
|
||||||
|
animationDelay: `${flake.delay}s`,
|
||||||
|
animationDuration: `${flake.duration}s`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
❄
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{/* Twinkling stars in corners */}
|
||||||
|
<div className="absolute top-4 left-4 text-yellow-300 animate-twinkle text-lg">
|
||||||
|
✨
|
||||||
|
</div>
|
||||||
|
<div className="absolute top-4 right-4 text-yellow-300 animate-twinkle text-lg" style={{ animationDelay: '0.5s' }}>
|
||||||
|
✨
|
||||||
|
</div>
|
||||||
|
<div className="absolute bottom-4 left-4 text-yellow-300 animate-twinkle text-lg" style={{ animationDelay: '1s' }}>
|
||||||
|
✨
|
||||||
|
</div>
|
||||||
|
<div className="absolute bottom-4 right-4 text-yellow-300 animate-twinkle text-lg" style={{ animationDelay: '1.5s' }}>
|
||||||
|
✨
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Christmas tree emoji decorations */}
|
||||||
|
<div className="absolute top-8 left-1/4 text-green-500/40 animate-sparkle text-xl" style={{ animationDelay: '2s' }}>
|
||||||
|
🎄
|
||||||
|
</div>
|
||||||
|
<div className="absolute top-8 right-1/4 text-green-500/40 animate-sparkle text-xl" style={{ animationDelay: '3s' }}>
|
||||||
|
🎄
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"commitHash": "884574c",
|
"commitHash": "48f1b97",
|
||||||
"buildTime": "2025-12-03T18:07:05.932Z"
|
"buildTime": "2025-12-08T00:12:30.781Z"
|
||||||
}
|
}
|
||||||
@@ -29,12 +29,36 @@ const config: Config = {
|
|||||||
shimmer: {
|
shimmer: {
|
||||||
'0%': { transform: 'translateX(-100%)' },
|
'0%': { transform: 'translateX(-100%)' },
|
||||||
'100%': { transform: 'translateX(100%)' }
|
'100%': { transform: 'translateX(100%)' }
|
||||||
|
},
|
||||||
|
twinkle: {
|
||||||
|
'0%, 100%': { opacity: '1', transform: 'scale(1)' },
|
||||||
|
'50%': { opacity: '0.3', transform: 'scale(0.8)' }
|
||||||
|
},
|
||||||
|
snowflake: {
|
||||||
|
'0%': { transform: 'translateY(-100vh) rotate(0deg)', opacity: '1' },
|
||||||
|
'100%': { transform: 'translateY(100vh) rotate(360deg)', opacity: '0' }
|
||||||
|
},
|
||||||
|
sparkle: {
|
||||||
|
'0%, 100%': { opacity: '0', transform: 'scale(0) rotate(0deg)' },
|
||||||
|
'50%': { opacity: '1', transform: 'scale(1) rotate(180deg)' }
|
||||||
|
},
|
||||||
|
glow: {
|
||||||
|
'0%, 100%': {
|
||||||
|
boxShadow: '0 0 5px hsl(0 84% 50%), 0 0 10px hsl(0 84% 50%), 0 0 15px hsl(0 84% 50%)'
|
||||||
|
},
|
||||||
|
'50%': {
|
||||||
|
boxShadow: '0 0 10px hsl(142 76% 36%), 0 0 20px hsl(142 76% 36%), 0 0 30px hsl(142 76% 36%)'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
animation: {
|
animation: {
|
||||||
"accordion-down": "accordion-down 0.2s ease-out",
|
"accordion-down": "accordion-down 0.2s ease-out",
|
||||||
"accordion-up": "accordion-up 0.2s ease-out",
|
"accordion-up": "accordion-up 0.2s ease-out",
|
||||||
"shimmer": "shimmer 2s infinite"
|
"shimmer": "shimmer 2s infinite",
|
||||||
|
"twinkle": "twinkle 2s ease-in-out infinite",
|
||||||
|
"snowflake": "snowflake 10s linear infinite",
|
||||||
|
"sparkle": "sparkle 1.5s ease-in-out infinite",
|
||||||
|
"glow": "glow 2s ease-in-out infinite"
|
||||||
},
|
},
|
||||||
colors: {
|
colors: {
|
||||||
background: 'hsl(var(--background))',
|
background: 'hsl(var(--background))',
|
||||||
|
|||||||
Reference in New Issue
Block a user