The one rule for whether drag-and-drop is initialized anywhere in the UI. `SortableList` consumes the hook itself, and `Board` reads `DRAG_AND_DROP_MEDIA_QUERY` tri-state (via `useMediaQuery`) to pick its drag or touch subtree — so hosts get correct touch behavior without wiring anything. `Board` needs the raw query rather than the hook because the hook's `?? false` erases "not known yet", and the board must render neither subtree until the answer exists (SSR / hydration). ## Key Components | Export | Type | Description | |---|---|---| | `useDragAndDropEnabled` | Hook | `true` only when `(pointer: fine) and (hover: hover) and (min-width: 800px)` matches | | `DRAG_AND_DROP_MEDIA_QUERY` | Const | The query itself, composed from `breakpoints.md` — no new breakpoint | ## Why pointer type AND breakpoint - A breakpoint alone fails on tablets: an iPad in landscape is 1024-1366px wide and would pass any sane `min-width` — exactly the case being excluded, because the HTML5 drag-and-drop API Pragmatic builds on does not fire under touch. - `pointer: coarse` alone fails the other way: a touchscreen laptop with a trackpad reports `pointer: fine` and should keep DnD, but in a narrow window its lists collapse to the mobile layout where a drag handle is out of place. - No `'ontouchstart' in window`, no UA sniffing: the first is true on touch laptops, the second is neither accurate nor reactive. A media query re-evaluates live when a mouse is attached or the window resizes. ## SSR Returns `false` during SSR and the first client render (`useMediaQuery` is still `undefined`): DnD off means no dead drag handle in server HTML, and the layout effect settles the real answer before first paint, so a desktop user never sees the touch fallback flash. ## Source [`use-drag-and-drop-enabled.ts`](https://github.com/flamingo-stack/openframe-oss-lib/blob/main/use-drag-and-drop-enabled.ts)