contao-components/swipe
Adds touch-enabled swipe/slider support for Contao via a reusable component. Provides an easy way to integrate swipe gestures and responsive carousels into frontend templates, improving navigation on mobile and modern browsers with minimal setup.
Installation
composer require contao-components/swipe
npm install @contao-components/swipe
node_modules and public/js are properly linked in Laravel’s asset pipeline (e.g., via Laravel Mix or Vite).Basic Initialization
Add the CSS and JS to your layout (e.g., resources/js/app.js or resources/css/app.css):
import Swipe from '@contao-components/swipe';
import '@contao-components/swipe/dist/swipe.css';
<link rel="stylesheet" href="{{ mix('css/app.css') }}">
<script src="{{ mix('js/app.js') }}" defer></script>
First Use Case: Simple Slider
<div class="swipe-container">
<div class="swipe-slide">Slide 1</div>
<div class="swipe-slide">Slide 2</div>
<div class="swipe-slide">Slide 3</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
new Swipe('.swipe-container');
});
</script>
.swipe-container: Wrapper for the slider..swipe-slide: Individual slides.Pass options via the constructor or init() method:
new Swipe('.swipe-container', {
startSlide: 1, // Zero-based index (default: 0)
speed: 400, // Transition speed (ms)
auto: 3000, // Auto-advance interval (ms, set to `false` to disable)
continuous: true, // Infinite loop
disableScroll: true, // Disable touch/scroll on body
callback: (index) => {
console.log('Slide changed to:', index);
}
});
Access the slider instance via data-swipe attribute:
<div class="swipe-container" data-swipe></div>
const swipe = document.querySelector('[data-swipe]').swipe;
swipe.next(); // Advance to next slide
swipe.prev(); // Go back
swipe.goto(2); // Jump to slide 2
swipe.stop(); // Pause auto-advance
Add a dot menu dynamically:
new Swipe('.swipe-container', {
dotMenu: true,
dotClass: 'custom-dot', // Custom CSS class
dotActiveClass: 'active' // Active state class
});
<div class="dot-menu"></div>
.dot-menu element found.Use @stack or @push for dynamic initialization:
<div class="swipe-container" id="hero-slider">
@foreach($slides as $slide)
<div class="swipe-slide">{{ $slide->content }}</div>
@endforeach
</div>
@push('scripts')
<script>
document.addEventListener('DOMContentLoaded', () => {
new Swipe('#hero-slider', {
auto: {{ $autoAdvance ? $autoAdvance : 'false' }},
callback: (index) => {
@this->emit('slide-changed', index);
}
});
});
</script>
@endpush
Bind to swipe events:
const swipe = new Swipe('.swipe-container');
swipe.on('slideChange', (index) => {
console.log('Slide changed to:', index);
});
swipe.on('beforeSlideChange', (oldIndex, newIndex) => {
console.log(`Transitioning from ${oldIndex} to ${newIndex}`);
});
CSS Conflicts
.swipe-slide has width: 100% and float: left (or display: flex)..swipe-container .swipe-slide {
transition: none !important;
}
Auto-Advance Timing
auto: 3000 may feel jarring; test with auto: 5000 or disable entirely for static sliders.Mobile Touch Delays
touch-action: pan-y to .swipe-container to prevent OS-level touch delays:
.swipe-container {
touch-action: pan-y;
}
Laravel Mix/Vite Caching
npm run dev -- --clear
Check Initialization:
console.log(document.querySelector('.swipe-container').swipe);
Swipe instance. If undefined, the element wasn’t found or the script ran too early.Event Debugging:
swipe.on('error', (err) => {
console.error('Swipe error:', err);
});
Custom Transitions
Override the default slide animation by extending the Swipe class:
class FadeSwipe extends Swipe {
slide(index) {
this.slides.forEach((slide, i) => {
slide.style.opacity = i === index ? 1 : 0;
});
}
}
new FadeSwipe('.swipe-container');
Laravel Service Provider
Register a global Swipe helper:
// app/Providers/AppServiceProvider.php
use ContaoComponents\Swipe\Swipe;
public function boot() {
if (!function_exists('swipe')) {
function swipe($selector, $options = []) {
return new Swipe($selector, $options);
}
}
}
Usage in Blade:
@swipe('#slider', ['auto' => 2000])
Server-Side Slide Data Fetch slides via AJAX and update dynamically:
fetch('/api/slides')
.then(res => res.json())
.then(slides => {
const container = document.querySelector('.swipe-container');
container.innerHTML = slides.map(slide => `<div class="swipe-slide">${slide.content}</div>`).join('');
new Swipe(container);
});
let resizeTimeout;
window.addEventListener('resize', () => {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(() => {
new Swipe('.swipe-container');
}, 200);
});
How can I help you explore Laravel packages today?