Web component · zero dependencies · GPL-2.0
Drop plain images inside an element and it becomes a slider. No jQuery, no stylesheet to load, no icon font, no build step — the markup and styles live in a shadow root, so nothing leaks in or out.
This page is the demo
Everything below is the real component, running on this page. It is not a screenshot, an iframe or a video — the same file the install command fetches. Hover the slider to pause it; drag it on a touch device.
animation="slide" nav pager autoplay interval="4500" pause-on-hover theme="dark" height="380px"
That is the whole markup — one import, then plain images:
<!-- one import; the component is a single file --> <script type="module" src="flex-slider.js"></script> <!-- plain images are the whole API --> <flex-slider animation="slide" nav pager autoplay interval="4500" pause-on-hover theme="dark" height="380px"> <img src="slide-1.jpg" alt="First slide"> <img src="slide-2.jpg" alt="Second slide"> <img src="slide-3.jpg" alt="Third slide"> </flex-slider>
One element, every mode
Three independent instances on this page, each configured by attributes alone. They share no state — paging one leaves the others where they were.
animation="fade" pager · Middle: pager thumbs · Right: direction="vertical"
Methods, properties and events
The element is the API. Every button drives the slider beside it, and each
slide-start and slide-end is written to the panel
underneath — the same events you would listen for in your own page.
listening for slide-start…
Restyled without touching the component
These two sliders below are driven by CSS custom properties and shadow parts from this page's own stylesheet. The component's internals are untouched — that is the whole point of the shadow boundary.
::part().
/* custom properties, set on the host */ #theme-warm { --fs-accent: #ff9f45; --fs-arrow-bg: rgba(255, 159, 69, .92); --fs-arrow-fg: #201603; --fs-dot: rgba(255, 159, 69, .45); --fs-dot-active: #ff9f45; --fs-radius: 16px; } /* or reach inside by shadow part */ #theme-square::part(arrow-prev), #theme-square::part(arrow-next) { border-radius: 6px; width: 46px; height: 46px; } #theme-square::part(pager) { gap: 10px; }
Add an image, the slider adapts
The component watches its own children. Appending an image adds a slide, removes nothing, and leaves the current position alone — no re-init call needed. Use the button to grow the gallery.
Attributes
| Attribute | Values | Notes |
|---|---|---|
animation | slide, fade | How slides change. Default slide. |
duration | ms | Transition length. Default 700. |
easing | any CSS timing function | Default ease. |
autoplay | present | Starts a slideshow on load. |
interval | ms | Time between slides. Default 5000. |
pause-on-hover | present | Pauses while the pointer is over it. |
nav | present | Shows the previous/next arrows. |
pager | present | Shows the dot or thumbnail pager. |
thumbs | present | Pager renders thumbnails instead of dots. |
slide-show-at | index, 0-based | Slide to start on. |
direction | horizontal, vertical | Axis the track moves along. |
reverse | present | Animates the other way. |
rtl | present | Mirrors arrows and layout. |
swipe | on, off | Drag and swipe. On by default. |
keyboard | on, off | Arrow keys, Home and End. On by default. |
smooth-height | present | Animates the height between slides. |
height | CSS length | Fixed box; images crop with object-fit: cover. |
theme | light, dark | Arrow and pager colours. |
loop | on, off | Wraps by default; off stops at the ends. |
label | string | Accessible name for the region. |
prev-label, next-label | string | Accessible names for the arrows. |
Methods & events
// methods slider.next(); slider.prev(); slider.goTo(2); slider.play(); slider.pause(); slider.refresh(); // re-read the images slider.reset(); slider.currentIndex = 0; // properties slider.currentIndex; // get and set slider.slides; // the original <img> elements // events — both carry { index, slide, count } and cross the shadow boundary slider.addEventListener('slide-start', (e) => console.log(e.detail.index)); slider.addEventListener('slide-end', (e) => console.log(e.detail.slide));
Install
# one file, no dependencies curl -O https://squidjam.github.io/FlexSlider/flex-slider.js # or clone the repo and copy it in git clone https://github.com/squidjam/FlexSlider.git
Where to go next
Everything this page demonstrates is written up, testable, or readable in source. The component is small enough to hold in your head — about 600 lines, no dependencies.
window.__flexSliderTests.
No jQuery, no polyfills, no bundler. One module a browser loads directly.
Slide widths are measured, not assumed, so any image size and container width works.
Labelled region, slide positions, named controls, keyboard support and no duplicate announcements.