opensourceui
GitHubTwitter/X
  1. Home
  2. Components
  3. Gallery
  4. Museum Placard
Back to Gallery

Components / Gallery

Museum Placard

Free Gallery React component — MuseumPlacardCard. MIT licensed, copy-paste ready for Next.js and Tailwind CSS.

Gallery exhibition label that flips to reveal curator notes and acquisition details. Art portfolios and culture sites love this interaction.

Preview

Setup

How to use

Free for personal and commercial use. No UI attribution required. Include the MIT copyright notice when copying component source into your project. Read the MIT license.

  1. 1

    Run in your terminal:

    $npm install clsx tailwind-merge lucide-react
  2. 2

    Copy lib/cn.ts below. Skip if you already have cn().

  3. 3

    Copy the code below and create components/gallery/museum-placard-card.tsx in your project.

  4. 4

    Import and render:

    Example

    import { MuseumPlacardCard } from "@/components/gallery/museum-placard-card";

    <MuseumPlacardCard artist="Name" title="Artwork title" year="2024" />

lib/cn.ts

1
2
3
4
5
6
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
 
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}

components/gallery/museum-placard-card.tsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
"use client";
 
import { forwardRef, useState, type ComponentPropsWithoutRef } from "react";
 
import { cn } from "@/lib/cn";
 
export type MuseumPlacardCardProps = Readonly<
{
artist?: string;
title?: string;
year?: string;
medium?: string;
dimensions?: string;
catalogRef?: string;
curatorNote?: string;
acquisition?: string;
} & ComponentPropsWithoutRef<"div">
>;
 
// Production-ready Museum Placard component — styled with Tailwind CSS.
export const MuseumPlacardCard = forwardRef<
HTMLDivElement,
MuseumPlacardCardProps
>(
(
{
className,
artist = "Elena Marchetti",
title = "Still Life with Winter Light",
year = "2019",
medium = "Oil on linen",
dimensions = "61 × 76 cm",
catalogRef = "INV.2024.118",
curatorNote = "Marchetti layers translucent glazes to suspend domestic objects between memory and observation.",
acquisition = "Gift of the Hartwell Foundation, 2024",
...props
},
ref,
) => {
const [flipped, setFlipped] = useState(false);
 
return (
<div
ref={ref}
data-slot="museum-placard-card"
className={cn("mx-auto w-72 font-sans perspective-[1000px]", className)}
{...props}
>
<button
type="button"
onClick={() => setFlipped((value) => !value)}
aria-pressed={flipped}
aria-label={flipped ? "Show artwork label" : "Show curator note"}
className="group relative h-52 w-full cursor-pointer border-0 bg-transparent p-0 text-left"
>
<div
className={cn(
"relative h-52 w-full transition-transform duration-500 [transform-style:preserve-3d]",
flipped && "[transform:rotateY(180deg)]",
)}
>
<div className="absolute inset-0 rounded-sm border border-neutral-200 bg-[#faf8f5] px-5 py-4 shadow-sm [backface-visibility:hidden]">
<p className="font-mono text-[9px] tracking-[0.22em] text-neutral-400 uppercase">
{catalogRef}
</p>
<p className="mt-3 text-sm font-medium text-neutral-800">
{artist}
</p>
<p className="mt-1 font-serif text-base leading-snug text-neutral-700 italic">
{title}
</p>
<p className="mt-1 text-xs text-neutral-500">{year}</p>
 
<div className="mt-4 space-y-1 border-t border-neutral-200 pt-3">
<p className="text-[11px] text-neutral-600">{medium}</p>
<p className="text-[11px] text-neutral-500">{dimensions}</p>
</div>
 
<p className="absolute right-4 bottom-3 font-mono text-[8px] tracking-wider text-neutral-400 uppercase opacity-0 transition-opacity group-hover:opacity-100">
Tap for note
</p>
</div>
 
<div className="absolute inset-0 [transform:rotateY(180deg)] rounded-sm border border-slate-300 bg-[#e8edf3] px-5 py-4 shadow-sm [backface-visibility:hidden]">
<p className="font-mono text-[9px] tracking-[0.22em] text-slate-500 uppercase">
Curator note
</p>
<p className="mt-3 text-[12px] leading-relaxed text-slate-700">
{curatorNote}
</p>
<p className="mt-4 border-t border-slate-300/80 pt-3 text-[10px] leading-relaxed text-slate-600">
{acquisition}
</p>
<p className="absolute right-4 bottom-3 font-mono text-[8px] tracking-wider text-slate-400 uppercase">
Tap to return
</p>
</div>
</div>
</button>
</div>
);
},
);
 
MuseumPlacardCard.displayName = "MuseumPlacardCard";
PreviousNext

On this

page

Jump to a section on this page.

  • lib/cn.ts
  • components/gallery/museum-placard-card.tsx

Sponsor spot · demo preview

Logo

Your brand name

Your tagline or category

A short pitch about your product or service goes here. This is a preview of how sponsor cards will look in this sidebar.

Your call to action

This slot is available. Sponsor Opensource UI and reach developers browsing components every day.