Skip to content
← Sortable List
Edit Sortable List
Name
Also known as (comma separated)
Brief definition
List supporting reordering.
Notes
Tier
Core
Useful
Reference
Deleted
Controls schema (JSON array)
[ { "id": "heading", "type": "text", "label": "Heading", "default": "Playlist order" }, { "id": "accent", "type": "color", "label": "Accent colour", "default": "#F5C2E0", "presets": [ "#F5C2E0", "#A8E6C1", "#BFD9F2", "#D9C8F0", "#E8A020", "#241430" ] }, { "id": "style", "type": "select", "label": "Row style", "default": "card", "options": [ "plain", "card", "numbered" ] }, { "id": "showHandle", "type": "toggle", "label": "Show drag handle", "default": true }, { "id": "gap", "type": "number", "label": "Row spacing", "default": 6, "min": 2, "max": 16, "step": 2 } ]
Templates (JSON object keyed by format)
{ "html": "<div class=\"srt srt--{{style}}\" style=\"--accent:{{accent}};--gap:{{gap}}px;\">\n <h3 class=\"srt__heading\">{{heading}}</h3>\n <p class=\"srt__hint\">Use the move buttons to reorder — no dragging required.</p>\n <ol class=\"srt__list\">\n <li class=\"srt__row\">\n {{#if showHandle}}<span class=\"srt__handle\" aria-hidden=\"true\">⠿</span>{{/if}}\n <span class=\"srt__text\">Morning warm-up</span>\n <span class=\"srt__moves\">\n <button type=\"button\" class=\"srt__move\" disabled aria-label=\"Move Morning warm-up up\">↑</button>\n <button type=\"button\" class=\"srt__move\" aria-label=\"Move Morning warm-up down\">↓</button>\n </span>\n </li>\n <li class=\"srt__row\">\n {{#if showHandle}}<span class=\"srt__handle\" aria-hidden=\"true\">⠿</span>{{/if}}\n <span class=\"srt__text\">Focus block</span>\n <span class=\"srt__moves\">\n <button type=\"button\" class=\"srt__move\" aria-label=\"Move Focus block up\">↑</button>\n <button type=\"button\" class=\"srt__move\" aria-label=\"Move Focus block down\">↓</button>\n </span>\n </li>\n <li class=\"srt__row\">\n {{#if showHandle}}<span class=\"srt__handle\" aria-hidden=\"true\">⠿</span>{{/if}}\n <span class=\"srt__text\">Wind-down</span>\n <span class=\"srt__moves\">\n <button type=\"button\" class=\"srt__move\" aria-label=\"Move Wind-down up\">↑</button>\n <button type=\"button\" class=\"srt__move\" disabled aria-label=\"Move Wind-down down\">↓</button>\n </span>\n </li>\n </ol>\n</div>\n<style>\n.srt { font: 14px/1.4 system-ui, sans-serif; color: #241430; background: #FAF7F2; max-width: 320px; width: 320px; padding: 16px; border-radius: 12px; box-sizing: border-box; }\n.srt__heading { margin: 0 0 4px; font-size: 15px; font-weight: 700; }\n.srt__hint { margin: 0 0 10px; font-size: 12px; color: #241430a0; }\n.srt__list { list-style: none; margin: 0; padding: 0; counter-reset: srt; }\n.srt__row { display: flex; align-items: center; gap: 8px; padding: 8px 10px; margin-bottom: var(--gap); border-radius: 8px; }\n.srt--card .srt__row { background: #fff; box-shadow: 0 1px 4px rgba(36,20,48,.08); }\n.srt--numbered .srt__row { counter-increment: srt; }\n.srt--numbered .srt__row::before { content: counter(srt) \".\"; font-weight: 700; color: var(--accent); }\n.srt__handle { color: #24143070; cursor: grab; }\n.srt__text { flex: 1; }\n.srt__moves { display: flex; gap: 2px; }\n.srt__move { width: 24px; height: 24px; border-radius: 6px; border: 1px solid #24143020; background: #fff; cursor: pointer; font-size: 12px; line-height: 1; }\n.srt__move:hover:not(:disabled) { background: var(--accent); }\n.srt__move:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }\n.srt__move:disabled { opacity: .35; cursor: not-allowed; }\n</style>", "tailwind": "<div class=\"srt-tw srt-tw--{{style}} font-sans text-sm text-[#241430] bg-[#FAF7F2] max-w-[320px] w-[320px] p-4 rounded-xl box-border\" style=\"--accent:{{accent}}\">\n <h3 class=\"m-0 mb-1 text-[15px] font-bold\">{{heading}}</h3>\n <p class=\"m-0 mb-2.5 text-xs text-[#241430a0]\">Use the move buttons to reorder — no dragging required.</p>\n <ol class=\"list-none m-0 p-0\">\n <li class=\"srt-tw__row flex items-center gap-2 py-2 px-2.5 rounded-lg\" style=\"margin-bottom:{{gap}}px\">\n {{#if showHandle}}<span aria-hidden=\"true\" class=\"text-[#24143070] cursor-grab\">⠿</span>{{/if}}\n <span class=\"flex-1\">Morning warm-up</span>\n <span class=\"flex gap-0.5\">\n <button type=\"button\" disabled aria-label=\"Move Morning warm-up up\" class=\"srt-tw__move w-6 h-6 rounded-md border border-[#24143020] bg-white cursor-not-allowed text-xs opacity-35\">↑</button>\n <button type=\"button\" aria-label=\"Move Morning warm-up down\" class=\"srt-tw__move w-6 h-6 rounded-md border border-[#24143020] bg-white cursor-pointer text-xs\">↓</button>\n </span>\n </li>\n <li class=\"srt-tw__row flex items-center gap-2 py-2 px-2.5 rounded-lg\" style=\"margin-bottom:{{gap}}px\">\n {{#if showHandle}}<span aria-hidden=\"true\" class=\"text-[#24143070] cursor-grab\">⠿</span>{{/if}}\n <span class=\"flex-1\">Focus block</span>\n <span class=\"flex gap-0.5\">\n <button type=\"button\" aria-label=\"Move Focus block up\" class=\"srt-tw__move w-6 h-6 rounded-md border border-[#24143020] bg-white cursor-pointer text-xs\">↑</button>\n <button type=\"button\" aria-label=\"Move Focus block down\" class=\"srt-tw__move w-6 h-6 rounded-md border border-[#24143020] bg-white cursor-pointer text-xs\">↓</button>\n </span>\n </li>\n <li class=\"srt-tw__row flex items-center gap-2 py-2 px-2.5 rounded-lg\">\n {{#if showHandle}}<span aria-hidden=\"true\" class=\"text-[#24143070] cursor-grab\">⠿</span>{{/if}}\n <span class=\"flex-1\">Wind-down</span>\n <span class=\"flex gap-0.5\">\n <button type=\"button\" aria-label=\"Move Wind-down up\" class=\"srt-tw__move w-6 h-6 rounded-md border border-[#24143020] bg-white cursor-pointer text-xs\">↑</button>\n <button type=\"button\" disabled aria-label=\"Move Wind-down down\" class=\"srt-tw__move w-6 h-6 rounded-md border border-[#24143020] bg-white cursor-not-allowed text-xs opacity-35\">↓</button>\n </span>\n </li>\n </ol>\n</div>\n<style>\n.srt-tw--card .srt-tw__row { background: #fff; box-shadow: 0 1px 4px rgba(36,20,48,.08); }\n.srt-tw--numbered .srt-tw__row { position: relative; padding-left: 24px; }\n.srt-tw__move:hover:not(:disabled) { background: {{accent}}; }\n.srt-tw__move:focus-visible { outline: 2px solid {{accent}}; outline-offset: 1px; }\n</style>", "react": "import { useState } from \"react\";\n\nexport function SortableList({\n heading = \"{{heading}}\",\n accent = \"{{accent}}\",\n style = \"{{style}}\",\n showHandle = {{showHandle}},\n gap = {{gap}},\n}) {\n const [items, setItems] = useState([\"Morning warm-up\", \"Focus block\", \"Wind-down\"]);\n const move = (i, dir) => {\n const j = i + dir;\n if (j < 0 || j >= items.length) return;\n const next = [...items];\n [next[i], next[j]] = [next[j], next[i]];\n setItems(next);\n };\n const rowStyle = {\n display: \"flex\", alignItems: \"center\", gap: 8, padding: \"8px 10px\", marginBottom: gap, borderRadius: 8,\n background: style === \"card\" ? \"#fff\" : \"transparent\",\n boxShadow: style === \"card\" ? \"0 1px 4px rgba(36,20,48,.08)\" : \"none\",\n };\n return (\n <div style={{ fontFamily: \"system-ui, sans-serif\", fontSize: 14, color: \"#241430\", background: \"#FAF7F2\", maxWidth: 320, width: 320, padding: 16, borderRadius: 12, boxSizing: \"border-box\" }}>\n <h3 style={{ margin: \"0 0 4px\", fontSize: 15, fontWeight: 700 }}>{heading}</h3>\n <p style={{ margin: \"0 0 10px\", fontSize: 12, color: \"#241430a0\" }}>Use the move buttons to reorder — no dragging required.</p>\n <ol style={{ listStyle: \"none\", margin: 0, padding: 0 }}>\n {items.map((item, i) => (\n <li key={item} style={rowStyle}>\n {showHandle && <span aria-hidden=\"true\" style={{ color: \"#24143070\", cursor: \"grab\" }}>⠿</span>}\n {style === \"numbered\" && <span style={{ fontWeight: 700, color: accent }}>{i + 1}.</span>}\n <span style={{ flex: 1 }}>{item}</span>\n <span style={{ display: \"flex\", gap: 2 }}>\n <button type=\"button\" disabled={i === 0} aria-label={`Move ${item} up`} onClick={() => move(i, -1)} style={{ width: 24, height: 24, borderRadius: 6, border: \"1px solid #24143020\", background: \"#fff\", cursor: i === 0 ? \"not-allowed\" : \"pointer\", opacity: i === 0 ? 0.35 : 1, fontSize: 12 }}>↑</button>\n <button type=\"button\" disabled={i === items.length - 1} aria-label={`Move ${item} down`} onClick={() => move(i, 1)} style={{ width: 24, height: 24, borderRadius: 6, border: \"1px solid #24143020\", background: \"#fff\", cursor: i === items.length - 1 ? \"not-allowed\" : \"pointer\", opacity: i === items.length - 1 ? 0.35 : 1, fontSize: 12 }}>↓</button>\n </span>\n </li>\n ))}\n </ol>\n </div>\n );\n}" }
Save
History