Quantity Stepper
A minus, count and plus control for carts and forms that clamps between a min and max and disables at the edges.
Preview and code
index.html
<div class="qs" data-min="1" data-max="9"><button class="qs__b" type="button" aria-label="Decrease"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6 12h12"/></svg></button><output class="qs__v" aria-live="polite">1</output><button class="qs__b" type="button" aria-label="Increase"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6 12h12M12 6v12"/></svg></button></div>
styles.css
.qs{display:inline-flex;align-items:center;padding:5px;border-radius:16px;background:#ececee;box-shadow:inset 0 1px 3px rgba(0,0,0,.12)}
.qs__b{display:grid;place-items:center;width:42px;height:42px;border:0;border-radius:12px;background:#fff;color:#262626;cursor:pointer;box-shadow:0 1px 2px rgba(0,0,0,.12),0 4px 8px rgba(0,0,0,.08);transition:transform .12s ease,opacity .2s ease}
.qs__b svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:2.2;stroke-linecap:round}
.qs__b:active{transform:scale(.94)}
.qs__b:disabled{opacity:.4;cursor:not-allowed;transform:none}
.qs__b:focus-visible{outline:2px solid #1659f5;outline-offset:2px}
.qs__v{min-width:52px;text-align:center;font:500 16px/1 ui-monospace,SFMono-Regular,Menlo,monospace;color:#262626}
script.js
const w=document.querySelector('.qs'),v=w.querySelector('.qs__v'),[m,p]=w.querySelectorAll('.qs__b');const lo=+w.dataset.min,hi=+w.dataset.max;let n=lo;
function r(){v.textContent=n;m.disabled=n<=lo;p.disabled=n>=hi}
m.addEventListener('click',()=>{n=Math.max(lo,n-1);r()});p.addEventListener('click',()=>{n=Math.min(hi,n+1);r()});r();
About
Set data-min and data-max on the wrapper. Both buttons are real buttons with labels, and the count is announced as it changes.