From 4f2764e0a54130d8bd5755bf3e3e6da2d4d72084 Mon Sep 17 00:00:00 2001 From: powerfullz <61956140+powerfullz@users.noreply.github.com> Date: Sun, 26 Apr 2026 19:12:49 +0800 Subject: [PATCH] fix: related content overflow and horizontal scroll issues (#1305) * fix: resolve related content card overflow and page horizontal scroll * fix: prevent related content overflow on mobile while preserving shadow space * fix: align related content cards and prevent shadow clipping Expands the left margin of the related content carousel to match the main article, preserving the full card shadow. Adds a visual mask gradient to gracefully fade out the right edge. * feat: add mask image also to left side of subsection list, which is activated when scrolling * feat: fade out right mask image near the end of horizontal scroll --------- Co-authored-by: Jimmy Cai --- assets/scss/partials/layout/list.scss | 84 +++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 5 deletions(-) diff --git a/assets/scss/partials/layout/list.scss b/assets/scss/partials/layout/list.scss index 06e7b0f..33cfed2 100644 --- a/assets/scss/partials/layout/list.scss +++ b/assets/scss/partials/layout/list.scss @@ -44,24 +44,79 @@ } } +@property --left-edge-fade-size { + syntax: ""; + inherits: false; + initial-value: 0px; +} + +@property --right-edge-fade-size { + syntax: ""; + inherits: false; + initial-value: 64px; +} + .subsection-list { --shadow-overlap-x: calc(var(--shadow-l2-blur) + 4px); --shadow-overlap-y: calc(var(--shadow-l2-blur) + var(--shadow-l2-y) + 4px); + --edge-fade-size: 64px; + --left-edge-fade-size: 0px; + --right-edge-fade-size: var(--edge-fade-size); overflow-x: auto; + /// Limit the horizontal overlap to the container padding to prevent page overflow on mobile. + /// On desktop, it still allows the full shadow to be visible. + --overlap-x: min(var(--shadow-overlap-x), var(--container-padding)); + /// These values are computed from the shadow of the article-list--tile (which uses shadow-l1 and shadow-l2) - /// so that the shadow is not cut off - margin-left: calc(var(--shadow-overlap-x) * -1); - margin-right: calc(var(--shadow-overlap-x) * -1); + /// so that the shadow is not cut off, while staying within the viewport on small screens. + /// Clamp both sides to prevent page-level horizontal overflow. + margin-left: calc(var(--overlap-x) * -1); + margin-right: calc(var(--overlap-x) * -1); margin-bottom: calc(var(--shadow-overlap-y) * -1); + + /// Smooth fade on both horizontal edges to avoid hard cuts where shadows are clamped. + --subsection-mask-image: linear-gradient( + to right, + transparent 0%, + rgb(0 0 0 / 0.06) calc(var(--left-edge-fade-size) * 0.12), + rgb(0 0 0 / 0.2) calc(var(--left-edge-fade-size) * 0.3), + rgb(0 0 0 / 0.45) calc(var(--left-edge-fade-size) * 0.52), + rgb(0 0 0 / 0.72) calc(var(--left-edge-fade-size) * 0.76), + black var(--left-edge-fade-size), + black calc(100% - var(--right-edge-fade-size)), + rgb(0 0 0 / 0.72) calc(100% - (var(--right-edge-fade-size) * 0.76)), + rgb(0 0 0 / 0.45) calc(100% - (var(--right-edge-fade-size) * 0.52)), + rgb(0 0 0 / 0.2) calc(100% - (var(--right-edge-fade-size) * 0.3)), + rgb(0 0 0 / 0.06) calc(100% - (var(--right-edge-fade-size) * 0.12)), + transparent 100% + ); + -webkit-mask-image: var(--subsection-mask-image); + mask-image: var(--subsection-mask-image); + + // CSS-only: left fade ramps in quickly once horizontal scrolling starts, + // and right fade turns off near the end of horizontal scroll. + animation: subsection-left-fade linear both, subsection-right-fade linear both; + animation-timeline: scroll(self inline); + animation-range: 1px 24px, 0% 100%; + animation-timing-function: ease-out, linear; + + [dir="rtl"] & { + margin-left: calc(var(--overlap-x) * -1); + margin-right: calc(var(--overlap-x) * -1); + } .article-list--tile { display: flex; - padding: var(--spacing-xs) var(--shadow-overlap-x) var(--shadow-overlap-y) var(--shadow-overlap-x); + padding: var(--spacing-xs) var(--overlap-x) var(--shadow-overlap-y) var(--overlap-x); width: max-content; gap: var(--spacing-lg); + [dir="rtl"] & { + padding: var(--spacing-xs) var(--overlap-x) var(--shadow-overlap-y) var(--overlap-x); + } + article { flex-shrink: 0; @@ -75,4 +130,23 @@ } } } -} \ No newline at end of file +} + +@keyframes subsection-left-fade { + from { + --left-edge-fade-size: 0px; + } + to { + --left-edge-fade-size: var(--edge-fade-size); + } +} + +@keyframes subsection-right-fade { + 0%, + 92% { + --right-edge-fade-size: var(--edge-fade-size); + } + 100% { + --right-edge-fade-size: 0px; + } +}