From c0cfa261a1cd40a5ab160e3f5a6a239c3dee3c9f Mon Sep 17 00:00:00 2001 From: Calum Muir Date: Mon, 22 Dec 2025 15:21:10 +0000 Subject: [PATCH] Add holiday wishlist banner --- client/src/App.tsx | 25 ++++ client/src/index.css | 342 +++++++++++++++++++++++++++++-------------- 2 files changed, 255 insertions(+), 112 deletions(-) diff --git a/client/src/App.tsx b/client/src/App.tsx index dca898d..0247544 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -41,6 +41,31 @@ function App() { +
+ + + The ponies have had a chat and put together an Amazon + Wish-List for Christmas! Click here + +
diff --git a/client/src/index.css b/client/src/index.css index e76a090..151c990 100644 --- a/client/src/index.css +++ b/client/src/index.css @@ -2,116 +2,116 @@ @tailwind utilities; @layer components { - .all-\[unset\] { - all: unset; - } + .all-\[unset\] { + all: unset; + } } :root { - --animate-spin: spin 1s linear infinite; + --animate-spin: spin 1s linear infinite; } .animate-fade-in { - animation: fade-in 1s var(--animation-delay, 0s) ease forwards; + animation: fade-in 1s var(--animation-delay, 0s) ease forwards; } .animate-fade-up { - animation: fade-up 1s var(--animation-delay, 0s) ease forwards; + animation: fade-up 1s var(--animation-delay, 0s) ease forwards; } .animate-marquee { - animation: marquee var(--duration) infinite linear; + animation: marquee var(--duration) infinite linear; } .animate-marquee-vertical { - animation: marquee-vertical var(--duration) linear infinite; + animation: marquee-vertical var(--duration) linear infinite; } .animate-shimmer { - animation: shimmer 8s infinite; + animation: shimmer 8s infinite; } .animate-spin { - animation: var(--animate-spin); + animation: var(--animate-spin); } @keyframes spin { - to { - transform: rotate(1turn); - } + to { + transform: rotate(1turn); + } } @keyframes image-glow { - 0% { - opacity: 0; - animation-timing-function: cubic-bezier(0.74, 0.25, 0.76, 1); - } + 0% { + opacity: 0; + animation-timing-function: cubic-bezier(0.74, 0.25, 0.76, 1); + } - 10% { - opacity: 0.7; - animation-timing-function: cubic-bezier(0.12, 0.01, 0.08, 0.99); - } + 10% { + opacity: 0.7; + animation-timing-function: cubic-bezier(0.12, 0.01, 0.08, 0.99); + } - to { - opacity: 0.4; - } + to { + opacity: 0.4; + } } @keyframes fade-in { - 0% { - opacity: 0; - transform: translateY(-10px); - } + 0% { + opacity: 0; + transform: translateY(-10px); + } - to { - opacity: 1; - transform: none; - } + to { + opacity: 1; + transform: none; + } } @keyframes fade-up { - 0% { - opacity: 0; - transform: translateY(20px); - } + 0% { + opacity: 0; + transform: translateY(20px); + } - to { - opacity: 1; - transform: none; - } + to { + opacity: 1; + transform: none; + } } @keyframes shimmer { - 0%, - 90%, - to { - background-position: calc(-100% - var(--shimmer-width)) 0; - } + 0%, + 90%, + to { + background-position: calc(-100% - var(--shimmer-width)) 0; + } - 30%, - 60% { - background-position: calc(100% + var(--shimmer-width)) 0; - } + 30%, + 60% { + background-position: calc(100% + var(--shimmer-width)) 0; + } } @keyframes marquee { - 0% { - transform: translate(0); - } + 0% { + transform: translate(0); + } - to { - transform: translateX(calc(-100% - var(--gap))); - } + to { + transform: translateX(calc(-100% - var(--gap))); + } } @keyframes marquee-vertical { - 0% { - transform: translateY(0); - } + 0% { + transform: translateY(0); + } - to { - transform: translateY(calc(-100% - var(--gap))); - } + to { + transform: translateY(calc(-100% - var(--gap))); + } } @tailwind base; @@ -119,80 +119,198 @@ @tailwind utilities; @layer base { - :root { - --background: 0 0% 100%; - --foreground: 222.2 47.4% 11.2%; + :root { + --background: 0 0% 100%; + --foreground: 222.2 47.4% 11.2%; - --muted: 210 40% 96.1%; - --muted-foreground: 215.4 16.3% 46.9%; + --muted: 210 40% 96.1%; + --muted-foreground: 215.4 16.3% 46.9%; - --popover: 0 0% 100%; - --popover-foreground: 222.2 47.4% 11.2%; + --popover: 0 0% 100%; + --popover-foreground: 222.2 47.4% 11.2%; - --border: 214.3 31.8% 91.4%; - --input: 214.3 31.8% 91.4%; + --border: 214.3 31.8% 91.4%; + --input: 214.3 31.8% 91.4%; - --card: transparent; - --card-foreground: 222.2 47.4% 11.2%; + --card: transparent; + --card-foreground: 222.2 47.4% 11.2%; - --primary: 222.2 47.4% 11.2%; - --primary-foreground: 210 40% 98%; + --primary: 222.2 47.4% 11.2%; + --primary-foreground: 210 40% 98%; - --secondary: 210 40% 96.1%; - --secondary-foreground: 222.2 47.4% 11.2%; + --secondary: 210 40% 96.1%; + --secondary-foreground: 222.2 47.4% 11.2%; - --accent: 210 40% 96.1%; - --accent-foreground: 222.2 47.4% 11.2%; + --accent: 210 40% 96.1%; + --accent-foreground: 222.2 47.4% 11.2%; - --destructive: 0 100% 50%; - --destructive-foreground: 210 40% 98%; + --destructive: 0 100% 50%; + --destructive-foreground: 210 40% 98%; - --ring: 215 20.2% 65.1%; + --ring: 215 20.2% 65.1%; - --radius: 0.5rem; - } + --radius: 0.5rem; + } - .dark { - --background: 224 71% 4%; - --foreground: 213 31% 91%; + .dark { + --background: 224 71% 4%; + --foreground: 213 31% 91%; - --muted: 223 47% 11%; - --muted-foreground: 215.4 16.3% 56.9%; + --muted: 223 47% 11%; + --muted-foreground: 215.4 16.3% 56.9%; - --accent: 216 34% 17%; - --accent-foreground: 210 40% 98%; + --accent: 216 34% 17%; + --accent-foreground: 210 40% 98%; - --popover: 224 71% 4%; - --popover-foreground: 215 20.2% 65.1%; + --popover: 224 71% 4%; + --popover-foreground: 215 20.2% 65.1%; - --border: 216 34% 17%; - --input: 216 34% 17%; + --border: 216 34% 17%; + --input: 216 34% 17%; - --card: transparent; - --card-foreground: 213 31% 91%; + --card: transparent; + --card-foreground: 213 31% 91%; - --primary: 210 40% 98%; - --primary-foreground: 222.2 47.4% 1.2%; + --primary: 210 40% 98%; + --primary-foreground: 222.2 47.4% 1.2%; - --secondary: 222.2 47.4% 11.2%; - --secondary-foreground: 210 40% 98%; + --secondary: 222.2 47.4% 11.2%; + --secondary-foreground: 210 40% 98%; - --destructive: 0 63% 31%; - --destructive-foreground: 210 40% 98%; + --destructive: 0 63% 31%; + --destructive-foreground: 210 40% 98%; - --ring: 216 34% 17%; + --ring: 216 34% 17%; - --radius: 0.5rem; - } + --radius: 0.5rem; + } } @layer base { - * { - @apply border-border; - } + * { + @apply border-border; + } - body { - @apply bg-background text-foreground; - font-feature-settings: "rlig" 1, "calt" 1; - } + body { + @apply bg-background text-foreground; + font-feature-settings: + "rlig" 1, + "calt" 1; + } +} + +.snow-banner { + position: sticky; + top: 0; + z-index: 50; + overflow: hidden; + background: linear-gradient(120deg, #0a3b6a, #0f5b8f); + border-bottom: 1px solid rgba(255, 255, 255, 0.2); + animation: banner-slide-down 0.6s ease-out; +} + +.snow-banner__link { + display: block; + padding: 14px 20px; + text-align: center; + font-family: "Merriweather", Helvetica, serif; + font-weight: 700; + color: #ffffff; + text-decoration: none; + letter-spacing: 0.01em; +} + +.snow-banner__link:hover, +.snow-banner__link:focus-visible { + text-decoration: underline; +} + +.snow-banner__snow { + pointer-events: none; + position: absolute; + inset: 0; +} + +.snowflake { + position: absolute; + top: -12px; + width: 6px; + height: 6px; + border-radius: 999px; + background: rgba(255, 255, 255, 0.8); + animation: snow-fall 6s linear infinite; +} + +.snowflake-1 { + left: 8%; + animation-delay: 0s; + animation-duration: 5.5s; +} + +.snowflake-2 { + left: 18%; + animation-delay: 1s; + animation-duration: 6.5s; +} + +.snowflake-3 { + left: 32%; + animation-delay: 0.5s; + animation-duration: 5.8s; +} + +.snowflake-4 { + left: 45%; + animation-delay: 1.4s; + animation-duration: 6.2s; +} + +.snowflake-5 { + left: 58%; + animation-delay: 0.2s; + animation-duration: 5.9s; +} + +.snowflake-6 { + left: 72%; + animation-delay: 1.1s; + animation-duration: 6.7s; +} + +.snowflake-7 { + left: 84%; + animation-delay: 0.7s; + animation-duration: 5.6s; +} + +.snowflake-8 { + left: 93%; + animation-delay: 1.6s; + animation-duration: 6.9s; +} + +@keyframes banner-slide-down { + from { + transform: translateY(-100%); + } + + to { + transform: translateY(0); + } +} + +@keyframes snow-fall { + 0% { + transform: translateY(-10px); + opacity: 0; + } + + 10% { + opacity: 0.9; + } + + 100% { + transform: translateY(60px); + opacity: 0; + } }