Building a Production-Ready Card Swiper Component: A Vue 3 & TypeScript Case Study

01.The Challenge
At Betway Africa, a teammate was building a "swipe bet" feature—users swipe cards left or right to make betting decisions. The interaction needed to feel smooth, work on touch and desktop, and handle edge cases.
The initial attempts hit common issues:
- Janky animations that didn't feel natural
- Touch events conflicting with mouse events
- Cards not stacking correctly
- No way to undo actions
- Accessibility gaps
Rather than debug line-by-line, I built a complete, reusable component as a reference. This let us see the full pattern, understand the decisions, and adapt it to the betting feature.


02. The Solution
I built Tallman Swiper—a production-ready card swiper component that demonstrates how to build this interaction pattern correctly.
Architecture First
I used Vue 3's Composition API with a composable architecture. Each piece of functionality lives in its own module:
swiperCore.ts— Core swipe logic and decision handlingswiperTouchEvents.ts— Unified touch and mouse event handlingswiperTransitions.ts— Animation and transition managementswiperQueue.ts— Card queue management and diffingswiperState.ts— Centralized state initialization
This separation makes the code easier to understand, test, and maintain. Each composable has a single responsibility.
The Multi-Input Problem
One challenge was supporting touch, mouse, and keyboard without conflicts. The solution was a unified event handler that normalizes inputs:
1const start = (e: TouchEvent | MouseEvent) => {2 let pageX, pageY3 if (e.type === 'touchstart') {4 pageX = (e as TouchEvent).changedTouches[0].pageX5 pageY = (e as TouchEvent).changedTouches[0].pageY6 } else {7 pageX = (e as MouseEvent).clientX8 pageY = (e as MouseEvent).clientY9 }10 // ... unified handling11}
This keeps the logic consistent across input methods.
Smooth Animations
Animations use CSS transforms with hardware acceleration and configurable easing. All magic numbers live in animationConstants.ts:
- Fast animations (300ms) for manual swipes
- Normal transitions (500ms) for standard actions
- Slower animations (800ms) for programmatic actions like undo
The cubic-bezier easing function creates natural motion that feels responsive.
TypeScript Throughout
Everything is typed. No @ts-ignore comments. Types are centralized in types.ts, making the API clear and catching errors early.
03. Key Features
Multi-Input Support
- Touch gestures on mobile
- Mouse drag on desktop
- Full keyboard navigation (arrow keys, Ctrl+Z for undo)
Flexible Swipe Actions
- Left swipe (reject)
- Right swipe (like)
- Up swipe (super like)
- Down swipe (optional)
- Undo/rewind functionality
Accessibility
- ARIA labels for screen readers
- Keyboard navigation
- Focus management
- Semantic HTML
Performance
- Hardware-accelerated transforms
- Efficient state management with Vue reactivity
- Proper cleanup to prevent memory leaks
- Optimized event handling
04. Technical Highlights
Composable Architecture
The component uses Vue 3 composables, making logic reusable and testable. Each composable handles one concern, so you can understand and modify parts independently.
State Management
State is managed through Vue's reactivity system with computed properties for derived values. The state machine tracks:
- Normal (idle)
- Moving (user dragging)
- Leaving (card animating out)
- Rewinding (undo in progress)
This prevents race conditions and ensures smooth transitions.
Queue Management
Cards are managed in a queue that automatically refills when running low. The diffing algorithm efficiently updates the visible stack, minimizing DOM operations.
Animation System
Animations use CSS transforms (translate3d, rotate) for GPU acceleration. Transitions are configurable, and the system handles edge cases like rapid swipes and undo operations.
05. The Result
The component is:
- Production-ready
- Fully typed with TypeScript
- Accessible
- Performant
- Well-documented
- Easy to customize
It served as a reference for the swipe bet feature and can be adapted for other use cases.
06. Lessons Learned
- Build a complete example, not just snippets. It shows the full pattern and decisions.
- Separate concerns. Composables make code easier to understand and maintain.
- Type everything. TypeScript catches errors early and documents the API.
- Accessibility from the start. Keyboard navigation and ARIA labels are essential.
- Performance matters. Hardware acceleration and efficient state management keep interactions smooth.
07. Conclusion
This project shows how to build a complex, interactive component with Vue 3 and TypeScript. By focusing on architecture, type safety, and user experience, we created something that works well and is maintainable.
Whether you're building a swipe bet feature, a dating app interface, or any card-based interaction, the patterns here provide a solid foundation. The code is open source and available for reference.
If you're working on similar interactions, feel free to use this as a starting point. Sometimes the best way to help a teammate is to show them the complete picture, not just point out what's wrong.