Signature
An animated SVG signature effect that draws out text as if hand-written.
Live preview recorded from Bakai Lab; experiment and demo by Bakai Tolondu uulu. Preview: platform recorded · rights authorized.
A customer pipeline board that changes between kanban and list views with animated layout transitions.
LSource LandLab - Bakai Tolondu uuluWhy it stands out
A customer pipeline board that changes between kanban and list views with animated layout transitions.
Build the list layout of a lead pipeline: five stages (To call, Called, Trial booked, Signed, Dropped) holding about twelve leads, STACKED vertically as full-width sections rather than side-by-side columns, where a lead is moved between stages BY HAND with a drag. It should be the same component as the kanban with a different view prop, not a second component. Make every length a container-query unit derived from one design frame width (1448), through a helper q(px) that returns (px / 1448 * 100) + 'cqw', and set container-type: inline-size on the component's root, so the whole thing scales to whatever width it is given instead of breaking its proportions. Every number below is a design pixel fed through that helper, so build it at the full 1448 frame. Give each stage its OWN panel, one per stage, and do not build a single table with the stages as tinted group rows inside it. A single table needs a title for the whole thing, which the page's heading already says, and it demotes each stage to a seam in a list. The stage is the heading a reader is actually looking for, so it gets the panel and the table belongs to it. Each panel is a light tray (#FAFAFB, 14px radius, 6px padding) with a 32px header band carrying the stage icon, the stage name and a live count that rolls when it changes, and a white rounded content card under it holding the rows. Give the rows no column headings. A name, a phone number and a note are each unmistakably themselves, and labelling them costs a 34px band per stage, five times down the page, to state what the content already states. Being full width is not cosmetic: it buys real columns, so the note stops being the thing that gets cut for space, which is the one honest loss in a narrow card layout. Lay a row out as a four-column grid at minmax(0, 1.6fr) minmax(0, 1.4fr) minmax(0, 2fr) 60px with a 12 gap, 16 side padding, and every cell set to min-width 0 with ellipsis so any of them can truncate rather than push the row wide. That mirrors the console's own "Member, Activity, Branch, Time": an identity column taking the most room, two content columns, and a fixed metadata column pinned right. Name 13px / 17 line-height / weight 500 / #171717 — 13 rather than the card's 14, because this is the table's value. Then the phone or booked time at 12.5px with tabular figures and a 12px marker icon, the note at 12.5px weight 400 in a lighter grey, and the age right-aligned in the last column. Rows are 44 tall, separated by hairlines rather than by gaps, with a hover fill. 44 specifically: it went 46 to 54 for comfort and back down so that all five stages and all twelve leads FIT without scrolling, which matters more, because a list whose whole point is "see everything in order" that hides its last stage below the fold has argued against itself. Twelve rows dominate the height budget, so if it ever needs retuning, trim rows first, the 10px gap between panels second, and leave the header alone since it is the thing being read. The drag is the point, so build it this way. The card you are holding is an OVERLAY: it renders once, absolutely positioned over the board, and is NOT the row itself. Do not make the row draggable. An element that carries both a drag transform and a layout animation fights itself, because both write the same matrix; and moving a lead between stages changes its DOM parent, which a dragged element cannot animate through, it snaps. So on pointer down, hide the source row from its stage's array, render the overlay at the row's measured rect, and open a HOLE in whichever stage it would land in. Exactly one hole exists at a time, and it is always the prediction, never a depression left behind in the source stage: the stage you left simply closes up, its remaining rows carrying a layout animation so they slide together. On release, animate the overlay to the hole's measured rect and swap the two in the same frame. What changes against a kanban is the AXIS, and it should be one code path that chooses, not two implementations. Because the stages are stacked, BOTH the stage and the slot come from y: one number answers "which stage" and "how far into it". That is safe because the stage is resolved from the snapshot's GROUP rects while the slot is resolved from the ROW rects inside the stage it just chose. Resolve the stage by EDGE distance rather than centre distance so a tall section cannot out-pull a short one, and bound it: past about 80px left or right of the board the answer is nowhere and the row returns home. The overlay's screen position subtracts the scroll offset from y where a kanban subtracts it from x, and the edge auto-scroll drives scrollTop where a kanban drives scrollLeft, within about 90px of the leading or trailing edge, up to 22px per frame scaled by depth into that zone. Measure the drop target from rects captured ONCE at pickup, never re-measured during the drag. The rows move while you drag, that is the gap opening, so re-measuring against moving targets is a feedback loop and it is the reason hand-rolled boards jitter. Compute the index against the layout as it stood at pickup, then correct by one for the row that left its own stage. A press only becomes a drag after about 4px of travel, so a click stays a click. Give the lifted row weight from the gesture rather than from keyframes: scale it to about 1.04 and tilt it up to 7 degrees by reading the VELOCITY of the drag, mapped from plus or minus 1400px/s and clamped, then smoothed through a spring (stiffness 300, damping 30, mass 0.6). Read that velocity from the row's SCREEN position rather than its position in board content, or it leans at rest while the board auto-scrolls under a still hand. Multiply it by a gain that is animated to 0 over the first 55% of the landing so the row levels off as it falls. Ring it in the destination stage's tone as it hovers, carried on opacity so it can fade without relayout. Animate the slots with ONE SCALAR, and do not let each slot own its own animation. This bites hardest in a list, where dragging down crosses a row boundary roughly every 57ms while a 240ms transition is still running, so three or four are always in flight; a slot reversed half-open still takes its full duration to close while the one opening has the whole distance to cover in the same time, the heights stop summing to one row, and everything below the drag shimmers. Interpolate the WHOLE arrangement of slot paddings from the one on screen to the one the new target wants with a single 0-to-1 value over 240ms on cubic-bezier(0.23, 1, 0.32, 1). Both arrangements hold exactly one row's worth of height, so the sum is conserved by construction, and reordering inside a panel then moves nothing below that panel at all. Re-read what is on screen as the starting arrangement on every change so an interrupted handover continues from the pixels. Land the row with a TWEEN, not a spring. A pixel-valued spring ends as soon as it is within half a pixel of the target and jumps the remainder, which measures as closing frames of 0.30, 0.26, 0.18 and then 0.59px: a threefold jump in speed on the one frame the gesture resolves on. Use min(0.32, 0.18 + distance / 2400) seconds, so a nudge lands in about 190ms and a long throw in 320, and min(0.4, 0.26 + distance / 2400) for a lead dropped into the lost stage. Ease it on cubic-bezier(0.25, 0.7, 0.2, 1), which covers 69% of the distance in the first quarter and never decelerates by more than about 1.7x between frames. Schedule every other property as a fraction of that duration and make each finish before the landing does, because commit swaps the overlay for a real card and anything still moving on that frame is a visible step. Aim the landing at where the slot WILL be. A slot's own top does not move while it opens, but a slot ABOVE it that is still closing does, and in a stacked list that means every panel above the target, not just the rows in its own stage: measured, the clone landed 13.75px above the true resting place until those panels were counted. Sum the padding still held by every slot wrapper that sits above the target and overlaps it horizontally, decided by geometry rather than by walking the tree, and subtract it from the target top. Scope the lookup for the landing slot to the TARGET stage's own list rather than the whole board, or a slot just left behind, still in the DOM playing its exit, is returned instead and the row flies to a slot that is disappearing and then jumps back. Use Inter, keep motion under about 300ms, use ease-out-quart cubic-bezier(0.19, 1, 0.22, 1) for anything entering or leaving and ease-in-out-quart cubic-bezier(0.77, 0, 0.175, 1) for anything morphing in place, and honour prefers-reduced-motion by dropping the tilt, jumping the slot arrangement straight to its target, and committing the drop with no landing at all.
Discovery vocabulary
Related by governed terms
An animated SVG signature effect that draws out text as if hand-written.
More from Lab - Bakai Tolondu uulu