FN-015: remove legacy lane-columns CSS

Remove the retired lane compatibility selector while preserving live Board scrolling and overscroll behavior.

- Remove legacy .lane-columns styling from Lane.css.
- Update mobile Board regression fixtures and solution documentation for current workflow-column paths.
- Add a patch changeset documenting the internal cleanup.

Files changed:
 .changeset/fn-015-removal.md                       |  7 ++++
 ...-board-ios-horizontal-overscroll-containment.md | 32 +++++++++--------
 .../__tests__/board-mobile-column-swipe.test.ts    | 11 +++---
 .../__tests__/board-mobile-overscroll-containment.test.ts | 28 ++++++++-------
 packages/dashboard/app/components/Lane.css         | 41 +++-------------------
 5 files changed, 51 insertions(+), 68 deletions(-)

Fusion-Task-Id: FN-015

Fusion-Task-Lineage: 78128638-a319-4589-8cdd-a6497efe226f

Co-authored-by: Fusion <noreply@runfusion.ai>
This commit is contained in:
Fusion Agent
2026-08-18 19:46:08 +00:00
parent c0966fb3c2
commit 561e0f4cd8
5 changed files with 51 additions and 68 deletions

View File

@@ -0,0 +1,7 @@
---
"@runfusion/fusion": patch
---
summary: Remove retired Board compatibility styling without changing live scrolling behavior.
category: internal
dev: Removes the legacy `.lane-columns` CSS after verifying current selected-workflow and All-workflows Board paths and known/bundled plugin surfaces do not consume it; live desktop containment and phone proximity snapping remain covered by CSS-fixture regression tests.

View File

@@ -22,7 +22,7 @@ tags:
- css-regression-test
- kanban
applies_when:
- "A horizontally scrollable board or lane strip uses `overflow-x: auto` with mobile momentum scrolling"
- "A horizontally scrollable Board surface uses `overflow-x: auto` with mobile momentum scrolling"
- "Edge dragging should keep native inner scrolling but must not chain or park content off screen"
---
@@ -30,35 +30,39 @@ applies_when:
## Problem
The mobile kanban board intentionally scrolls horizontally between columns using `overflow-x: auto`, `-webkit-overflow-scrolling: touch`, and `scroll-snap-type: x proximity`. On iOS Safari/PWA, that same momentum scroller can rubber-band past its first or last column if the scroller does not contain horizontal overscroll. The visible result is that the columns slide away from the viewport edge, exposing empty space and sometimes chaining the drag to the document.
The mobile kanban Board intentionally scrolls horizontally between columns using `overflow-x: auto`, `-webkit-overflow-scrolling: touch`, and `scroll-snap-type: x proximity`. On iOS Safari/PWA, that same momentum scroller can rubber-band past its first or last column if the scroller does not contain horizontal overscroll. The visible result is that the columns slide away from the viewport edge, exposing empty space and sometimes chaining the drag to the document.
## Root cause
The board had page-level mobile overscroll protection on `html, body`, but the board itself is the horizontal scroll container. The base `.board` and the mobile `@media (max-width: 768px) .board` rules declared the intended scroll and snap properties without `overscroll-behavior-x`, so iOS edge overscroll was not contained at the board boundary. Workflow and multi-lane board variants in `Lane.css` had the same independent horizontal scrollers.
The board had page-level mobile overscroll protection on `html, body`, but the Board itself is the horizontal scroll container. The base `.board` and the live workflow-column `.board.board-workflow-columns` rules declare the intended scroll and snap properties with `overscroll-behavior-x: contain`, so iOS edge overscroll stops at the Board boundary. Both selected-workflow and All-workflows render paths use the workflow-column selector.
## Solution
Add axis-specific containment to each horizontal board strip:
Keep axis-specific containment on the current horizontal Board surfaces:
```css
.board,
.board.board-workflow-columns,
.lane-columns {
.board.board-workflow-columns {
overflow-x: auto;
overscroll-behavior-x: contain;
scroll-snap-type: x proximity;
}
@media (max-width: 768px), (max-height: 480px) {
.board.board-workflow-columns {
scroll-snap-type: x proximity;
}
}
```
Keep `contain` rather than `none`: the board can retain its native inner scroll feel while edge overscroll stops at the board/lane container instead of chaining outward. Do not replace this with `overflow: hidden`/`clip`, and do not switch snap back to `x mandatory`; both would regress intentional mobile column navigation.
Keep `contain` rather than `none`: the Board retains its native inner scroll feel while edge overscroll stops at the Board container instead of chaining outward. Desktop and tablet workflow columns explicitly use `scroll-snap-type: none`; phone and short-landscape workflow columns use proximity snapping. Do not replace this with `overflow: hidden`/`clip`, and do not switch snap to `x mandatory`; both would regress intentional mobile column navigation.
## Regression coverage
Use a CSS-fixture test that loads the combined dashboard CSS and asserts:
Use CSS-fixture tests that load the combined dashboard CSS and assert:
- the mobile `.board` rule still has `overflow-x: auto` and `scroll-snap-type: x proximity`;
- the mobile `.board` rule declares `overscroll-behavior-x: contain`;
- the base `.board`, `.board.board-workflow-columns`, and `.lane-columns` horizontal scrollers also declare containment;
- no checked board path uses `scroll-snap-type: x mandatory`.
- the base `.board` and `.board.board-workflow-columns` rules have `overflow-x: auto`, `overscroll-behavior-x: contain`, and `scroll-snap-type: none`;
- the phone and short-landscape `.board` and `.board.board-workflow-columns` rules retain `scroll-snap-type: x proximity`;
- neither Board workflow render path permits `scroll-snap-type: x mandatory`;
- the retired lane compatibility selector is absent from both the base and phone CSS slices.
For FN-6378 this lives in `packages/dashboard/app/__tests__/board-mobile-overscroll-containment.test.ts`.
For FN-6378 this lives in `packages/dashboard/app/__tests__/board-mobile-overscroll-containment.test.ts` and the companion column-swipe fixture.

