{"_id":"fluid-marquee","_rev":"9-da37baf638f26160c7a3b3f7ef2e5316","name":"fluid-marquee","dist-tags":{"latest":"1.2.0"},"versions":{"1.0.0":{"name":"fluid-marquee","version":"1.0.0","keywords":["marquee","scroller","ticker","scrolling","carousel","javascript","zero-dependency"],"author":{"name":"Ewan Howell","email":"ewanhowell5195"},"license":"MIT","_id":"fluid-marquee@1.0.0","maintainers":[{"name":"ewanhowell5195","email":"ewan@ewanhowell.com"}],"homepage":"https://fluid-marquee.ewanhowell.com/","bugs":{"url":"https://github.com/ewanhowell5195/fluid-marquee/issues"},"dist":{"shasum":"43fcaec9288185e67236b2cd026f9d9a11c13985","tarball":"https://registry.npmjs.org/fluid-marquee/-/fluid-marquee-1.0.0.tgz","fileCount":5,"integrity":"sha512-nooMYlFWwe//yw/cWCh/ddScC9sXilj312znvA76TOEzRWTz9Oj0okGsy2POUpPy5J76W4AXT6UjFW3a3g5owQ==","signatures":[{"sig":"MEYCIQC+CnxstU0Li1rm1CNP2k8/DaQL0G27nuCX7svaINTxTQIhAL2x2wcfr4MYAZ/juiPj1+eTMB3jlnMucFRp/MiwcfeC","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":25152},"main":"./dist/fluid-marquee.min.js","type":"module","style":"./dist/fluid-marquee.min.css","exports":{".":"./dist/fluid-marquee.min.js","./styles.css":"./dist/fluid-marquee.min.css"},"gitHead":"d2fd484454ff7dbbd09ef7ac5631bca9654c7bf4","scripts":{"build":"node build.js"},"_npmUser":{"name":"ewanhowell5195","email":"ewan@ewanhowell.com"},"repository":{"url":"git+https://github.com/ewanhowell5195/fluid-marquee.git","type":"git"},"_npmVersion":"10.9.7","description":"A lightweight, zero-dependency marquee/scrolling content library using modern JavaScript and CSS.","directories":{},"_nodeVersion":"22.22.2","_hasShrinkwrap":false,"devDependencies":{"terser":"^5.43.1","clean-css":"^5.3.3"},"_npmOperationalInternal":{"tmp":"tmp/fluid-marquee_1.0.0_1779145071767_0.1282527833514726","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"fluid-marquee","version":"1.0.1","keywords":["marquee","scroller","ticker","scrolling","carousel","javascript","zero-dependency"],"author":{"name":"Ewan Howell","email":"ewanhowell5195"},"license":"MIT","_id":"fluid-marquee@1.0.1","maintainers":[{"name":"ewanhowell5195","email":"ewan@ewanhowell.com"}],"homepage":"https://fluid-marquee.ewanhowell.com/","bugs":{"url":"https://github.com/ewanhowell5195/fluid-marquee/issues"},"dist":{"shasum":"e1677fddf500e8d51f619fc380d4aa848a5a2a44","tarball":"https://registry.npmjs.org/fluid-marquee/-/fluid-marquee-1.0.1.tgz","fileCount":5,"integrity":"sha512-uCvcr/8fR11P5ErXOdPzYvXd4A9As+2nAW7kF81wsPx00G6x29YxsdL8W/gQO1tdoL+nLYdBJpQMx84MtM9Xzg==","signatures":[{"sig":"MEQCIC6o52Z41xLK821IeAUUDf0zy64EYCnH4lUYYjuacTnFAiBRQ16w9EjZaPLZJyfFuse0s9L1iS7+vkoUH2e0VUEu7Q==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":25124},"main":"./dist/fluid-marquee.min.js","type":"module","style":"./dist/fluid-marquee.min.css","exports":{".":"./dist/fluid-marquee.min.js","./styles.css":"./dist/fluid-marquee.min.css"},"gitHead":"0c3e8d28d54f4bed342c83511bd4d2adf66b94dd","scripts":{"build":"node build.js"},"_npmUser":{"name":"ewanhowell5195","email":"ewan@ewanhowell.com"},"repository":{"url":"git+https://github.com/ewanhowell5195/fluid-marquee.git","type":"git"},"_npmVersion":"10.9.7","description":"A lightweight, zero-dependency marquee/scrolling content library using modern JavaScript and CSS.","directories":{},"_nodeVersion":"22.22.2","_hasShrinkwrap":false,"devDependencies":{"terser":"^5.43.1","clean-css":"^5.3.3"},"_npmOperationalInternal":{"tmp":"tmp/fluid-marquee_1.0.1_1779145958606_0.24662834895279317","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"fluid-marquee","version":"1.1.0","keywords":["marquee","scroller","ticker","scrolling","carousel","javascript","zero-dependency"],"author":{"name":"Ewan Howell","email":"ewanhowell5195"},"license":"MIT","_id":"fluid-marquee@1.1.0","maintainers":[{"name":"ewanhowell5195","email":"ewan@ewanhowell.com"}],"homepage":"https://fluid-marquee.ewanhowell.com/","bugs":{"url":"https://github.com/ewanhowell5195/fluid-marquee/issues"},"dist":{"shasum":"19b456d39203349ff8fdec0a90a1e476489944fc","tarball":"https://registry.npmjs.org/fluid-marquee/-/fluid-marquee-1.1.0.tgz","fileCount":5,"integrity":"sha512-u7abv6tQIkOgEzLcfLQyyAzPLSBgrDgZFOyRZsUCKjX+eMovD9I4fKJ4m1rARU5AKtGIiPwwG9xZcyLGflOIsw==","signatures":[{"sig":"MEYCIQCdJGttrNN43jDXbBTOA2nUcUZ+m96Y0cPbT85sjlyA2QIhAKRanqFzWiWGJiklrgfd0K3vCZTLeKxmY4LnoWzH6+RK","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":25730},"main":"./dist/fluid-marquee.min.js","type":"module","style":"./dist/fluid-marquee.min.css","exports":{".":"./dist/fluid-marquee.min.js","./styles.css":"./dist/fluid-marquee.min.css"},"gitHead":"f63dac8fa9f3be11cd1f42dac9e13b1bf0dca0e3","scripts":{"build":"node build.js"},"_npmUser":{"name":"ewanhowell5195","email":"ewan@ewanhowell.com"},"repository":{"url":"git+https://github.com/ewanhowell5195/fluid-marquee.git","type":"git"},"_npmVersion":"10.9.7","description":"A lightweight, zero-dependency marquee/scrolling content library using modern JavaScript and CSS.","directories":{},"_nodeVersion":"22.22.2","_hasShrinkwrap":false,"devDependencies":{"terser":"^5.43.1","clean-css":"^5.3.3"},"_npmOperationalInternal":{"tmp":"tmp/fluid-marquee_1.1.0_1779146778879_0.8964898837471404","host":"s3://npm-registry-packages-npm-production"}},"1.1.1":{"name":"fluid-marquee","version":"1.1.1","keywords":["marquee","scroller","ticker","scrolling","carousel","javascript","zero-dependency"],"author":{"name":"Ewan Howell","email":"ewanhowell5195"},"license":"MIT","_id":"fluid-marquee@1.1.1","maintainers":[{"name":"ewanhowell5195","email":"ewan@ewanhowell.com"}],"homepage":"https://fluid-marquee.ewanhowell.com/","bugs":{"url":"https://github.com/ewanhowell5195/fluid-marquee/issues"},"dist":{"shasum":"2c99f611289a892b8647e8b5dfbc0d3214f32bcb","tarball":"https://registry.npmjs.org/fluid-marquee/-/fluid-marquee-1.1.1.tgz","fileCount":5,"integrity":"sha512-sL9BcewdmNQ8Vp4zLBnEx/7yJbikF0AGIeG2n9+5oBVcHBZ7p7bi/BFOUlo2uWwR4VRg/S1QHapMXrvg7BYBWQ==","signatures":[{"sig":"MEUCIQCAGjtYyEWAwZAg19PTavh4LX0ffS5cOKVMXzyO9/o9GgIgKnqr9wgtzAnqV2f7Tk5nIlbgcnv8DU7s6dwyoC7IUKY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":25637},"main":"./dist/fluid-marquee.min.js","type":"module","style":"./dist/fluid-marquee.min.css","exports":{".":"./dist/fluid-marquee.min.js","./styles.css":"./dist/fluid-marquee.min.css"},"gitHead":"5150572924eb263dfc6fa72224f85da2e9fc41e6","scripts":{"build":"node build.js"},"_npmUser":{"name":"ewanhowell5195","email":"ewan@ewanhowell.com"},"repository":{"url":"git+https://github.com/ewanhowell5195/fluid-marquee.git","type":"git"},"_npmVersion":"10.9.7","description":"A lightweight, zero-dependency marquee/scrolling content library using modern JavaScript and CSS.","directories":{},"_nodeVersion":"22.22.2","_hasShrinkwrap":false,"devDependencies":{"terser":"^5.43.1","clean-css":"^5.3.3"},"_npmOperationalInternal":{"tmp":"tmp/fluid-marquee_1.1.1_1779148189521_0.19446017964421403","host":"s3://npm-registry-packages-npm-production"}},"1.1.2":{"name":"fluid-marquee","version":"1.1.2","keywords":["marquee","scroller","ticker","scrolling","carousel","javascript","zero-dependency"],"author":{"name":"Ewan Howell","email":"ewanhowell5195"},"license":"MIT","_id":"fluid-marquee@1.1.2","maintainers":[{"name":"ewanhowell5195","email":"ewan@ewanhowell.com"}],"homepage":"https://fluid-marquee.ewanhowell.com/","bugs":{"url":"https://github.com/ewanhowell5195/fluid-marquee/issues"},"dist":{"shasum":"fb90cc3de1c860b6df521f46412e3ae3ac1043b4","tarball":"https://registry.npmjs.org/fluid-marquee/-/fluid-marquee-1.1.2.tgz","fileCount":5,"integrity":"sha512-ZkVJNEHrJtVjqcdBaujjQTbOWc9gjUhxwaTr1KppyhjfE0PrWYhyuY/VFCPFDRIJVgf5pd+9PEXSsoRyx/vGUA==","signatures":[{"sig":"MEQCIFSMI4s/OCRgkvF2O5WrzVyhqVQl0kbqZjzK2GnPyHYsAiBcJrAnyvIa9UxRkNEfwGHHw8rhIlhUuN8AZFOHYLs2lQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":25759},"main":"./dist/fluid-marquee.min.js","type":"module","style":"./dist/fluid-marquee.min.css","exports":{".":"./dist/fluid-marquee.min.js","./styles.css":"./dist/fluid-marquee.min.css"},"gitHead":"b12a1eef23d7489ba65d53c1028cfac0ba04ee52","scripts":{"build":"node build.js"},"_npmUser":{"name":"ewanhowell5195","email":"ewan@ewanhowell.com"},"repository":{"url":"git+https://github.com/ewanhowell5195/fluid-marquee.git","type":"git"},"_npmVersion":"10.9.7","description":"A lightweight, zero-dependency marquee/scrolling content library using modern JavaScript and CSS.","directories":{},"_nodeVersion":"22.22.2","_hasShrinkwrap":false,"devDependencies":{"terser":"^5.43.1","clean-css":"^5.3.3"},"_npmOperationalInternal":{"tmp":"tmp/fluid-marquee_1.1.2_1779149020729_0.8455250779490255","host":"s3://npm-registry-packages-npm-production"}},"1.1.3":{"name":"fluid-marquee","version":"1.1.3","keywords":["marquee","scroller","ticker","scrolling","carousel","javascript","zero-dependency"],"author":{"name":"Ewan Howell","email":"ewanhowell5195"},"license":"MIT","_id":"fluid-marquee@1.1.3","maintainers":[{"name":"ewanhowell5195","email":"ewan@ewanhowell.com"}],"homepage":"https://fluid-marquee.ewanhowell.com/","bugs":{"url":"https://github.com/ewanhowell5195/fluid-marquee/issues"},"dist":{"shasum":"49812c187f0f17fb702b5f645b6aad36e41c48d4","tarball":"https://registry.npmjs.org/fluid-marquee/-/fluid-marquee-1.1.3.tgz","fileCount":5,"integrity":"sha512-ZhEuvI4Bp5m2RKtvDAPQ+oVfEu2fQQkE5ymu/bGVwwIAkX6fXb3VdjnEwLaiIGQtmjeefUrqemb37d+EnU3fjw==","signatures":[{"sig":"MEYCIQDU0wItkWF3/6jSOjhgXnQzXh2J/0mqvBuIs1z2WWnTHwIhAOqp/XMsWKWX2e3Po0G5rRhrzKUhnAhEplyKQ4hb1GVU","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":25778},"main":"./dist/fluid-marquee.min.js","type":"module","style":"./dist/fluid-marquee.min.css","exports":{".":"./dist/fluid-marquee.min.js","./styles.css":"./dist/fluid-marquee.min.css"},"gitHead":"77f4afc9e8ab4b97a20e2478c5cfa381060f7948","scripts":{"build":"node build.js"},"_npmUser":{"name":"ewanhowell5195","email":"ewan@ewanhowell.com"},"repository":{"url":"git+https://github.com/ewanhowell5195/fluid-marquee.git","type":"git"},"_npmVersion":"10.9.7","description":"A lightweight, zero-dependency marquee/scrolling content library using modern JavaScript and CSS.","directories":{},"_nodeVersion":"22.22.2","_hasShrinkwrap":false,"devDependencies":{"terser":"^5.43.1","clean-css":"^5.3.3"},"_npmOperationalInternal":{"tmp":"tmp/fluid-marquee_1.1.3_1779149925625_0.5463684935078266","host":"s3://npm-registry-packages-npm-production"}},"1.1.4":{"name":"fluid-marquee","version":"1.1.4","keywords":["marquee","scroller","ticker","scrolling","carousel","javascript","zero-dependency"],"author":{"name":"Ewan Howell","email":"ewanhowell5195"},"license":"MIT","_id":"fluid-marquee@1.1.4","maintainers":[{"name":"ewanhowell5195","email":"ewan@ewanhowell.com"}],"homepage":"https://fluid-marquee.ewanhowell.com/","bugs":{"url":"https://github.com/ewanhowell5195/fluid-marquee/issues"},"dist":{"shasum":"30cdbcdd415ab34a9b5f54d3ea499b6e736b2681","tarball":"https://registry.npmjs.org/fluid-marquee/-/fluid-marquee-1.1.4.tgz","fileCount":5,"integrity":"sha512-Vjap9ZC2s9MgnVL+VEvI/qGQYJAhbrg0uu1D/u4vuEjmGJbYId9V6d4wJvpYpgnYmw3nuYsOyTQa2KDQZHOZ1g==","signatures":[{"sig":"MEYCIQC94DqBU9W7J/pzQ0ys2Oko1Ptg1dmMCz/vyjIC0NE/DQIhALTyu37ZE6txPl/7ku77u0Sa9rfcIsc2Gut95tPlFNOT","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":25923},"main":"./dist/fluid-marquee.min.js","type":"module","style":"./dist/fluid-marquee.min.css","exports":{".":"./dist/fluid-marquee.min.js","./styles.css":"./dist/fluid-marquee.min.css"},"gitHead":"bf4566641dab52beab7cbf3440a29799f36298f3","scripts":{"build":"node build.js"},"_npmUser":{"name":"ewanhowell5195","email":"ewan@ewanhowell.com"},"repository":{"url":"git+https://github.com/ewanhowell5195/fluid-marquee.git","type":"git"},"_npmVersion":"10.9.7","description":"A lightweight, zero-dependency marquee/scrolling content library using modern JavaScript and CSS.","directories":{},"_nodeVersion":"22.22.2","_hasShrinkwrap":false,"devDependencies":{"terser":"^5.43.1","clean-css":"^5.3.3"},"_npmOperationalInternal":{"tmp":"tmp/fluid-marquee_1.1.4_1779150396859_0.9152584495776159","host":"s3://npm-registry-packages-npm-production"}},"1.1.5":{"name":"fluid-marquee","version":"1.1.5","keywords":["marquee","scroller","ticker","scrolling","carousel","javascript","zero-dependency"],"author":{"name":"Ewan Howell","email":"ewanhowell5195"},"license":"MIT","_id":"fluid-marquee@1.1.5","maintainers":[{"name":"ewanhowell5195","email":"ewan@ewanhowell.com"}],"homepage":"https://fluid-marquee.ewanhowell.com/","bugs":{"url":"https://github.com/ewanhowell5195/fluid-marquee/issues"},"dist":{"shasum":"763f02493a477ca070efde52a052a745211005f3","tarball":"https://registry.npmjs.org/fluid-marquee/-/fluid-marquee-1.1.5.tgz","fileCount":5,"integrity":"sha512-nDUaQ99RktuiNx4vxDhyFARB+YgVDvDFaaFlvYYPLb4/QWaXLoAkMG9TOFiZtnlC372twIhhtdxMYbdAgZUtOw==","signatures":[{"sig":"MEMCICpeGsTAYjnZVONEFwrVqzGb+ytZHXEWc+6rr43rHSG2Ah9IEsuL6xWo8YuDsUdTgtqgP8TDQbzeGh6Mk96i7iOL","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":26622},"main":"./dist/fluid-marquee.min.js","type":"module","style":"./dist/fluid-marquee.min.css","exports":{".":"./dist/fluid-marquee.min.js","./styles.css":"./dist/fluid-marquee.min.css"},"gitHead":"4018d560c86c98eb5509bffea3faa21a5c929338","scripts":{"build":"node build.js"},"_npmUser":{"name":"ewanhowell5195","email":"ewan@ewanhowell.com"},"repository":{"url":"git+https://github.com/ewanhowell5195/fluid-marquee.git","type":"git"},"_npmVersion":"10.9.7","description":"A lightweight, zero-dependency marquee/scrolling content library using modern JavaScript and CSS.","directories":{},"_nodeVersion":"22.22.2","_hasShrinkwrap":false,"devDependencies":{"terser":"^5.43.1","clean-css":"^5.3.3"},"_npmOperationalInternal":{"tmp":"tmp/fluid-marquee_1.1.5_1779178416830_0.3170771366310552","host":"s3://npm-registry-packages-npm-production"}},"1.2.0":{"name":"fluid-marquee","version":"1.2.0","description":"A lightweight, zero-dependency marquee/scrolling content library using modern JavaScript and CSS.","author":{"name":"Ewan Howell","email":"ewanhowell5195"},"license":"MIT","type":"module","main":"./dist/fluid-marquee.min.js","style":"./dist/fluid-marquee.min.css","exports":{".":"./dist/fluid-marquee.min.js","./styles.css":"./dist/fluid-marquee.min.css"},"repository":{"type":"git","url":"git+https://github.com/ewanhowell5195/fluid-marquee.git"},"bugs":{"url":"https://github.com/ewanhowell5195/fluid-marquee/issues"},"homepage":"https://fluid-marquee.ewanhowell.com/","scripts":{"build":"node build.js"},"devDependencies":{"clean-css":"^5.3.3","terser":"^5.43.1"},"keywords":["marquee","scroller","ticker","scrolling","carousel","javascript","zero-dependency"],"_id":"fluid-marquee@1.2.0","gitHead":"0e6f6fbf31c1c34d8f76d02652402b7e1b5fda89","_nodeVersion":"22.22.2","_npmVersion":"10.9.7","dist":{"integrity":"sha512-iIhfZ9AdeW7KzVYC2KPz7JKU465ciMJZ6OpSIK3VS/pQXFff1yWM1dQ+vG025ZkwCYViXJKbBO2D1hr/HAMNGg==","shasum":"06750422fc7399a158c2e10a6db387044c9e8975","tarball":"https://registry.npmjs.org/fluid-marquee/-/fluid-marquee-1.2.0.tgz","fileCount":5,"unpackedSize":30600,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCICxlKPRnhMp538SiddThLI52IlYEYNUBA3VFCUJ/7WluAiEA2QH1Ie3SwtfHRT0oYU1dHjIUV1GXbbADle3u0Ztucak="}]},"_npmUser":{"name":"ewanhowell5195","email":"ewan@ewanhowell.com"},"directories":{},"maintainers":[{"name":"ewanhowell5195","email":"ewan@ewanhowell.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/fluid-marquee_1.2.0_1779195898413_0.8275407559682124"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-18T22:57:51.766Z","modified":"2026-05-19T13:04:58.657Z","1.0.0":"2026-05-18T22:57:51.906Z","1.0.1":"2026-05-18T23:12:38.769Z","1.1.0":"2026-05-18T23:26:19.026Z","1.1.1":"2026-05-18T23:49:49.665Z","1.1.2":"2026-05-19T00:03:40.905Z","1.1.3":"2026-05-19T00:18:45.794Z","1.1.4":"2026-05-19T00:26:37.024Z","1.1.5":"2026-05-19T08:13:36.963Z","1.2.0":"2026-05-19T13:04:58.558Z"},"bugs":{"url":"https://github.com/ewanhowell5195/fluid-marquee/issues"},"author":{"name":"Ewan Howell","email":"ewanhowell5195"},"license":"MIT","homepage":"https://fluid-marquee.ewanhowell.com/","keywords":["marquee","scroller","ticker","scrolling","carousel","javascript","zero-dependency"],"repository":{"type":"git","url":"git+https://github.com/ewanhowell5195/fluid-marquee.git"},"description":"A lightweight, zero-dependency marquee/scrolling content library using modern JavaScript and CSS.","maintainers":[{"name":"ewanhowell5195","email":"ewan@ewanhowell.com"}],"readme":"# fluid-marquee\n\nA lightweight, zero-dependency marquee/scrolling content library using modern JavaScript and CSS.\nJust add `class=\"fluid-marquee\"` to any container!\n\n[![npm version](https://badge.fury.io/js/fluid-marquee.svg)](https://www.npmjs.com/package/fluid-marquee)\n[![jsDelivr](https://data.jsdelivr.com/v1/package/npm/fluid-marquee/badge)](https://www.jsdelivr.com/package/npm/fluid-marquee)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\n[**Live Demo**](https://fluid-marquee.ewanhowell.com/)\n\n## Features\n\n* No dependencies\n* Only scrolls when content actually overflows, otherwise it stays static and centered\n* Horizontal or vertical\n* Configurable speed, in either direction\n* Smooth ease in/out on pause and resume\n* Pause on hover, on click, or both\n* Drag-to-scrub with momentum, on mouse and touch\n* Auto recalculates on resize and when images finish loading\n* Auto pauses when scrolled off-screen\n* Performance-conscious - steady-state scrolling runs on the compositor thread, off the main thread\n* Programmatic API for pause, resume, and item management\n* Framework-friendly when items are managed via the JS API (`add`, `remove`, `setItems`) instead of reactive rendering\n\n## Quick Start\n\n### Install via npm\n```bash\nnpm install fluid-marquee\n```\n\n```js\nimport \"fluid-marquee/styles.css\"\nimport \"fluid-marquee\"\n```\n\n### Or use via CDN\nhttps://www.jsdelivr.com/package/npm/fluid-marquee\n\n### Add a marquee to your HTML\n\n```html\n<div class=\"fluid-marquee\">\n  <div class=\"fluid-marquee-item\">First item</div>\n  <div class=\"fluid-marquee-item\">Second item</div>\n  <div class=\"fluid-marquee-item\">Third item</div>\n</div>\n```\n\nEvery `.fluid-marquee` on the page is initialised automatically on load - no setup or config required. Marquees added later can be initialised with `FluidMarquee.init(el)` or `FluidMarquee.initAll()`.\n\nBy default, the marquee only animates when its contents are wider than the container. If they fit, the items stay static and centered. Resize the window or add/remove items and it recalculates automatically.\n\n## Settings\n\nAll options can be set as HTML data attributes:\n\n| Attribute | Description |\n|---|---|\n| `data-fluid-marquee-speed=\"64\"` | Scroll speed in pixels per second. Defaults to `64`. Negative values reverse direction. |\n| `data-fluid-marquee-infinite` | Always scroll, even if the content fits inside the container. |\n| `data-fluid-marquee-vertical` | Scroll vertically instead of horizontally. The container needs a height. |\n| `data-fluid-marquee-pausable` | Pause on hover (auto resumes) and on click (click outside to resume). |\n| `data-fluid-marquee-pause-hover` | Only pause on hover. |\n| `data-fluid-marquee-pause-click` | Only pause on click. |\n| `data-fluid-marquee-draggable` | Allow click-and-drag (or touch-drag) to scrub through the marquee, with momentum on release. |\n| `data-fluid-marquee-run-scripts` | Re-execute `<script>` tags inside cloned items. Off by default. |\n\nAll options can also be passed as a JavaScript object - see [Programmatic init](#programmatic-init).\n\n## Advanced Usage\n\n### Speed and direction\n\nSpeed is in pixels per second. Set a negative value to scroll in reverse:\n\n```html\n<div class=\"fluid-marquee\" data-fluid-marquee-speed=\"64\">…</div>\n<div class=\"fluid-marquee\" data-fluid-marquee-speed=\"-128\">…</div>\n```\n\n### Infinite scroll\n\nWithout `data-fluid-marquee-infinite`, the marquee stays static when the content fits. Add it to force scrolling regardless:\n\n```html\n<div class=\"fluid-marquee\" data-fluid-marquee-infinite>…</div>\n```\n\n### Vertical\n\n```html\n<div class=\"fluid-marquee\" data-fluid-marquee-vertical style=\"height: 320px;\">\n  <div class=\"fluid-marquee-item\">First</div>\n  <div class=\"fluid-marquee-item\">Second</div>\n  <div class=\"fluid-marquee-item\">Third</div>\n</div>\n```\n\nDefault item padding is swapped from `0 16px` to `16px 0` when vertical.\n\n### Pausing\n\n```html\n<!-- Hover and click -->\n<div class=\"fluid-marquee\" data-fluid-marquee-pausable>…</div>\n\n<!-- Pauses while hovered, auto resumes -->\n<div class=\"fluid-marquee\" data-fluid-marquee-pause-hover>…</div>\n\n<!-- Click to lock pause, click outside to unlock -->\n<div class=\"fluid-marquee\" data-fluid-marquee-pause-click>…</div>\n```\n\nPause and resume are smoothly eased so the marquee doesn't visually snap.\n\n### Dragging\n\nAdd `data-fluid-marquee-draggable` to let users grab the marquee and scrub through it. Flicking on release applies momentum that decays smoothly back into the normal scroll.\n\n```html\n<div class=\"fluid-marquee\" data-fluid-marquee-draggable>…</div>\n```\n\n### Scripts in items\n\nBy default, `<script>` tags inside items are *not* re-executed when items are cloned. Add `data-fluid-marquee-run-scripts` to re-execute them on each clone:\n\n```html\n<div class=\"fluid-marquee\" data-fluid-marquee-run-scripts data-fluid-marquee-infinite>\n  <div class=\"fluid-marquee-item\">\n    <button>Click me</button>\n    <script>\n      const btn = document.currentScript.previousElementSibling\n      btn.addEventListener(\"click\", () => alert(\"Clicked!\"))\n    </script>\n  </div>\n</div>\n```\n\nIn this example, each clone runs its own copy of the script, so every visible button gets its own click listener. Without `data-fluid-marquee-run-scripts`, the inert clones produced by `cloneNode` wouldn't run and only the original button would respond - clicks on the clones would do nothing.\n\nEvent delegation on the marquee element is the alternative if you want the listener attached just once.\n\n## Programmatic API\n\n`fluid-marquee` auto-initialises every `.fluid-marquee` on the page. You can also drive it from JavaScript.\n\n### Programmatic init\n\n```js\n// Initialise a specific element (returns the instance, idempotent)\nFluidMarquee.init(el, { speed: 64, pausable: true })\n\n// Initialise everything inside a root (defaults to document)\nFluidMarquee.initAll(document, { draggable: true })\n\n// Initialise an element directly, without the closest() lookup\nnew FluidMarquee(el, { speed: 64 })\n```\n\n`FluidMarquee.init(el)` walks up from `el` with `closest(\".fluid-marquee\")` and uses that, so you can pass a child of the marquee. `new FluidMarquee(el)` initialises `el` itself directly, with no lookup.\n\nBoth paths are idempotent: calling them on an already-initialised element returns the existing instance instead of creating a new one.\n\nThe `options` argument accepts the same keys as the data attributes, but in `camelCase`:\n\n| Option | Type |\n|---|---|\n| `speed` | number |\n| `infinite` | boolean |\n| `vertical` | boolean |\n| `pausable` | boolean |\n| `pauseHover` | boolean |\n| `pauseClick` | boolean |\n| `draggable` | boolean |\n| `runScripts` | boolean |\n\n### Getting the instance\n\nAfter init, the marquee instance is available three ways:\n\n```js\nconst el = document.querySelector(\".fluid-marquee\")\n\nel.marquee                  // The instance, attached to the marquee element itself\nFluidMarquee.get(el)        // Same instance - also accepts any descendant (uses closest)\nFluidMarquee.init(el)       // Same - also accepts any descendant (uses closest)\n```\n\n`el.marquee` is the shortest form when you already have a reference to the marquee element. `get` and `init` walk up from `el` using `closest(\".fluid-marquee\")`, so they work whether you pass the marquee itself or any element inside it.\n\n### Events\n\nA `fluid-marquee:init` event is dispatched on each marquee element once it finishes initialising.\n\n```js\nel.addEventListener(\"fluid-marquee:init\", e => {\n  e.target.marquee.pause()\n})\n```\n\nA `fluid-marquee:ready` event is dispatched on `window` once the initial auto-init pass has run (after `DOMContentLoaded`):\n\n```js\naddEventListener(\"fluid-marquee:ready\", () => {\n  document.querySelector(\".fluid-marquee\").marquee.pause()\n})\n```\n\nUse this when your code runs before the library has had a chance to initialise (e.g. when scripts are deferred or async).\n\n`fluid-marquee:pause` and `fluid-marquee:resume` events fire when a pause cause activates or deactivates, *as long as no higher-priority cause is already active*. `event.detail.cause` tells you which cause the event refers to:\n\n| `cause` | Meaning |\n|---|---|\n| `\"api\"` | `m.pause()` / `m.resume()` |\n| `\"click\"` | The user clicked the marquee (or clicked outside to unlock) |\n| `\"drag\"` | The user is dragging the marquee |\n| `\"hover\"` | The mouse is over the marquee |\n\nPriority order is `api > click > drag > hover`. While a higher-priority cause is in effect, lower-priority causes flip silently in the background. For example, hovering on/off while click-paused fires no events, since click is the visible cause.\n\n```js\nel.addEventListener(\"fluid-marquee:pause\", e => {\n  if (e.detail.cause === \"hover\") return // ignore hover\n  pauseButton.textContent = \"Resume\"\n})\n\nel.addEventListener(\"fluid-marquee:resume\", e => {\n  if (e.detail.cause === \"hover\") return\n  pauseButton.textContent = \"Pause\"\n})\n```\n\n`event.detail.marquee` is the instance, the same as `e.target.marquee`.\n\n### Instance API\n\n```js\nm.pause()         // Sticky pause - only resume() clears it\nm.pause(false)    // User-style pause - clicking outside the marquee clears it\nm.resume()        // Clears api and click pauses (hover and drag self-resolve)\nm.paused          // True if anything is currently keeping it paused\nm.apiPaused       // True if paused via m.pause()\nm.userPaused      // Aggregate - hoverPaused || clickPaused || dragPaused\nm.hoverPaused     // True if the mouse is currently hovering\nm.clickPaused     // True if the user clicked the marquee to lock pause\nm.dragPaused      // True while the user is actively touching/dragging the marquee\n\nm.refresh()       // Force a re-measure (rarely needed, ResizeObserver handles most cases)\nm.destroy()       // Tear down completely and restore the current items as direct children\n```\n\n### Item management\n\n```js\nm.items           // Getter - array of the current item elements\n\nm.add(itemEl)              // Append one or more items\nm.add(itemA, itemB, itemC) // Append several\nm.add([itemA, itemB])      // Or pass an array\n\nm.remove(itemEl)           // Remove one or more items\nm.remove(itemA, itemB)     // Several\nm.remove([itemA, itemB])   // Or as an array\n\nm.setItems([a, b, c])      // Replace all items at once\nm.setItems(a, b, c)        // Or pass them as args\n```\n\nAfter any items change, the measure snapshot is rebuilt and clones are regenerated automatically.\n\n### Example\n\n```html\n<div class=\"fluid-marquee\">\n  <div class=\"fluid-marquee-item\">First</div>\n  <div class=\"fluid-marquee-item\">Second</div>\n</div>\n<button id=\"add\">Add</button>\n<button id=\"pause\">Toggle pause</button>\n```\n\n```js\nconst m = document.querySelector(\".fluid-marquee\").marquee\n\ndocument.getElementById(\"add\").onclick = () => {\n  const item = document.createElement(\"div\")\n  item.className = \"fluid-marquee-item\"\n  item.textContent = `Item ${m.items.length + 1}`\n  m.add(item)\n}\n\ndocument.getElementById(\"pause\").onclick = () => {\n  if (m.paused) m.resume()\n  else m.pause()\n}\n```\n\n## Styling\n\n`fluid-marquee` only ships the structural CSS it needs to function. All visual styling is up to you - style `.fluid-marquee` and `.fluid-marquee-item` like any other element:\n\n```css\n.fluid-marquee {\n  background: #f1f1f1;\n  border: 1px solid #ddd;\n  padding: 12px 0;\n}\n\n.fluid-marquee-item {\n  font-weight: 600;\n}\n```\n\n### Structure after init\n\nAfter init, your items are no longer direct children of `.fluid-marquee`. They get wrapped in `.fluid-marquee-sub`, which sits inside `.fluid-marquee-track`. Cloned copies of the strip are appended alongside the original. So the DOM looks roughly like:\n\n```html\n<div class=\"fluid-marquee fluid-marquee-initialised\">\n  <div class=\"fluid-marquee-track\">\n    <div class=\"fluid-marquee-sub\">…your items…</div>\n    <div class=\"fluid-marquee-sub fluid-marquee-clone\">…</div>\n    <div class=\"fluid-marquee-sub fluid-marquee-clone\">…</div>\n  </div>\n  <div class=\"fluid-marquee-measure\">…hidden measure copy…</div>\n</div>\n```\n\nAvoid direct-child selectors like `.fluid-marquee > *` or `.fluid-marquee > .item` - they won't match your items post-init. Use `.fluid-marquee-item` (or a descendant selector) instead.\n\n### Classes\n\nThese classes are added by `fluid-marquee` and can be targeted with CSS:\n\n| Class | When applied |\n|---|---|\n| `fluid-marquee-initialised` | After the marquee is initialised. |\n| `fluid-marquee-scrolling` | While the marquee is actively scrolling (content overflows or `data-fluid-marquee-infinite` is set). |\n| `fluid-marquee-vertical` | When `data-fluid-marquee-vertical` is set. |\n| `fluid-marquee-draggable` | When `data-fluid-marquee-draggable` is set. |\n| `fluid-marquee-dragging` | While the user is actively dragging. |\n| `fluid-marquee-clone` | On each cloned copy of the item strip. |\n\nIf the content fits inside the container and `data-fluid-marquee-infinite` is not set, the marquee stays static and centered.\n\n## How it works\n\n`fluid-marquee` uses a clone-and-translate technique:\n\n1. **Wraps your items** in an internal `.fluid-marquee-track` and `.fluid-marquee-sub`\n2. **Measures the strip** in a hidden node alongside the real one\n3. **Decides whether to scroll** based on whether the strip overflows the container\n4. **Clones the strip** as many times as needed to fill the visible width\n5. **Animates with a hybrid WAAPI + rAF strategy** (see below) by translating the track, wrapping the offset modulo the strip width\n6. **Recalculates automatically** on container resize, image load, and item changes\n\n### Hybrid animation\n\nThe animation runs in two modes and seamlessly hands off between them:\n\n* **WAAPI (Web Animations API)** drives the steady state. When the marquee is just scrolling at a constant speed (not paused, not dragging, no momentum, on-screen), the track is animated via `element.animate()`. This runs on the browser's compositor thread - the same path CSS `@keyframes` use - so it stays smooth even when the main thread is busy, and doesn't burden the main thread itself.\n* **`requestAnimationFrame`** takes over whenever something needs frame-by-frame JS: drag-to-scrub, momentum flick, the smooth ease in/out on pause/resume, or any other transitional state.\n\nOn handoff, the current `currentTime` of the WAAPI animation is read back into the shared `offset`, and the next mode picks up exactly where the previous one left off - so you get native-compositor smoothness in the common case without giving up any of the interactive behaviour rAF makes easy.\n\n## License\n\nMIT © [Ewan Howell](https://ewanhowell.com/)\n","readmeFilename":"README.md"}