Reorderable list input component

Adapted by Philip van Heemstra (https://www.vanheemstra.com/) Based on article and demo by Darin Senneff (https://www.darins.page/) https://www.darins.page/articles/designing-a-reorderable-list-component

by PhilQ

HTML

<div id="demo">
  <div class="ranking-input ranking-input-is-lowfi">
    <a href="#ranking-input-skip-end" class="ranking-input-skip-link" id="ranking-input-skip-start">&darr; Jump to list end</a>
    <span class="ranking-input-visually-hidden ranking-input-heading">Fruit ranking</span>
    <ol>
      <li aria-labelledby="ranking-input-marker1 ranking-input-name1" class="ranking-input-item-lowfi">
        <div id="ranking-input-marker1" class="ranking-input-item--marker">1.</div>
        <div class="ranking-input-item--inner">
          <div id="ranking-input-name1" class="ranking-input-item--text">Apple</div>
          <img decoding="async" class="ranking-input-item--photo" src="https://assets.codepen.io/128542/apple.png" width="72" height="72" alt="Apple" />
          <div class="ranking-input-item--move-group">
            <label for="ranking-input-move1">Position<span class="ranking-input-visually-hidden"> of Apple </span>:</label>
            <input type="text" name="fruit__positions[apple]" id="ranking-input-move1" inputmode="numeric" pattern="[0-9]*" autocomplete="off" />
          </div>
          <input type="hidden" name="fruit[]" value="apple" class="ranking-input-item--input" autocomplete="off" />
        </div>
      </li>
      <li aria-labelledby="ranking-input-marker2 ranking-input-name2" class="ranking-input-item-lowfi">
        <div id="ranking-input-marker2" class="ranking-input-item--marker">2.</div>
        <div class="ranking-input-item--inner">
          <div id="ranking-input-name2" class="ranking-input-item--text">Banana</div>
          <img decoding="async" class="ranking-input-item--photo" src="https://assets.codepen.io/128542/banana.png" width="72" height="72" alt="Banana" />
          <div class="ranking-input-item--move-group">
            <label for="ranking-input-move2">Position<span class="ranking-input-visually-hidden"> of Banana </span>:</label>
            <input type="text" name="fruit__positions[banana]"...

SCSS

/* Demo styles */
body {
  background: #eee;
  padding: 5rem;
  font-family: sans-serif;
}

#demo {
	width: 100%;
	max-width: 500px;
	margin: 5rem auto;
}

/* Component styles */

*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }

@mixin breakpoint($point) {
	@media (max-width: #{$point}px) { @content ; }
}

$prefix: 'ranking-input';

.#{$prefix} {
		--color--grey-3: #F5F6F7;
		--color--grey-40: #8D9297;

	--color-lowfi-background: var(--color--grey-3);
	--color-lowfi-seperator: var(--color--grey-40);

		--color--grey-50: #71767A;
		--color--grey-60: #565C65;
	
	--color-border: var(--color--grey-50);
	--color-background: #fff;
	--color-text: var(--color--grey-60);

		--color--teal-4: #EAFBFF;
		--color--teal-50: #0097BD;
		--color--teal-60: #006D88;

	--color-focus-border: var(--color--teal-50);
	--color-focus-background: var(--color--teal-4);
	--color-focus-text: var(--color--teal-60);

	--color-hover-border: var(--color--teal-50);
	--color-hover-background: var(--color--teal-4);
	--color-hover-text: var(--color--teal-60);

		--color--violet-4: #fef2ff;
		--color--violet-50: #be32d0;
		--color--violet-60: #93348c;

	--color-dragged-over-border: var(--color--violet-50);
	--color-dragged-over-background: var(--color--violet-4);
	--color-dragged-over-text: var(--color--violet-60);

	--button-gap: 1em;
	--dot-color: rgba(0,0,0,.6);
	--dot-distance: 1em;
	--drag-border-scale: .98;
	--animation-duration: .25;
	--row-gap: 2em;
	--marker-width: 2em;
	--marker-gap: 2em;
	--gap-indicator-scale-x: .5;
	--gap-indicator-overhang: 4em;
	--gap-border-width: 8px;
	--focus-border-width: 4px;

	margin: calc(2 * var(--row-gap)) 0 var(--row-gap);
	padding: 0 0 var(--row-gap);
	position: relative;
	text-align: left;
}

.#{$prefix}-gap {
	height: var(--row-gap);
	margin-left: calc(var(--marker-width) + var(--marker-gap));
	position: relative;
	width: calc(100% - (var(--marker-width) + var(--marker-gap)));

	&::before {
		background: none;
		border-top: dashed...

JavaScript

// Initiating component on element
document.addEventListener("DOMContentLoaded", () => {
		// Unique prefix for component (as used in SCSS and HTML)
		const prefix = 'ranking-input';

		// (optional) Remove duplicate SVGs
		document.querySelectorAll(`.${prefix}-svg`).forEach((el, idx) => {
				if ( idx > 0 ) {
						el.remove();
				}
		});

		// Initiate first non-initiated
		let el = document.querySelector(`.${prefix}-is-lowfi`);
		if ( el ) {
				// Note: comment out next line to see lowfi form.
				el.vhw_ranking_input = new RankingInput( el, prefix );
		}
});

/**
 * Ranking input component class
 *
 * Adapted by Philip van Heemstra (https://www.vanheemstra.com/)
 *
 * Based on article and demo by Darin Senneff (https://www.darins.page/)
 * https://www.darins.page/articles/designing-a-reorderable-list-component
 *
 * @todo Refactor and cleanup
 */

class RankingInput {

    static idx = 0;

    prefix = 'prefix'; // placeholder
    handle = 'prefix1'; // placeholder
    wrapper;
    oldList;
    items;
    buttons;
    listEnd;
    status;
    wrapperFrag;
    gapHeight;
    firstGap;
    curName;
    curItem;
    curButton;
    curDraggedEl;
    grabCoords;
    statusTimer;
    
    constructor( wrapper, prefix = 'prefix' ) {
        this.prefix = prefix;
        this.handle = `${this.prefix}${++RankingInput.idx}`;
        this.wrapper = wrapper;

				wrapper.classList.remove(`${this.prefix}-is-lowfi`);
        this.setup();
    }

    debounce(func, delay) {
        let inDebounce;
        return function () {
            const context = this;
            const args = arguments;
            clearTimeout(inDebounce);
            inDebounce = setTimeout(() => func.apply(context, args), delay);
        };
    }
    
    itemDragStart(e) {
        //console.log("dragstart");
    
        // add drag styling
        e.target.parentElement.classList.add("dragging");
    
        this.items.forEach((item) => {
            item.classList.remove("dragged-over");
  ...