View File

@@ -88,12 +88,13 @@ describe("mobile board column swipe target containment (FN-6389)", () => {
expect(columnBodyBlock).not.toContain("overflow-y: hidden");
});
it("opts workflow and multi-lane board interiors into horizontal panning", () => {
it("opts live workflow Board columns into horizontal panning", () => {
expect(baseCss).not.toMatch(/\.lane-columns\b/);
expect(mobileCss).not.toMatch(/\.lane-columns\b/);
for (const selector of [
".board.board-workflow-columns",
".board.board-workflow-columns > .column",
".lane-columns",
".lane-columns > .column",
]) {
expectTouchPanXY(baseCss, selector);
}
@@ -120,10 +121,10 @@ describe("mobile board column swipe target containment (FN-6389)", () => {
expect(widthContainmentBlock).toContain("max-inline-size: 100%;");
});
it("preserves FN-6378 horizontal overscroll containment, with proximity snap on phones and none on desktop", () => {
it("preserves live Board containment, phone proximity snap, and desktop free-panning", () => {
expectContainmentScroller(ruleBlock(baseCss, ".board"), "none");
expectContainmentScroller(ruleBlock(mobileCss, ".board"), "proximity");
expectContainmentScroller(ruleBlock(baseCss, ".board.board-workflow-columns"), "none");
expectContainmentScroller(ruleBlock(baseCss, ".lane-columns"), "none");
expectContainmentScroller(ruleBlock(mobileCss, ".board.board-workflow-columns"), "proximity");
});
});

View File

@@ -57,22 +57,24 @@ describe("board-mobile-overscroll-containment (FN-6378)", () => {
expect(boardBlock).not.toContain("scroll-snap-type: x");
});
it("workflow columns and multi-lane column strips contain horizontal overscroll and do not snap on desktop", () => {
it("live workflow Board columns contain horizontal overscroll and do not snap on desktop", () => {
const workflowColumnsBlock = extractRuleBlock(baseCss, ".board.board-workflow-columns");
const laneColumnsBlock = extractRuleBlock(baseCss, ".lane-columns");
for (const block of [workflowColumnsBlock, laneColumnsBlock]) {
expect(block).toContain("overflow-x: auto");
expect(block).toContain("overscroll-behavior-x: contain");
expect(block).toContain("scroll-snap-type: none");
expect(block).not.toContain("scroll-snap-type: x");
}
expect(workflowColumnsBlock).toContain("overflow-x: auto");
expect(workflowColumnsBlock).toContain("overscroll-behavior-x: contain");
expect(workflowColumnsBlock).toContain("scroll-snap-type: none");
expect(workflowColumnsBlock).not.toContain("scroll-snap-type: x");
});
it("phone tier re-enables proximity snapping for workflow columns and lane strips", () => {
// Both live in Lane.css's mobile block; assert against the mobile slice of all app CSS.
expect(mobileCss).toContain("scroll-snap-type: x proximity");
expect(mobileCss).not.toContain("scroll-snap-type: x mandatory");
expect(mobileCss).toMatch(/\.board\.board-workflow-columns,\s*\n?\s*\.lane-columns\s*\{[^}]*scroll-snap-type: x proximity/);
it("does not ship the retired lane selector in base or phone CSS", () => {
expect(baseCss).not.toMatch(/\.lane-columns\b/);
expect(mobileCss).not.toMatch(/\.lane-columns\b/);
});
it("phone tier re-enables proximity snapping for the live workflow Board", () => {
const workflowColumnsBlock = extractRuleBlock(mobileCss, ".board.board-workflow-columns");
expect(workflowColumnsBlock).toContain("scroll-snap-type: x proximity");
expect(workflowColumnsBlock).not.toContain("scroll-snap-type: x mandatory");
});
});

View File

@@ -123,36 +123,6 @@
text-align: center;
}
.lane-columns {
display: flex;
gap: var(--column-gap, 12px);
padding: 12px;
overflow-x: auto;
overflow-y: hidden;
overscroll-behavior-x: contain;
/* FNXC:BoardNavigation 2026-07-24-10:05: lane columns snap on phones only (see mobile block). */
scroll-snap-type: none;
scrollbar-color: var(--border) transparent;
scrollbar-width: thin;
min-height: 0;
touch-action: pan-x pan-y;
}
.lane-columns > .column {
flex: 0 0 clamp(280px, 28vw, 340px);
/* Repo convention (mobile-scroll-snap test): snap-align must be `center`. */
scroll-snap-align: center;
touch-action: pan-x pan-y;
}
.lane-columns::-webkit-scrollbar {
height: 6px;
}
.lane-columns::-webkit-scrollbar-thumb {
background: var(--border);
border-radius: var(--radius-sm, 6px);
}
/* Inline column feedback (capacity-exhausted promote, deterministic drag
* rejection) — rendered in-column, not a toast (R17 / promote spec). */
.column-inline-feedback {
@@ -195,14 +165,13 @@
}
/*
FNXC:BoardNavigation 2026-07-24-10:05:
Phone tier keeps proximity snapping (paired with the JS column pager in useColumnScrollSnap) so a
swipe still rests on exactly one column center. Mirrors the mobile `.board` block in styles.css and
matches the mobile viewport query (max-width: 768px, max-height: 480px).
FNXC:BoardNavigation 2026-08-18-19:29:
The selected-workflow and All-workflows Board paths share this live workflow-column scroller.
Keep desktop free-panning and phone proximity snapping here; retired lane compatibility styling is
not part of the current Board or plugin surface.
*/
@media (max-width: 768px), (max-height: 480px) {
.board.board-workflow-columns,
.lane-columns {
.board.board-workflow-columns {
scroll-snap-type: x proximity;
}
}