feat(mobile): snap-scroll kanban board with viewport-sized columns

The pipelines board was already horizontally scrollable, but on a
phone it felt imprecise — swipes landed mid-column, there was no
visual hint that more columns existed past the viewport, and 260px
columns felt cramped at 375px width.

- Each StageColumn is now w-[85vw] min/max-clamped on mobile, so the
  next column's edge peeks in as a "more here" affordance. On lg+
  the columns revert to flex-1 basis-[260px] (share the row).
- Scroll container gets snap-x + snap-mandatory on mobile, snap-none
  on lg+. Each column has snap-start so swipes always land cleanly
  at the left edge of the next column.
- shrink-0 on mobile prevents flex from compressing the columns when
  space is tight.

No desktop visual change — all the mobile-specific classes are
overridden back by their lg: counterparts, and the original
flex-1 basis-[260px] behavior is preserved above lg.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Arnas Donauskas 2026-04-19 11:53:10 +03:00
parent f223d5b86f
commit 4d901e16bd
No known key found for this signature in database

View File

@ -102,7 +102,11 @@ export function PipelineBoard({
onDragEnd={handleDragEnd}
onDragCancel={handleDragCancel}
>
<div className="pipeline-scroll flex gap-3 overflow-x-auto pb-4">
{/* snap-x + snap-mandatory on mobile so swipes land the next
stage cleanly at the viewport edge instead of mid-column.
Disabled on lg+ because the full board fits without scroll
there and snapping would interfere with the natural layout. */}
<div className="pipeline-scroll flex snap-x snap-mandatory gap-3 overflow-x-auto pb-4 lg:snap-none">
{sortedStages.map((stage) => {
const stageDeals = dealsByStage.get(stage.id) ?? [];
const totalValue = stageDeals.reduce(
@ -176,7 +180,13 @@ function StageColumn({
const { setNodeRef, isOver } = useDroppable({ id: stage.id });
return (
<div className="flex min-w-[260px] flex-1 basis-[260px] flex-col rounded-xl border border-slate-800 bg-slate-900/60 p-4">
// On mobile each column is `w-[85vw]` (with a reasonable min/max)
// so the next column's edge peeks in — a "there's more here" hint.
// snap-start lands each column cleanly when swiping. On lg+ we
// restore the flex-1 share-the-row behavior. The droppable ref is
// on the inner messages region below — intentionally NOT here, so
// a drag over the column header doesn't highlight the whole column.
<div className="flex w-[85vw] min-w-[260px] max-w-[320px] shrink-0 snap-start flex-col rounded-xl border border-slate-800 bg-slate-900/60 p-4 lg:w-auto lg:max-w-none lg:flex-1 lg:basis-[260px] lg:shrink lg:snap-none">
{/* 3px colored top border — sits above the column's padding */}
<div
className="-mx-4 -mt-4 h-[3px] rounded-t-xl"