Core Concepts

Build A Sortable Surface

A SnapSort surface has three parts:

  • An engine that owns input, layout reads, writes, and animations.
  • One or more containers that define where items can be placed.
  • Items inside those containers, each representing one draggable piece of UI.

Each container has a direction and a group. Direction controls the layout flow. Group controls which containers can exchange items.

Container and Item are the only classes/components you need — every container picks its drag behavior with a mode config field instead of a separate class. Item never needs to know the mode; the same Item works under a container of any mode.

Groups

Containers with the same groupID can exchange items. Containers with different groups are isolated from one another.

Use one group for each independent drag-and-drop area. For example, a sentence builder might use one group for all answer and word-bank containers in a single exercise, while a kanban board might use a separate group for each board.

Direction

Set direction to control the main flow:

ValueBehavior
"column"Stack items vertically.
"row"Arrange items horizontally and wrap when the container wraps.

Sections

Drag and Drop Modes

SnapSort treats drag and drop as a set of placement modes, selected per container with config.mode. A mode decides how the dragged item leaves normal layout, how the preview ghost is placed, and which container/index wins when the user drops.

new Container(engine, parent, { mode: "euclidean" | "progressive" | "insertion" });

mode defaults to "euclidean", so it can be omitted for the common case.

Euclidean

Euclidean placement is the default mode (mode: "euclidean", or omit mode entirely). During a drag, SnapSort creates a full-size ghost and evaluates valid insertion positions. The position whose final ghost center is closest to the dragged item wins.

Use Euclidean placement for general reorderable UI:

  • kanban columns
  • vertical lists
  • horizontal button rows
  • nested containers with predictable item sizes

Euclidean placement is simple and direct, and the visible ghost usually matches the result the user expects. In wrapped rows with very different item widths, final layout reflow can make the closest ghost center differ from the insertion boundary the user is aiming at.

Progressive

Progressive placement (mode: "progressive") is designed for ordered, variable-size items. Instead of choosing the closest final ghost center, it treats the layout as a progression of placement regions and chooses the region occupied by the dragged item’s center.

Use Progressive placement when users think in reading order:

  • sentence builders
  • word or token banks
  • wrapped rows with highly variable item widths
  • answer areas where left-to-right, then top-to-bottom order matters

Progressive placement is a better fit when the user’s intent is “insert here in the sequence” rather than “move toward the nearest final box.”

Insertion

Insertion placement (mode: "insertion") replaces the full-size ghost with a thin floating marker line that shows exactly where the item will land, without reflowing the surrounding layout during the drag. The dragged item itself stays in its original DOM position until drop.

Use Insertion placement for line-marker UIs:

  • file/folder trees and outlines
  • nested, collapsible structures
  • any list where a full-size ghost reflowing sibling items would be visually noisy

Choosing a Mode

ModeBest forGhost behavior
"euclidean" (default)General list and board reordering.Full-size flow-layout spacer, FLIP-animated.
"progressive"Ordered token layouts and wrapped sentence builders.Full-size flow-layout spacer, FLIP-animated.
"insertion"File trees, outlines, and other line-marker UIs.Thin floating marker line, absolutely positioned.

Mode is a property of the whole drag tree, resolved from the root container when a drag starts. Nest containers of the same mode; mixing modes within one tree is not supported.