d-ui logod-ui
カード

Flip Card

ホバーまたはクリックで表裏がフリップするカードコンポーネント。

インストール

npx shadcn add https://d-ui.daigo-suhara.com/registry/flip-card.json

サンプル

ホバーしてね
裏面が見えました!

使い方

import { FlipCard } from "@/components/ui/flip-card"

export default function Example() {
  return (
    <FlipCard
      height="180px"
      className="w-64"
      front={
        <div className="flex h-full items-center justify-center rounded-xl bg-primary text-primary-foreground text-lg font-bold">
          表面
        </div>
      }
      back={
        <div className="flex h-full items-center justify-center rounded-xl bg-muted text-foreground text-sm p-4 text-center">
          裏面のコンテンツがここに表示されます。
        </div>
      }
    />
  )
}

プロパティ

front必須
ReactNode

表面のコンテンツ

back必須
ReactNode

裏面のコンテンツ

trigger任意
"hover" | "click"

フリップのトリガー

デフォルト:"hover"
direction任意
"horizontal" | "vertical"

フリップの方向

デフォルト:"horizontal"
height任意
string

カードの高さ

デフォルト:"200px"
className必須
string

追加のCSSクラス

ソースコード

"use client";

import * as React from "react";
import { cn } from "@/lib/utils";

interface FlipCardProps {
  front: React.ReactNode;
  back: React.ReactNode;
  trigger?: "hover" | "click";
  direction?: "horizontal" | "vertical";
  className?: string;
  height?: string;
}

export function FlipCard({
  front,
  back,
  trigger = "hover",
  direction = "horizontal",
  className,
  height = "200px",
}: FlipCardProps) {
  const [flipped, setFlipped] = React.useState(false);
  const isHover = trigger === "hover";
  const isVertical = direction === "vertical";

  return (
    <div
      className={cn("relative cursor-pointer", className)}
      style={{ height, perspective: "1000px" }}
      onClick={!isHover ? () => setFlipped((f) => !f) : undefined}
      onMouseEnter={isHover ? () => setFlipped(true) : undefined}
      onMouseLeave={isHover ? () => setFlipped(false) : undefined}
    >
      <div
        className="relative w-full h-full transition-transform duration-500"
        style={{
          transformStyle: "preserve-3d",
          transform: flipped
            ? isVertical
              ? "rotateX(180deg)"
              : "rotateY(180deg)"
            : "rotateX(0deg) rotateY(0deg)",
        }}
      >
        {/* Front */}
        <div
          className="absolute inset-0 rounded-xl overflow-hidden"
          style={{ backfaceVisibility: "hidden" }}
        >
          {front}
        </div>
        {/* Back */}
        <div
          className="absolute inset-0 rounded-xl overflow-hidden"
          style={{
            backfaceVisibility: "hidden",
            transform: isVertical ? "rotateX(180deg)" : "rotateY(180deg)",
          }}
        >
          {back}
        </div>
      </div>
    </div>
  );
}