TranslatedScroll Component
The TranslatedScroll component creates a horizontally scrollable container with smooth CSS transform-based scrolling. It supports mouse wheel scrolling, touch/mouse dragging, and an optional custom scrollbar.
Features
- Transform-based Scrolling: Uses CSS
translateXfor smooth, GPU-accelerated horizontal scrolling - Mouse Wheel Support: Scroll horizontally using mouse wheel or trackpad
- Touch & Mouse Drag: Drag content horizontally on touch devices or with mouse
- Custom Scrollbar: Optional draggable scrollbar with automatic visibility
- Responsive: Automatically adapts to container size changes using ResizeObserver
- Overflow Detection: Scrollbar only appears when content exceeds container width
Configuration
export interface TranslatedScrollOptions {
innerWrapperSelector?: string; // Selector for the scrolling content wrapper
showScrollbar?: boolean; // Show custom scrollbar (default: true)
enableMouseDrag?: boolean; // Enable mouse drag on desktop (default: false)
}Basic Usage
Minimal Setup
<div x-data="TranslatedScroll()">
<div class="content">
<!-- Wide content that needs horizontal scrolling -->
<div class="item">Item 1</div>
<div class="item">Item 2</div>
<div class="item">Item 3</div>
<!-- ... more items -->
</div>
</div>With Options
<div x-data="TranslatedScroll({
innerWrapperSelector: '.scroll-content',
showScrollbar: true,
enableMouseDrag: true
})">
<div class="scroll-content">
<!-- Your scrollable content -->
</div>
</div>Statamic Template Example
The package includes a reference implementation in the form of a faded scroll container:
{{# resources/views/partials/_faded_translated_scroll.antlers.html #}}
<div class="faded-translated-scroll {{ class ?? '' }}"
x-data="TranslatedScroll({innerWrapperSelector: '.faded-translated-scroll-content'})">
<div class="faded-translated-scroll-background">
<div class="faded-translated-scroll-content">
{{ slot }}
</div>
</div>
</div>Usage in Statamic templates:
{{ partial:partials/faded_translated_scroll class="my-scroll-container" }}
<div class="items">
{{ collection:items }}
<div class="item">{{ title }}</div>
{{ /collection:items }}
</div>
{{ /partial:partials/faded_translated_scroll }}How It Works
CSS Transform Scrolling
The component uses a CSS variable --scroll-x to control horizontal positioning via transform: translateX():
.a-translated-scroll-inner-wrapper {
transform: translateX(var(--scroll-x));
}This provides smooth, GPU-accelerated scrolling without relying on the browser's native overflow scrolling.
Inner Wrapper Selector
The component needs to know which element contains the scrollable content:
- Default: Uses the first child element of the component root
- Custom: Specify
innerWrapperSelectorto target a specific nested element
<!-- Default: first child is used -->
<div x-data="TranslatedScroll()">
<div><!-- This will be the scrolling wrapper --></div>
</div>
<!-- Custom: specific selector -->
<div x-data="TranslatedScroll({innerWrapperSelector: '.content'})">
<div class="background">
<div class="content"><!-- This will be the scrolling wrapper --></div>
</div>
</div>Automatic Resize Handling
The component uses ResizeObserver to watch both the container and inner wrapper for size changes. When dimensions change, it recalculates the maximum scroll width and updates the scrollbar accordingly.
Interaction Methods
Mouse Wheel Scrolling
Horizontal scrolling is triggered by vertical mouse wheel movement. The component prevents the default vertical scroll behavior and translates it to horizontal movement.
Touch Dragging
On touch devices, users can drag the content horizontally. The component tracks touch events and updates the scroll position in real-time.
Mouse Dragging
When enableMouseDrag: true, desktop users can click and drag the content. The cursor changes to grab when hovering and grabbing while dragging.
Scrollbar Dragging
The custom scrollbar can be clicked and dragged to navigate through the content. The scrollbar width is proportional to the visible content area.
Styling
The component applies CSS classes automatically:
Container Classes
.a-translated-scroll- Applied to the root element.is-draggable- Added whenenableMouseDragis enabled.is-dragging- Added during active drag operation
Inner Wrapper Classes
.a-translated-scroll-inner-wrapper- Applied to the scrolling content wrapper
Scrollbar Classes
.a-translated-scroll-scrollbar- The custom scrollbar element.is-draggable- Added when content overflows and scrollbar is usable.is-dragging- Added during active scrollbar drag
Customizing Scrollbar
Override the default scrollbar styles:
.a-translated-scroll-scrollbar {
height: 8px;
background: rgba(0, 0, 0, 0.3);
border-radius: 8px;
}
.a-translated-scroll-scrollbar:hover,
.a-translated-scroll-scrollbar.is-dragging {
background: rgba(0, 0, 0, 0.5);
}Example: Faded Edges
Create a container with faded edges to indicate scrollable content:
.faded-translated-scroll {
position: relative;
overflow: hidden;
}
.faded-translated-scroll-background {
position: relative;
&::before,
&::after {
content: '';
position: absolute;
top: 0;
bottom: 0;
width: 50px;
pointer-events: none;
z-index: 1;
}
&::before {
left: 0;
background: linear-gradient(to right, white, transparent);
}
&::after {
right: 0;
background: linear-gradient(to left, white, transparent);
}
}
.faded-translated-scroll-content {
display: flex;
gap: 20px;
padding: 20px 0;
}Browser Compatibility
The component uses modern browser APIs:
- ResizeObserver: Supported in all modern browsers
- CSS Custom Properties: Supported in all modern browsers
- Touch Events: Supported on touch devices
- Transform: Hardware-accelerated in all modern browsers
Use Cases
- Image Galleries: Horizontal scrolling image carousels
- Product Lists: Scrollable product showcases
- Timeline Components: Horizontal timeline navigation
- Data Tables: Wide tables with horizontal scrolling
- Card Layouts: Scrollable card decks
- Tag Lists: Overflow tag collections
Performance
The component is optimized for performance:
- GPU Acceleration: Uses CSS transforms for smooth scrolling
- Debounced Updates: Resize calculations are debounced with
setTimeout - Passive Event Listeners: Uses passive listeners where possible
- Minimal DOM Updates: Only updates necessary CSS properties