diff --git a/docs/superpowers/specs/2026-05-08-l4d2-blueprint-overlay-picker-design.md b/docs/superpowers/specs/2026-05-08-l4d2-blueprint-overlay-picker-design.md new file mode 100644 index 0000000..02db508 --- /dev/null +++ b/docs/superpowers/specs/2026-05-08-l4d2-blueprint-overlay-picker-design.md @@ -0,0 +1,118 @@ +# L4D2 Blueprint Overlay Picker Design + +**Goal:** Replace the checkbox + numeric-Order table on the blueprint detail page with a drag-to-reorder list and a single dropdown to add overlays. Drag-and-drop is the primary reorder mechanic; per-row Order text inputs are removed. + +**Approval status:** User-approved. No companion implementation plan — small surface, implemented directly. + +## Context + +`templates/blueprint_detail.html:14-28` currently renders one HTML table for the blueprint's overlays. Each row carries a `Use` checkbox, a numeric `Order` text input, and the overlay name. To enable an overlay you check it; to reorder you type integers into per-row text fields. Adding a new overlay between existing ones means renumbering everything below it by hand. + +This spec replaces that table with a single ordered list of *selected* overlays plus a `` element supplies typeahead-by-letter and keyboard navigation for free, which covers the no-drag path. The page is desktop-primary. + +## Locked Decisions + +1. **Single ordered list of selected overlays only.** No second pane. The "available" set lives in the `` and the ` + + +``` + +Browser form serialization preserves DOM order across multiple inputs that share a `name`. Werkzeug's `request.form.getlist("overlay_ids")` returns them in submission order. `ordered_overlay_ids_from_form` then assigns each id its enumerate-index position via the `fallback_position` branch (lines 19-31 of `routes/blueprint_routes.py`) and feeds the result to `replace_blueprint_overlays`. + +The JSON path (`POST /blueprints` with `application/json`) already takes `overlay_ids` list order at line 64 of the same file — this spec does not affect it. + +## UI / UX details + +- **Empty state.** When no overlays are selected, a `[data-overlay-empty]` paragraph reads "No overlays selected. Pick one below to add." JS toggles its `hidden` attribute on every list mutation. +- **Drag handle.** Visual only (`⋮⋮` glyph). The whole row is `draggable="true"`; the user does not have to grab the handle specifically. +- **Drop indicator math.** During `dragover`, compute `event.clientY < rect.top + rect.height/2`; that boolean picks `drop-before` (bar at top) vs `drop-after` (bar at bottom). On `drop`, read which class is set and `insertBefore` or `insertBefore(…, target.nextSibling)` accordingly. +- **Sorted insert on remove.** Walk ``; add `