Skip to content

Flip Cards

Beginner

Two-sided cards with flip animations. Click to flip, swipe to approve/reject.

Demo

Source
vue
<script lang="ts" setup>
import { ref } from 'vue'
import { FlashCards, FlipCard } from 'vue3-flashcards'
import AnswerCard from './AnswerCard.vue'
import QuestionCard from './QuestionCard.vue'

const cards = ref([
  { text: 'What is the capital of France?', back: 'Paris', difficulty: 'Easy', category: 'Geography' },
  { text: 'What is 15 × 8?', back: '120', difficulty: 'Medium', category: 'Mathematics' },
  { text: 'Who wrote "Romeo and Juliet"?', back: 'William Shakespeare', difficulty: 'Easy', category: 'Literature' },
])

const waitAnimationEnd = ref(true)
const invertAxios = ref<false>(false)
</script>

<template>
  <div class="w-full flex flex-col justify-center items-center py-20">
    <div class="mb-8 bg-white dark:bg-gray-800 p-4 rounded-xl shadow-md">
      <label class="flex items-center gap-3 cursor-pointer mb-1">
        <input
          v-model="waitAnimationEnd"
          type="checkbox"
          class="toggle toggle-primary"
        >
        <span class="font-medium text-gray-700 dark:text-gray-300">Wait for animation end</span>
      </label>
      <label class="flex items-center gap-3 cursor-pointer">
        <input
          v-model="invertAxios"
          type="checkbox"
          class="toggle toggle-primary"
        >
        <span class="font-medium text-gray-700 dark:text-gray-300">Invert axis</span>
      </label>
    </div>

    <div class="max-w-sm w-full">
      <FlashCards :items="cards">
        <template #default="{ item }">
          <FlipCard :wait-animation-end="waitAnimationEnd" :flip-axis="invertAxios ? 'x' : 'y'">
            <template #front>
              <QuestionCard :item="item" />
            </template>
            <template #back>
              <AnswerCard :item="item" />
            </template>
          </FlipCard>
        </template>
      </FlashCards>
    </div>
  </div>
</template>
vue
<script setup lang="ts">
interface QuizItem {
  text: string
  back: string
  difficulty: string
  category: string
}

defineProps<{
  item: QuizItem
}>()
</script>

<template>
  <div class="relative overflow-hidden rounded-2xl shadow-2xl h-72 bg-gradient-to-br from-blue-500 to-purple-600 text-white transform transition-all duration-300">
    <!-- Question mark icon -->
    <div class="absolute top-4 right-4 w-8 h-8 bg-white/20 rounded-full flex items-center justify-center">
      <span class="text-white font-bold">?</span>
    </div>

    <!-- Category tag -->
    <div class="absolute top-4 left-4">
      <span class="px-3 py-1 bg-white/20 rounded-full text-sm font-medium">
        {{ item.category }}
      </span>
    </div>

    <!-- Question content -->
    <div class="p-6 h-full flex flex-col justify-center items-center text-center">
      <h2 class="text-lg font-bold mb-4 leading-relaxed">
        {{ item.text }}
      </h2>
      <div class="text-sm opacity-80">
        Tap to reveal answer
      </div>
    </div>

    <!-- Difficulty indicator -->
    <div class="absolute bottom-4 left-4">
      <div class="flex items-center gap-2">
        <div class="w-2 h-2 bg-white rounded-full" />
        <div :class="item.difficulty === 'Easy' ? 'w-2 h-2 bg-green-400 rounded-full' : 'w-2 h-2 bg-white/50 rounded-full'" />
        <div :class="item.difficulty === 'Medium' ? 'w-2 h-2 bg-yellow-400 rounded-full' : 'w-2 h-2 bg-white/50 rounded-full'" />
        <div :class="item.difficulty === 'Hard' ? 'w-2 h-2 bg-red-400 rounded-full' : 'w-2 h-2 bg-white/50 rounded-full'" />
      </div>
    </div>
  </div>
</template>
vue
<script setup lang="ts">
interface QuizItem {
  text: string
  back: string
  difficulty: string
  category: string
}

defineProps<{
  item: QuizItem
}>()
</script>

<template>
  <div class="relative overflow-hidden rounded-2xl shadow-2xl h-72 bg-gradient-to-br from-emerald-500 to-teal-600 text-white transform transition-all duration-300">
    <!-- Answer checkmark icon -->
    <div class="absolute top-4 right-4 w-8 h-8 bg-white/20 rounded-full flex items-center justify-center">
      <span class="text-white font-bold">✓</span>
    </div>

    <!-- Category tag -->
    <div class="absolute top-4 left-4">
      <span class="px-3 py-1 bg-white/20 rounded-full text-sm font-medium">
        {{ item.category }}
      </span>
    </div>

    <!-- Answer content -->
    <div class="p-6 h-full flex flex-col justify-center items-center text-center">
      <div class="text-sm opacity-80 mb-2">
        Answer:
      </div>
      <h2 class="text-2xl font-bold mb-4">
        {{ item.back }}
      </h2>
      <div class="text-sm opacity-80">
        Swipe to continue
      </div>
    </div>

    <!-- Difficulty indicator -->
    <div class="absolute bottom-4 left-4">
      <span class="px-2 py-1 bg-white/20 rounded-full text-xs">
        {{ item.difficulty }}
      </span>
    </div>
  </div>
</template>

Key Concepts

  • FlipCard component: Independent two-sided card
  • flip-axis="x": Horizontal flip animation
  • flip() method: Programmatically trigger flip
  • Perfect for: flashcards, quiz apps, reveal content

Released under the MIT License.