Skip to content

Getting Started

Terminal
npm install @rpxl/riffle

Riffle for Vue needs Vue 3.4.20 or later.

A complete single-file component. Pass it any array of objects with a title:

Quickstart.vue
<template>
<!-- justify-content keeps the stack's one grid column, and so every card, the
card's own width (centred), so the fan scales about the card itself. -->
<Riffle
aria-label="Films"
:cards="films"
:card-width="300"
:card-height="400"
style="justify-content: center"
>
<template #card="{ card, index }">
<div class="card" :style="{ background: `hsl(${index * 72} 65% 45%)` }">
{{ card.title }}
</div>
</template>
</Riffle>
</template>
<script setup lang="ts">
import { Riffle } from '@rpxl/riffle/vue'
interface Film {
title: string
}
defineProps<{ films: Film[] }>()
</script>
<style scoped>
.card {
width: 300px;
height: 400px;
border-radius: 16px;
color: white;
display: grid;
place-items: center;
}
</style>

That exact file, running. Drag the front card:

Neon Harbor
The Quiet Orbit
Salt and Static
Paper Moons
Last Train to Vela
Glasshouse
Northbound
Kite Season
  • <Riffle> renders one card per item in cards, from your #card slot, and stacks them in a single grid cell for the engine to move. The slot’s card is typed from cards.
  • Every Riffle option is a prop: card-width and card-height here. Other attributes, such as aria-label or style, fall through to the root element.
  • cardWidth and cardHeight describe each card’s geometry to the engine (how far a drag travels, how the fan is laid out) and do not size or style any element: the cards’ own styles give them their size, background and radius.
  • The engine marks the stack as a carousel but cannot name it: give it an accessible name with aria-label, as in aria-label="Films".

For your own markup instead of <Riffle>, useRiffle returns two directives, plus activeIndex, next, prev and goTo. Destructure the directives at the top level of <script setup> under exactly these names, so the template can see them, then put v-riffle-root on the container and v-riffle-card="index" on each card:

ScriptSetupStack.vue
const { vRiffleRoot, vRiffleCard, activeIndex, next, prev } = useRiffle({
count: films.length,
cardWidth: WIDTH,
cardHeight: HEIGHT,
getLabel,
})
  • Adapter details: how options update between renders, refs, the imperative handle, and server rendering.