From 46407b49a108078c85f575753788fdc900a200d9 Mon Sep 17 00:00:00 2001 From: 4gray Date: Sun, 27 Sep 2026 11:46:50 +0200 Subject: [PATCH] fix(dashboard): extend rail edge fades to the track clipping edge The rail track bleeds past the viewport (padding plus negative margin) so card focus rings are not clipped, but the edge fades were anchored to the viewport. That left a strip of the partly visible card beyond each fade. Share the bleed through a --rail-bleed custom property used by the track padding/margin, its scroll padding and the fade offsets, so they cannot drift apart. Raise the bleed from 4px to 8px: the card focus ring needs 5px (2px outline + 3px offset) and 7px with the hover lift, so the first/last card's ring was losing a pixel. Co-Authored-By: Claude Opus 5.5 --- .changes/dashboard-rail-edge-fade.md | 6 +++++ docs/architecture/workspace-dashboard.md | 4 ++++ .../lib/rails/dashboard-rail.component.scss | 22 ++++++++++++------- 3 files changed, 24 insertions(+), 8 deletions(-) create mode 100644 .changes/dashboard-rail-edge-fade.md diff --git a/.changes/dashboard-rail-edge-fade.md b/.changes/dashboard-rail-edge-fade.md new file mode 100644 index 000000000..f2cf900ba --- /dev/null +++ b/.changes/dashboard-rail-edge-fade.md @@ -0,0 +1,6 @@ +--- +type: fix +area: dashboard +--- + +Dashboard rails no longer show a thin strip of the partly visible card past the soft edge fade, and the focus outline of the first or last card in a rail is no longer cut off. diff --git a/docs/architecture/workspace-dashboard.md b/docs/architecture/workspace-dashboard.md index 352f8cb79..9ee6a4444 100644 --- a/docs/architecture/workspace-dashboard.md +++ b/docs/architecture/workspace-dashboard.md @@ -143,6 +143,10 @@ hooks: `dashboard-hero`, `dashboard-hero-slide` (`data-hero-kind`), optional `aspectRatio` (default `'2 / 3'`), optional `testId`. 2. Behavior: horizontal flex track with `scroll-snap-type: x mandatory`. 3. Chevron buttons fade in on hover (desktop only via `@media (hover: none)`). + Edge fades follow the chevrons' visibility. The track bleeds + `--rail-bleed` past the viewport on every side so card focus rings and + hover lift are not clipped; the fades are offset by the same variable so + they reach the track's clipping edge and no card strip shows beyond them. 4. Cards are keyboard-focusable router links; `scroll-snap-align: start` means arrow-key nav lands on card boundaries. 5. Image handling: `loading="lazy"`, `decoding="async"`, fallback icon tile diff --git a/libs/workspace/dashboard/feature/src/lib/rails/dashboard-rail.component.scss b/libs/workspace/dashboard/feature/src/lib/rails/dashboard-rail.component.scss index 4717177d3..485e34ced 100644 --- a/libs/workspace/dashboard/feature/src/lib/rails/dashboard-rail.component.scss +++ b/libs/workspace/dashboard/feature/src/lib/rails/dashboard-rail.component.scss @@ -3,6 +3,11 @@ --rail-card-width: var(--cover-rail-width, 172px); --rail-gap: var(--cover-gap, 14px); --rail-radius: 12px; + // How far the scrolling track bleeds past the viewport on every side: + // room for a card's focus ring (2px outline + 3px offset) plus its 2px + // hover lift, so neither is clipped. The track clips partially visible + // cards at this outer edge, so the edge fades extend by the same amount. + --rail-bleed: 8px; } .rail { @@ -81,13 +86,14 @@ // Soft edge gradients hint at horizontal scroll without any interaction. // Anchored to the viewport so they appear/disappear in sync with the - // chevrons' `.rail__chev--visible` state. + // chevrons' `.rail__chev--visible` state, but offset by the track bleed + // so each fade ends exactly where the track clips the partial card. &::before, &::after { content: ''; position: absolute; - top: 0; - bottom: 0; + top: calc(-1 * var(--rail-bleed)); + bottom: calc(-1 * var(--rail-bleed)); width: 32px; pointer-events: none; z-index: 1; @@ -96,7 +102,7 @@ } &::before { - left: 0; + left: calc(-1 * var(--rail-bleed)); background: linear-gradient( to right, var(--app-content-bg, #161a22) 0%, @@ -105,7 +111,7 @@ } &::after { - right: 0; + right: calc(-1 * var(--rail-bleed)); background: linear-gradient( to left, var(--app-content-bg, #161a22) 0%, @@ -135,10 +141,10 @@ overflow-x: auto; overflow-y: hidden; scroll-snap-type: x mandatory; - scroll-padding-inline-start: 4px; + scroll-padding-inline-start: var(--rail-bleed); scrollbar-width: none; - padding: 4px; - margin: -4px; + padding: var(--rail-bleed); + margin: calc(-1 * var(--rail-bleed)); &::-webkit-scrollbar { display: none;