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 <jimmy@cai.im>
This commit is contained in:
powerfullz
2026-04-26 13:12:49 +02:00
committed by GitHub
co-authored by Jimmy Cai
parent 37f6a77a1a
commit 4f2764e0a5
+78 -4
View File
@@ -44,24 +44,79 @@
} }
} }
@property --left-edge-fade-size {
syntax: "<length>";
inherits: false;
initial-value: 0px;
}
@property --right-edge-fade-size {
syntax: "<length>";
inherits: false;
initial-value: 64px;
}
.subsection-list { .subsection-list {
--shadow-overlap-x: calc(var(--shadow-l2-blur) + 4px); --shadow-overlap-x: calc(var(--shadow-l2-blur) + 4px);
--shadow-overlap-y: calc(var(--shadow-l2-blur) + var(--shadow-l2-y) + 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; 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) /// 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 /// so that the shadow is not cut off, while staying within the viewport on small screens.
margin-left: calc(var(--shadow-overlap-x) * -1); /// Clamp both sides to prevent page-level horizontal overflow.
margin-right: calc(var(--shadow-overlap-x) * -1); margin-left: calc(var(--overlap-x) * -1);
margin-right: calc(var(--overlap-x) * -1);
margin-bottom: calc(var(--shadow-overlap-y) * -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 { .article-list--tile {
display: flex; 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; width: max-content;
gap: var(--spacing-lg); gap: var(--spacing-lg);
[dir="rtl"] & {
padding: var(--spacing-xs) var(--overlap-x) var(--shadow-overlap-y) var(--overlap-x);
}
article { article {
flex-shrink: 0; flex-shrink: 0;
@@ -76,3 +131,22 @@
} }
} }
} }
@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;
}
}