From 73f9be91efdcbb469f8a789c55f2a2f2fb2147d5 Mon Sep 17 00:00:00 2001 From: CodeDevMLH <145071728+CodeDevMLH@users.noreply.github.com> Date: Fri, 27 Feb 2026 03:53:05 +0100 Subject: [PATCH] Refactor z-index values and optimize CSS properties for various seasonal styles; enhance performance with will-change property [skip ci] --- Jellyfin.Plugin.Seasonals/Web/birthday.css | 2 +- Jellyfin.Plugin.Seasonals/Web/halloween.css | 2 +- Jellyfin.Plugin.Seasonals/Web/oscar.css | 7 +- .../Web/resurrection.css | 134 +++++----- Jellyfin.Plugin.Seasonals/Web/snowflakes.css | 249 ++++++++---------- Jellyfin.Plugin.Seasonals/Web/space.css | 6 +- Jellyfin.Plugin.Seasonals/Web/spooky.css | 44 +--- Jellyfin.Plugin.Seasonals/Web/sports.css | 9 +- Jellyfin.Plugin.Seasonals/Web/spring.css | 5 +- Jellyfin.Plugin.Seasonals/Web/starwars.css | 3 +- Jellyfin.Plugin.Seasonals/Web/underwater.css | 14 +- 11 files changed, 210 insertions(+), 265 deletions(-) diff --git a/Jellyfin.Plugin.Seasonals/Web/birthday.css b/Jellyfin.Plugin.Seasonals/Web/birthday.css index 3c2a9e7..1b08bd9 100644 --- a/Jellyfin.Plugin.Seasonals/Web/birthday.css +++ b/Jellyfin.Plugin.Seasonals/Web/birthday.css @@ -5,7 +5,7 @@ width: 100vw; height: 100vh; pointer-events: none; - z-index: 9999; + z-index: 10; overflow: hidden; contain: strict; contain: layout paint; diff --git a/Jellyfin.Plugin.Seasonals/Web/halloween.css b/Jellyfin.Plugin.Seasonals/Web/halloween.css index c88ed9e..78aff21 100644 --- a/Jellyfin.Plugin.Seasonals/Web/halloween.css +++ b/Jellyfin.Plugin.Seasonals/Web/halloween.css @@ -7,7 +7,7 @@ width: 100%; height: 100%; pointer-events: none; - z-index: 10000; + z-index: 10; contain: layout paint; } diff --git a/Jellyfin.Plugin.Seasonals/Web/oscar.css b/Jellyfin.Plugin.Seasonals/Web/oscar.css index f674cb9..6c9c481 100644 --- a/Jellyfin.Plugin.Seasonals/Web/oscar.css +++ b/Jellyfin.Plugin.Seasonals/Web/oscar.css @@ -6,7 +6,7 @@ width: 100%; height: 100%; pointer-events: none; - z-index: 10; /* Behind popups but over background */ + z-index: 10; contain: strict; overflow: hidden; } @@ -30,8 +30,9 @@ } .oscar-spotlight { + will-change: transform; position: absolute; - top: -10vh; + top: 0; /* MARK: SPOTLIGHT WIDTH CONFIGURATION */ /* To adjust bottom width (spread), change 'width' property (e.g., 20vw for narrow, 40vw for wide). */ /* To adjust top width (origin), modify first two percentages in 'clip-path' (e.g., 48% 0, 52% 0 for a very thin start). */ @@ -42,9 +43,11 @@ transform-origin: top center; animation: spotlight-sweep 12s infinite alternate ease-in-out; mix-blend-mode: screen; + translate: 0 -10vh; } .oscar-flash { + will-change: transform; position: absolute; width: 10px; height: 10px; diff --git a/Jellyfin.Plugin.Seasonals/Web/resurrection.css b/Jellyfin.Plugin.Seasonals/Web/resurrection.css index 3ffdcc5..c40059f 100644 --- a/Jellyfin.Plugin.Seasonals/Web/resurrection.css +++ b/Jellyfin.Plugin.Seasonals/Web/resurrection.css @@ -1,68 +1,66 @@ -.resurrection-container { - display: block; - position: fixed; - overflow: hidden; - top: 0; - left: 0; - width: 100%; - height: 100%; - pointer-events: none; - z-index: 10; - contain: layout paint; -} - -.resurrection-symbol { - position: fixed; - z-index: 15; - top: 0; - translate: 0 -15vh; - user-select: none; - -webkit-user-select: none; - cursor: default; - animation-name: resurrection-fall; - animation-timing-function: linear; - animation-iteration-count: infinite; - will-change: transform; -} - -.resurrection-sway-wrapper { - will-change: transform; - animation-name: resurrection-sway; - animation-timing-function: ease-in-out; - animation-iteration-count: infinite; -} - -.resurrection-symbol img { - z-index: 15; - height: auto; - width: 56px; - opacity: 0.95; - filter: drop-shadow(0 0 8px rgba(255, 215, 130, 0.5)); -} - -@media (max-width: 768px) { - .resurrection-symbol img { - width: 42px; - } -} - -@keyframes resurrection-fall { - 0% { - transform: translate3d(0, -15vh, 0); - } - - 100% { - transform: translate3d(0, 105vh, 0); - } -} - -@keyframes resurrection-sway { - 0%, - 100% { - transform: translateX(0); - } - - 50% { - transform: translateX(65px); - } -} +.resurrection-container { + display: block; + position: fixed; + overflow: hidden; + top: 0; + left: 0; + width: 100%; + height: 100%; + pointer-events: none; + z-index: 10; + contain: layout paint; +} + +.resurrection-symbol { + position: fixed; + z-index: 15; + top: 0; + translate: 0 -15vh; + user-select: none; cursor: default; + animation-name: resurrection-fall; + animation-timing-function: linear; + animation-iteration-count: infinite; + will-change: transform; +} + +.resurrection-sway-wrapper { + will-change: transform; + animation-name: resurrection-sway; + animation-timing-function: ease-in-out; + animation-iteration-count: infinite; +} + +.resurrection-symbol img { + z-index: 15; + height: auto; + width: 56px; + opacity: 0.95; + filter: drop-shadow(0 0 8px rgba(255, 215, 130, 0.5)); +} + +@media (max-width: 768px) { + .resurrection-symbol img { + width: 42px; + } +} + +@keyframes resurrection-fall { + 0% { + transform: translate3d(0, -15vh, 0); + } + + 100% { + transform: translate3d(0, 105vh, 0); + } +} + +@keyframes resurrection-sway { + 0%, + 100% { + transform: translateX(0); + } + + 50% { + transform: translateX(65px); + } +} diff --git a/Jellyfin.Plugin.Seasonals/Web/snowflakes.css b/Jellyfin.Plugin.Seasonals/Web/snowflakes.css index fd01489..71b7d8c 100644 --- a/Jellyfin.Plugin.Seasonals/Web/snowflakes.css +++ b/Jellyfin.Plugin.Seasonals/Web/snowflakes.css @@ -1,139 +1,112 @@ -.snowflakes { - display: block; - position: fixed; - overflow: hidden; - top: 0; - left: 0; - width: 100%; - height: 100%; - pointer-events: none; - z-index: 10; - contain: layout paint; -} - -.snowflake { - position: fixed; - z-index: 15; - top: 0; - will-change: transform; - translate: 0 -10vh; - font-size: 1em; - color: #fff; - font-family: Arial, sans-serif; - text-shadow: 0 0 5px #000; - user-select: none; - -webkit-user-select: none; - cursor: default; - -webkit-animation-name: heart-fall, heart-shake; - -webkit-animation-duration: 12s, 3s; - -webkit-animation-timing-function: linear, ease-in-out; - -webkit-animation-iteration-count: infinite, infinite; - animation-name: snowflakes-fall, snowflakes-shake; - animation-duration: 12s, 3s; - animation-timing-function: linear, ease-in-out; - animation-iteration-count: infinite, infinite; -} - -@-webkit-keyframes snowflakes-fall { - 0% { - translate: 0 -10vh; - } - - 100% { - translate: 0 110vh; - } -} - -@-webkit-keyframes snowflakes-shake { - - 0%, - 100% { - transform: translateX(0); - } - - 50% { - transform: translateX(80px); - } -} - -@keyframes snowflakes-fall { - 0% { - translate: 0 -10vh; - } - - 100% { - translate: 0 110vh; - } -} - -@keyframes snowflakes-shake { - - 0%, - 100% { - transform: translateX(0); - } - - 50% { - transform: translateX(80px); - } -} - -.snowflake:nth-of-type(0) { - left: 0%; - animation-delay: 0s, 0s; -} - -.snowflake:nth-of-type(1) { - left: 10%; - animation-delay: 1s, 1s; -} - -.snowflake:nth-of-type(2) { - left: 20%; - animation-delay: 6s, 0.5s; -} - -.snowflake:nth-of-type(3) { - left: 30%; - animation-delay: 4s, 2s; -} - -.snowflake:nth-of-type(4) { - left: 40%; - animation-delay: 2s, 2s; -} - -.snowflake:nth-of-type(5) { - left: 50%; - animation-delay: 8s, 3s; -} - -.snowflake:nth-of-type(6) { - left: 60%; - animation-delay: 6s, 2s; -} - -.snowflake:nth-of-type(7) { - left: 70%; - animation-delay: 2.5s, 1s; -} - -.snowflake:nth-of-type(8) { - left: 80%; - animation-delay: 1s, 0s; -} - -.snowflake:nth-of-type(9) { - left: 90%; - animation-delay: 3s, 1.5s; -} - -.snowflake:nth-of-type(10) { - left: 25%; - animation-delay: 2s, 0s; -} - -.snowflake:nth-of-type(11) { - left: 65%; - animation-delay: 4s, 2.5s; +.snowflakes { + display: block; + position: fixed; + overflow: hidden; + top: 0; + left: 0; + width: 100%; + height: 100%; + pointer-events: none; + z-index: 10; + contain: layout paint; +} + +.snowflake { + position: fixed; + z-index: 15; + top: 0; + will-change: transform; + translate: 0 -10vh; + font-size: 1em; + color: #fff; + font-family: Arial, sans-serif; + text-shadow: 0 0 5px #000; + user-select: none; cursor: default; animation-name: snowflakes-fall, snowflakes-shake; + animation-duration: 12s, 3s; + animation-timing-function: linear, ease-in-out; + animation-iteration-count: infinite, infinite; +} + + + +@keyframes snowflakes-fall { + 0% { + translate: 0 -10vh; + } + + 100% { + translate: 0 110vh; + } +} + +@keyframes snowflakes-shake { + + 0%, + 100% { + transform: translateX(0); + } + + 50% { + transform: translateX(80px); + } +} + +.snowflake:nth-of-type(0) { + left: 0%; + animation-delay: 0s, 0s; +} + +.snowflake:nth-of-type(1) { + left: 10%; + animation-delay: 1s, 1s; +} + +.snowflake:nth-of-type(2) { + left: 20%; + animation-delay: 6s, 0.5s; +} + +.snowflake:nth-of-type(3) { + left: 30%; + animation-delay: 4s, 2s; +} + +.snowflake:nth-of-type(4) { + left: 40%; + animation-delay: 2s, 2s; +} + +.snowflake:nth-of-type(5) { + left: 50%; + animation-delay: 8s, 3s; +} + +.snowflake:nth-of-type(6) { + left: 60%; + animation-delay: 6s, 2s; +} + +.snowflake:nth-of-type(7) { + left: 70%; + animation-delay: 2.5s, 1s; +} + +.snowflake:nth-of-type(8) { + left: 80%; + animation-delay: 1s, 0s; +} + +.snowflake:nth-of-type(9) { + left: 90%; + animation-delay: 3s, 1.5s; +} + +.snowflake:nth-of-type(10) { + left: 25%; + animation-delay: 2s, 0s; +} + +.snowflake:nth-of-type(11) { + left: 65%; + animation-delay: 4s, 2.5s; } \ No newline at end of file diff --git a/Jellyfin.Plugin.Seasonals/Web/space.css b/Jellyfin.Plugin.Seasonals/Web/space.css index a721dea..fba730f 100644 --- a/Jellyfin.Plugin.Seasonals/Web/space.css +++ b/Jellyfin.Plugin.Seasonals/Web/space.css @@ -5,12 +5,13 @@ width: 100vw; height: 100vh; pointer-events: none; - z-index: 9999; + z-index: 10; overflow: hidden; contain: strict; } .space-bg-glow { + will-change: transform; position: absolute; top: 0; left: 0; width: 100vw; height: 100vh; background: radial-gradient(circle at 70% 30%, rgba(138, 43, 226, 0.15), transparent 60%), @@ -33,6 +34,7 @@ } .space-shooting-star { + will-change: opacity; position: absolute; width: 250px; height: 3px; @@ -61,11 +63,11 @@ } .space-symbol img { + will-change: transform; width: 6vh; height: auto; max-width: 60px; object-fit: contain; - /* Add a slow spin to images */ animation: space-slow-spin var(--rot-dur, 20s) linear infinite; } diff --git a/Jellyfin.Plugin.Seasonals/Web/spooky.css b/Jellyfin.Plugin.Seasonals/Web/spooky.css index de9a5a0..ff7e605 100644 --- a/Jellyfin.Plugin.Seasonals/Web/spooky.css +++ b/Jellyfin.Plugin.Seasonals/Web/spooky.css @@ -8,6 +8,7 @@ height: 100%; pointer-events: none; z-index: 10; + contain: layout paint; } .spooky { @@ -16,19 +17,8 @@ will-change: transform; translate: 0 120vh; z-index: 15; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; user-select: none; - -webkit-user-select: none; cursor: default; - - -webkit-animation-name: spooky-float; - -webkit-animation-duration: 10s; - -webkit-animation-timing-function: linear; - -webkit-animation-iteration-count: infinite; - -webkit-animation-play-state: running; - animation-name: spooky-float; animation-duration: 10s; animation-timing-function: linear; @@ -40,13 +30,6 @@ width: 30px; height: auto; will-change: transform; - - -webkit-animation-name: spooky-shake; - -webkit-animation-duration: 3s; - -webkit-animation-timing-function: ease-in-out; - -webkit-animation-iteration-count: infinite; - -webkit-animation-play-state: running; - animation-name: spooky-shake; animation-duration: 3s; animation-timing-function: ease-in-out; @@ -59,22 +42,6 @@ width: 100%; } -@-webkit-keyframes spooky-float { - 0% { - translate: 0 120vh; - opacity: 0; - } - 10% { - opacity: 0.8; - } - 90% { - opacity: 0.8; - } - 100% { - translate: 0 -150px; - opacity: 0; - } -} @keyframes spooky-float { 0% { @@ -93,14 +60,6 @@ } } -@-webkit-keyframes spooky-shake { - 0%, 100% { - transform: translateX(0) scale(1) rotate(15deg); - } - 50% { - transform: translateX(80px) scale(1.2) rotate(-15deg); - } -} @keyframes spooky-shake { 0%, 100% { @@ -111,7 +70,6 @@ } } -/* Base predefined starting offsets (if not overridden by js) */ .spooky:nth-of-type(0) { left: 1%; } .spooky:nth-of-type(1) { left: 10%; } .spooky:nth-of-type(2) { left: 20%; } diff --git a/Jellyfin.Plugin.Seasonals/Web/sports.css b/Jellyfin.Plugin.Seasonals/Web/sports.css index 4bce34e..8a04be8 100644 --- a/Jellyfin.Plugin.Seasonals/Web/sports.css +++ b/Jellyfin.Plugin.Seasonals/Web/sports.css @@ -5,16 +5,17 @@ width: 100vw; height: 100vh; pointer-events: none; - z-index: 9999; + z-index: 10; overflow: hidden; contain: strict; } .sports-symbol { position: absolute; - top: -10vh; + top: 0; opacity: 0.9; z-index: 40; + translate: 0 -10vh; } .sports-inner { @@ -29,13 +30,15 @@ } .sports-confetti { + will-change: transform, opacity; position: absolute; - top: -5vh; + top: 0; width: 10px; height: 15px; opacity: 0.8; animation: sports-confetti-fall linear infinite; border-radius: 2px; + translate: 0 -5vh; } .sports-confetti.circle { diff --git a/Jellyfin.Plugin.Seasonals/Web/spring.css b/Jellyfin.Plugin.Seasonals/Web/spring.css index 26d2248..f533bf2 100644 --- a/Jellyfin.Plugin.Seasonals/Web/spring.css +++ b/Jellyfin.Plugin.Seasonals/Web/spring.css @@ -29,13 +29,14 @@ /* Sunbeams */ .spring-sunbeam { position: fixed; - top: -50%; + top: 0; height: 200%; background: linear-gradient(to bottom, rgba(255, 255, 255, 0), rgba(255, 255, 200, 0.08) 50%, rgba(255, 255, 255, 0)); z-index: 5; transform-origin: top center; pointer-events: none; opacity: 0; + translate: 0 -50vh; } /* Grass Container (Wrapper) */ @@ -72,6 +73,7 @@ /* SVG Meadow Layer */ .spring-meadow-layer { + will-change: transform; position: absolute; bottom: 0; left: 0; @@ -94,6 +96,7 @@ } .spring-sway { + will-change: transform; transform-origin: bottom center; animation: spring-meadow-sway 4s ease-in-out infinite alternate; } diff --git a/Jellyfin.Plugin.Seasonals/Web/starwars.css b/Jellyfin.Plugin.Seasonals/Web/starwars.css index 154e36e..0b7db18 100644 --- a/Jellyfin.Plugin.Seasonals/Web/starwars.css +++ b/Jellyfin.Plugin.Seasonals/Web/starwars.css @@ -13,10 +13,11 @@ .starwars-center { position: absolute; - top: 50%; + top: 0; left: 50%; width: 0; height: 0; + translate: 0 50vh; } .starwars-streak { diff --git a/Jellyfin.Plugin.Seasonals/Web/underwater.css b/Jellyfin.Plugin.Seasonals/Web/underwater.css index 66d54b5..84c8dfb 100644 --- a/Jellyfin.Plugin.Seasonals/Web/underwater.css +++ b/Jellyfin.Plugin.Seasonals/Web/underwater.css @@ -35,6 +35,7 @@ } .underwater-seaweed { + will-change: transform, opacity; position: absolute; bottom: -1vh; font-size: 4rem; @@ -52,6 +53,7 @@ } .underwater-bubble { + will-change: transform; position: absolute; bottom: -5vh; border-radius: 50%; @@ -73,13 +75,13 @@ } @keyframes underwater-traverse-up { - 0% { top: 120vh; } - 100% { top: -20vh; } + 0% { top: 0; translate: 0 120vh; } + 100% { top: 0; translate: 0 -20vh; } } @keyframes underwater-traverse-down { - 0% { top: -20vh; } - 100% { top: 120vh; } + 0% { top: 0; translate: 0 -20vh; } + 100% { top: 0; translate: 0 120vh; } } @keyframes underwater-sway-y { @@ -101,8 +103,9 @@ } .underwater-god-rays { + will-change: transform; position: absolute; - top: -50vh; + top: 0; left: -50vw; width: 200vw; height: 200vh; @@ -119,6 +122,7 @@ transform-origin: top center; mix-blend-mode: overlay; filter: blur(5px); + translate: 0 -50vh; } @keyframes god-rays-sway {