{
    "id": "bhaskar/quantity-stepper",
    "kind": "component",
    "title": "Quantity Stepper",
    "summary": "A minus, count and plus control for carts and forms that clamps between a min and max and disables at the edges.",
    "author": "Admin",
    "stars": 0,
    "url": "https://ownlyplans.com/bhaskar/quantity-stepper/",
    "stack": [],
    "tools": [
        "claude-code",
        "cursor",
        "lovable"
    ],
    "difficulty": "beginner",
    "license": "mit",
    "version": "1.0",
    "overview": "Set data-min and data-max on the wrapper. Both buttons are real buttons with labels, and the count is announced as it changes.",
    "steps": [],
    "files": [],
    "install": "",
    "how_to_use": "Use the component code (html, css, js, or react) directly, or run the install command.",
    "component": {
        "category": "Buttons",
        "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>",
        "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)}\n.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}\n.qs__b svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:2.2;stroke-linecap:round}\n.qs__b:active{transform:scale(.94)}\n.qs__b:disabled{opacity:.4;cursor:not-allowed;transform:none}\n.qs__b:focus-visible{outline:2px solid #1659f5;outline-offset:2px}\n.qs__v{min-width:52px;text-align:center;font:500 16px/1 ui-monospace,SFMono-Regular,Menlo,monospace;color:#262626}",
        "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;\nfunction r(){v.textContent=n;m.disabled=n<=lo;p.disabled=n>=hi}\nm.addEventListener('click',()=>{n=Math.max(lo,n-1);r()});p.addEventListener('click',()=>{n=Math.min(hi,n+1);r()});r();",
        "react": "",
        "dependencies": [],
        "install": "",
        "licence_notice": ""
    }
}