Swiper
Swiper is a touch-friendly horizontal scroll component. It shares the same responsive max-width system as .container and adds drag-to-scroll behavior powered by the Pointer Events API — no third-party library needed.
Use Swiper for:
- Product and media card carousels
- Horizontally scrollable tag or filter rows
- Image galleries and photo strips
- News and article feed sliders
- Step indicators and onboarding flows
- Any overflowing horizontal list
FrontAlign Swiper combines a CSS layout structure with a JavaScript drag engine. The visual layer is provided by
.swiper,.swiper-wrapper, and.swiper-itemclasses, while the JavaScript layer handles pointer events, click guarding, scroll multiplier, and disposal.
Getting Started
Swiper does not need to be initialized manually. FrontAlign's Smart Observer detects .swiper[fa-component] elements in the DOM and activates them automatically.
<div className="swiper" fa-component="swiper">
<div className="swiper-wrapper">
<div className="swiper-item w-2fr3 p-4 bg-blue">
<div className="card">Card 1</div>
</div>
<div className="swiper-item w-2fr3 p-4 bg-indigo">
<div className="card">Card 2</div>
</div>
<div className="swiper-item w-2fr3 p-4 bg-success">
<div className="card">Card 3</div>
</div>
</div>
</div>
No additional script is required when the Smart Observer is active — the swiper initializes as soon as the element enters the DOM.
Swiper requires three nested elements: the outer .swiper wrapper, a .swiper-wrapper scroll track, and one or more .swiper-item children.
Elements
.swiper
The outer wrapper. Clips overflowing items with overflow: hidden and shares the container's responsive max-width and margin-inline: auto centering. Receives the .is-dragging class during active drag.
.swiper-wrapper
The horizontal scroll track. Uses display: flex with flex-wrap: nowrap to keep all items on a single row. The native scrollbar is hidden on all browsers for a clean appearance. -webkit-overflow-scrolling: touch enables momentum scrolling on iOS.
.swiper-item
Individual slides or cards. Uses flex: 0 0 auto so items never grow or shrink — they always render at their natural or explicitly defined width. scroll-snap-align: start snaps items to the leading edge during scroll.
If the .swiper-wrapper is not found inside the element, the initializer will not work — it will not throw.
Drag Behavior
Drag is powered by the Pointer Events API, which handles both mouse and touch in a single code path.
Lifecycle
| Event | What happens |
|---|---|
pointerdown | Records start position and scrollLeft. Sets pointer capture so drag continues even if cursor leaves the element. Adds .is-dragging to the outer wrapper. |
pointermove | Calculates delta from start X, applies a 1.15 multiplier for a natural drag feel, and updates scrollLeft in real time. |
pointerup / pointercancel / pointerleave | Ends the drag, releases pointer capture, removes .is-dragging. |
Drag Threshold & Click Guard
A 3px movement threshold distinguishes intentional drags from accidental clicks.
| Movement | Result |
|---|---|
| Less than 3px | Treated as a regular click — scroll is not applied. |
| 3px or more | Scroll is applied. The next click event on any child is suppressed. |
When the threshold is exceeded, a once capture listener is attached to the wrapper that calls preventDefault() and stopImmediatePropagation() on the next click event. This prevents unintended navigation or button activations at the end of a drag.
.is-dragging
While dragging, .is-dragging is added to the .swiper element. Use it to control cursor appearance or disable pointer events on interactive children.
.swiper.is-dragging {
cursor: grabbing;
}
.swiper.is-dragging a,
.swiper.is-dragging button {
pointer-events: none;
}
Javascript Runtime
For the swiper to initialize, add the fa-component="swiper" attribute to the swiper element and call new FrontAlign() once on the page. The Smart Observer automatically discovers and initializes supported components. When faApp.dispose() is called, all observers, event listeners, and component instances managed by that FrontAlign runtime are fully cleaned up.
React Usage
For React and Next.js projects, use the useSwiper hook instead of initializing manually. The hook attaches fa-component="swiper" behavior to the ref element and disposes on unmount.
new FrontAlign() and Smart Observer are not used in React — the hook manages the DOM ref directly.
import { useSwiper } from 'frontalign/react';
useSwiper returns a single ref to attach to the .swiper root element.
| Return value | Type | Description |
|---|---|---|
ref | React.RefObject | Attach to the .swiper root element via ref=. Initialization runs after mount; disposal runs on unmount via swiperDispose(). |
Basic usage
import { useSwiper } from 'frontalign/react';
export function ProductCarousel() {
const ref = useSwiper();
return (
<div className="swiper" ref={ref}>
<div className="swiper-wrapper">
<div className="swiper-item" style={{ width: 260 }}>Card 1</div>
<div className="swiper-item" style={{ width: 260 }}>Card 2</div>
<div className="swiper-item" style={{ width: 260 }}>Card 3</div>
</div>
</div>
);
}
Dynamically rendered items
The hook initializes once on mount. If items are rendered from async data, render the outer structure immediately so the hook has a stable ref to attach to.
import { useSwiper } from 'frontalign/react';
export function FeedCarousel({ posts }: { posts: Post[] }) {
const ref = useSwiper();
return (
<div className="swiper" ref={ref}>
<div className="swiper-wrapper">
{posts.map(post => (
<div key={post.id} className="swiper-item" style={{ width: 280 }}>
<div className="card">
<img src={post.thumbnail} alt={post.title} />
<h3>{post.title}</h3>
</div>
</div>
))}
</div>
</div>
);
}
With conditional rendering
When the swiper is conditionally mounted, the hook's cleanup disposes the instance automatically on unmount — no manual swiperDispose() call is needed.
import { useState } from 'react';
import { useSwiper } from 'frontalign/react';
export function GallerySection({ visible }: { visible: boolean }) {
const ref = useSwiper();
if (!visible) return null;
return (
<div className="swiper" ref={ref}>
<div className="swiper-wrapper">
<div className="swiper-item w-2fr3 p-4 bg-blue">
<div className="card">Card 1</div>
</div>
<div className="swiper-item w-2fr3 p-4 bg-indigo">
<div className="card">Card 2</div>
</div>
<div className="swiper-item w-2fr3 p-4 bg-success">
<div className="card">Card 3</div>
</div>
</div>
</div>
);
}
Continue with the React Integration guide for provider setup, hooks, and Next.js usage.
Accessibility
Swiper does not manage ARIA attributes automatically — apply them manually for better screen reader support.
Recommended practices:
- Wrap the swiper in a
role="region"with anaria-labeldescribing the carousel content. - Add
aria-labelto each.swiper-itemor its primary child so screen readers can identify individual slides. - Do not rely on drag as the only navigation method — ensure interactive children (buttons, links) are keyboard accessible.
- Avoid placing focusable elements inside items that become unreachable when overflowed out of view.
<section role="region" aria-label="Featured products">
<div class="swiper" fa-component="swiper">
<div class="swiper-wrapper">
<div class="swiper-item" style="width: 260px;" aria-label="Product 1">
<div class="card">Card 1</div>
</div>
<div class="swiper-item" style="width: 260px;" aria-label="Product 2">
<div class="card">Card 2</div>
</div>
</div>
</div>
</section>
Class Reference
| Class | Role |
|---|---|
.swiper | Outer wrapper. Clips overflow and provides responsive max-width. |
.swiper-wrapper | Flex scroll track. Hides scrollbar, enables touch momentum, sets item gap. |
.swiper-item | Individual slide. Fixed size via flex: 0 0 auto, snaps to start on scroll. |
.is-dragging | Applied to .swiper during active drag. Use to change cursor or disable child interactions. |