Draggable Widget

Widget that can be repositioned.

Pipeline

html

<section class="dragwidget dragwidget--idle" style="--c:#D9C8F0;" aria-label="Pipeline widget, draggable">
  <span class="dragwidget__handle" aria-hidden="true">⠿</span>
  <h3 class="dragwidget__title">Pipeline</h3>
  <span class="dragwidget__state"></span>
</section>
<style>
.dragwidget { display: flex; align-items: center; gap: 8px; border: 1.5px solid #24143018; border-radius: 10px; padding: 12px; background: #FAF7F2; font: 13px system-ui, sans-serif; color: #241430; max-width: 260px; }
.dragwidget__handle { cursor: grab; color: #24143066; font-size: 16px; }
.dragwidget__title { margin: 0; font-size: 14px; flex: 1; }
.dragwidget__state { font-size: 11px; opacity: .6; }
.dragwidget--dragging { border-style: dashed; border-color: var(--c); box-shadow: 0 10px 20px rgba(36,20,48,.18); opacity: .85; transform: rotate(-1deg); }
.dragwidget--drop-target { border-color: var(--c); background: color-mix(in srgb, var(--c) 20%, #FAF7F2); }
.dragwidget--idle .dragwidget__state::before { content: "Idle"; }
.dragwidget--dragging .dragwidget__state::before { content: "Dragging"; }
.dragwidget--drop-target .dragwidget__state::before { content: "Drop target"; }
</style>

tailwind

<section class="dragwidget-tw dragwidget-tw--idle flex items-center gap-2 border-[1.5px] border-[#24143018] rounded-[10px] p-3 bg-[#FAF7F2] font-sans text-[13px] text-[#241430] max-w-xs" style="--c:#D9C8F0;" aria-label="Pipeline widget, draggable">
  <span aria-hidden="true" class="cursor-grab text-[#24143066] text-base">⠿</span>
  <h3 class="m-0 text-sm flex-1">Pipeline</h3>
  <span class="dragwidget-tw__state text-[11px] opacity-60"></span>
</section>
<style>
.dragwidget-tw--dragging { border-style: dashed; border-color: var(--c); box-shadow: 0 10px 20px rgba(36,20,48,.18); opacity: .85; transform: rotate(-1deg); }
.dragwidget-tw--drop-target { border-color: var(--c); background: color-mix(in srgb, var(--c) 20%, #FAF7F2); }
.dragwidget-tw--idle .dragwidget-tw__state::before { content: "Idle"; }
.dragwidget-tw--dragging .dragwidget-tw__state::before { content: "Dragging"; }
.dragwidget-tw--drop-target .dragwidget-tw__state::before { content: "Drop target"; }
</style>

react

export function DraggableWidget({
  title = "Pipeline",
  state = "idle",
  showHandle = true,
  color = "#D9C8F0",
}) {
  const stateText = { idle: "Idle", dragging: "Dragging", "drop-target": "Drop target" }[state] ?? "Idle";
  const wrap = {
    idle: { border: "1.5px solid #24143018" },
    dragging: { border: `1.5px dashed ${color}`, boxShadow: "0 10px 20px rgba(36,20,48,.18)", opacity: 0.85, transform: "rotate(-1deg)" },
    "drop-target": { border: `1.5px solid ${color}`, background: `${color}33` },
  }[state] ?? {};
  return (
    <section aria-label={`${title} widget, draggable`} style={{ display: "flex", alignItems: "center", gap: 8, borderRadius: 10, padding: 12, background: "#FAF7F2", font: "13px system-ui, sans-serif", color: "#241430", maxWidth: 260, ...wrap }}>
      {showHandle && <span aria-hidden="true" style={{ cursor: "grab", color: "#24143066", fontSize: 16 }}>⠿</span>}
      <h3 style={{ margin: 0, fontSize: 14, flex: 1 }}>{title}</h3>
      <span style={{ fontSize: 11, opacity: 0.6 }}>{stateText}</span>
    </section>
  );
}