« get me outta code hell

content, css: track banners - hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
diff options
context:
space:
mode:
author(quasar) nebula <qznebula@protonmail.com>2026-03-14 15:51:01 -0300
committer(quasar) nebula <qznebula@protonmail.com>2026-03-14 15:51:01 -0300
commita76cadc34f4e3a2d48816d3d01968fc3283e1af0 (patch)
tree2c2eaa2b224ce9ece93f1de361acf58e15a951ef
parent28471913b2da726ab07b7549310d976c1b31bb54 (diff)
content, css: track banners preview
-rw-r--r--src/common-util/colors.js2
-rw-r--r--src/content/dependencies/generateAlbumBanner.js22
-rw-r--r--src/content/dependencies/generateBanner.js7
-rw-r--r--src/content/dependencies/generateColorStyleAttribute.js1
-rw-r--r--src/content/dependencies/generateColorStyleVariables.js11
-rw-r--r--src/content/dependencies/generateTrackInfoPage.js10
-rw-r--r--src/static/css/site.css30
7 files changed, 77 insertions, 6 deletions
diff --git a/src/common-util/colors.js b/src/common-util/colors.js
index 7298c46a..50e51cd9 100644
--- a/src/common-util/colors.js
+++ b/src/common-util/colors.js
@@ -14,6 +14,7 @@ export function getColors(themeColor, {
   const dim = primary.desaturate(2).darken(1.5);
   const deep = primary.saturate(1.2).luminance(0.035);
   const deepGhost = deep.alpha(0.8);
+  const abyss = primary.saturate(0.6).luminance(0.012);
   const light = chroma.average(['#ffffff', primary], 'rgb', [4, 1]);
   const lightGhost = primary.luminance(0.8).saturate(4).alpha(0.08);
 
@@ -31,6 +32,7 @@ export function getColors(themeColor, {
     dim: dim.hex(),
     deep: deep.hex(),
     deepGhost: deepGhost.hex(),
+    abyss: abyss.hex(),
     light: light.hex(),
     lightGhost: lightGhost.hex(),
 
diff --git a/src/content/dependencies/generateAlbumBanner.js b/src/content/dependencies/generateAlbumBanner.js
index dce258de..b8faf7e6 100644
--- a/src/content/dependencies/generateAlbumBanner.js
+++ b/src/content/dependencies/generateAlbumBanner.js
@@ -5,7 +5,11 @@ export default {
     }
 
     return {
-      banner: relation('generateBanner'),
+      banner:
+        relation('generateBanner'),
+
+      colorAttribute:
+        relation('generateColorStyleAttribute', album.color),
     };
   },
 
@@ -20,7 +24,14 @@ export default {
     };
   },
 
-  generate(data, relations, {html, language}) {
+  slots: {
+    mode: {
+      validate: v => v.is('main', 'sub'),
+      default: 'main',
+    },
+  },
+
+  generate(data, relations, slots, {html, language}) {
     if (!relations.banner) {
       return html.blank();
     }
@@ -29,6 +40,13 @@ export default {
       path: data.path,
       dimensions: data.dimensions,
       alt: language.$('misc.alt.albumBanner'),
+
+      attributes: [
+        slots.mode === 'sub' && [
+          {class: ['dim', 'short']},
+          relations.colorAttribute.slot('context', 'banner'),
+        ],
+      ],
     });
   },
 };
diff --git a/src/content/dependencies/generateBanner.js b/src/content/dependencies/generateBanner.js
index 509b15c2..b6214cc0 100644
--- a/src/content/dependencies/generateBanner.js
+++ b/src/content/dependencies/generateBanner.js
@@ -11,10 +11,17 @@ export default {
     alt: {
       type: 'string',
     },
+
+    attributes: {
+      type: 'attributes',
+      mutable: false,
+    },
   },
 
   generate: (slots, {html, to}) =>
     html.tag('div', {id: 'banner'},
+      slots.attributes,
+
       html.tag('img',
         {src: to(...slots.path)},
 
diff --git a/src/content/dependencies/generateColorStyleAttribute.js b/src/content/dependencies/generateColorStyleAttribute.js
index 277ec434..10dd7810 100644
--- a/src/content/dependencies/generateColorStyleAttribute.js
+++ b/src/content/dependencies/generateColorStyleAttribute.js
@@ -18,6 +18,7 @@ export default {
       validate: v => v.is(
         'any-content',
         'image-box',
+        'banner',
         'primary-only'),
 
       default: 'any-content',
diff --git a/src/content/dependencies/generateColorStyleVariables.js b/src/content/dependencies/generateColorStyleVariables.js
index 0865ed3e..843fa94c 100644
--- a/src/content/dependencies/generateColorStyleVariables.js
+++ b/src/content/dependencies/generateColorStyleVariables.js
@@ -10,6 +10,7 @@ export default {
         'image-box',
         'page-root',
         'image-box',
+        'banner',
         'primary-only'),
 
       default: 'any-content',
@@ -30,6 +31,7 @@ export default {
       dim,
       deep,
       deepGhost,
+      abyss,
       lightGhost,
       bg,
       bgBlack,
@@ -42,13 +44,14 @@ export default {
       `--dim-color: ${dim}`,
       `--deep-color: ${deep}`,
       `--deep-ghost-color: ${deepGhost}`,
+      `--abyss-color: ${abyss}`,
       `--light-ghost-color: ${lightGhost}`,
       `--bg-color: ${bg}`,
       `--bg-black-color: ${bgBlack}`,
       `--shadow-color: ${shadow}`,
     ];
 
-    let selectedDeclarations;
+    let selectedDeclarations = [];
 
     switch (slots.context) {
       case 'any-content':
@@ -76,6 +79,12 @@ export default {
           `--primary-color: ${primary}`,
         ];
         break;
+
+      case 'banner':
+        selectedDeclarations = [
+          `--abyss-color: ${abyss}`,
+        ];
+        break;
     }
 
     switch (slots.mode) {
diff --git a/src/content/dependencies/generateTrackInfoPage.js b/src/content/dependencies/generateTrackInfoPage.js
index 77adff02..6280b07e 100644
--- a/src/content/dependencies/generateTrackInfoPage.js
+++ b/src/content/dependencies/generateTrackInfoPage.js
@@ -68,6 +68,11 @@ export default {
         ? relation('generateSingleArtworkColumn', track)
         : relation('generateTrackArtworkColumn', track)),
 
+    banner:
+      (track.album.hasBannerArt
+        ? relation('generateAlbumBanner', track.album)
+        : null),
+
     contentHeading:
       relation('generateContentHeading'),
 
@@ -425,6 +430,11 @@ export default {
                 showExtraLinks: false,
               })),
 
+        banner:
+          relations.banner
+            ?.slot('mode', data.firstTrackInSingle ? 'main' : 'sub') ??
+          null,
+
         secondaryNav:
           relations.secondaryNav
             .slot('mode', data.firstTrackInSingle ? 'album' : 'track'),
diff --git a/src/static/css/site.css b/src/static/css/site.css
index 6b969b8d..299b093b 100644
--- a/src/static/css/site.css
+++ b/src/static/css/site.css
@@ -96,11 +96,15 @@ body::before, .wallpaper-part {
   transition: --banner-shine 0.8s;
 }
 
-#banner:hover {
+#banner:not(.dim, .collapsed):hover {
   --banner-shine: 35%;
   transition-delay: 0.3s;
 }
 
+#banner.dim, #banner.collapsed {
+  --banner-shine: 0%;
+}
+
 #banner::after, #banner::before {
   content: "";
   position: absolute;
@@ -347,7 +351,6 @@ body::before, .wallpaper-part {
 
 #banner {
   background: black;
-  background-color: var(--dim-color);
   border-bottom: 1px solid var(--primary-color);
 }
 
@@ -366,8 +369,29 @@ body::before, .wallpaper-part {
   pointer-events: none;
 }
 
+#banner {
+  background-color: var(--abyss-color);
+}
+
 #banner.dim img {
-  opacity: 0.8;
+  opacity: 0.32;
+}
+
+#banner.short, #banner.short img {
+  /* 1100px -> 72.5px, 900px -> 60px
+   * 12.5px height per 200px width
+   * Viewport maxes at 1124px, so:
+   *   Start from 72.5px
+   *   Get width below maximum:
+   *     (1124px - min(1124px, 100vw))
+   *   Multiply by rate of change:
+   *     (12.5 / 200)
+   */
+  max-height: calc(72.5px - (1124px - min(1124px, 100vw)) * (12.5 / 200));
+}
+
+#banner.short img {
+  object-position: 0 var(--short-banner-alignment, 50%);
 }
 
 #header,