Polaroid
const css = `
.card-polaroid {
width: 168px;
padding: 10px 10px 36px;
border-radius: 4px;
background: light-dark(#42454c, #e5e6e9);
box-shadow: 0 14px 28px -12px light-dark(rgba(0, 0, 0, 0.18), rgba(0, 0, 0, 0.6)), 0 2px 4px light-dark(rgba(0, 0, 0, 0.08), rgba(0, 0, 0, 0.25));
transform: rotate(-3deg);
transition: transform 0.5s cubic-bezier(0.34, 1.4, 0.64, 1);
cursor: pointer;
position: relative;
}
.card-polaroid:hover, .card-polaroid:focus-visible { transform: rotate(1deg) translateY(-4px); }
.card-polaroid:focus-visible { outline: 2px solid light-dark(#42454c, #e5e6e9); outline-offset: 4px; }
.card-polaroid__photo {
position: relative;
height: 132px;
overflow: hidden;
background: linear-gradient(light-dark(#6d7179, #7d8087), light-dark(#ebedf0, #232529) 60%, light-dark(#f3f4f7, #1a1c1f));
}
.card-polaroid__scene { position: absolute; inset: 0; filter: blur(9px) contrast(0.5); transition: filter 2.4s cubic-bezier(0.22, 1, 0.36, 1); }
.card-polaroid__scene::before {
content: '';
position: absolute;
left: 58%;
top: 26px;
width: 32px;
height: 32px;
border-radius: 50%;
background: light-dark(#373a40, #f1f2f3);
box-shadow: 0 0 24px 6px color-mix(in oklab, light-dark(#373a40, #f1f2f3) 35%, transparent);
}
.card-polaroid__scene::after {
content: '';
position: absolute;
left: -10%;
right: -10%;
bottom: 0;
height: 64px;
background: light-dark(#fbfcfd, #121417);
clip-path: polygon(0 100%, 0 60%, 14% 40%, 30% 62%, 48% 20%, 64% 54%, 80% 34%, 100% 58%, 100% 100%);
}
/* The undeveloped film: a milky layer that clears as the image comes up. */
.card-polaroid__film {
position: absolute;
inset: 0;
background: light-dark(#ebedf0, #232529);
transition: opacity 2.4s cubic-bezier(0.4, 0, 0.2, 1);
}
.card-polaroid:hover .card-polaroid__scene, .card-polaroid:focus-visible .card-polaroid__scene { filter: none; }
.card-polaroid:hover .card-polaroid__film, .card-polaroid:focus-visible .card-polaroid__film { opacity: 0; }
.card-polaroid__caption {
position: absolute;
left: 14px;
right: 14px;
bottom: 10px;
color: light-dark(#ebedf0, #232529);
font-family: 'Segoe Print', 'Bradley Hand', 'Comic Sans MS', cursive;
font-size: 13px;
transform: rotate(-1deg);
}
@media (prefers-reduced-motion: reduce) {
.card-polaroid, .card-polaroid__scene, .card-polaroid__film { transition-duration: 0.01s; }
}
`;
export function PolaroidCard() {
return (
<>
<style>
{css}
</style>
<div className="card-polaroid" tabIndex={0} role="img" aria-label="Instant photo: sunset over the ridge, summer 2026">
<div className="card-polaroid__photo" aria-hidden="true">
<div className="card-polaroid__scene" />
<div className="card-polaroid__film" />
</div>
<span className="card-polaroid__caption" aria-hidden="true">
the ridge, summer '26
</span>
</div>
</>
);
}Markup where the component goes, CSS in your stylesheet. No JavaScript.
<div class="card-polaroid" tabindex="0" role="img" aria-label="Instant photo: sunset over the ridge, summer 2026">
<div class="card-polaroid__photo" aria-hidden="true">
<div class="card-polaroid__scene"></div>
<div class="card-polaroid__film"></div>
</div>
<span class="card-polaroid__caption" aria-hidden="true">the ridge, summer '26</span>
</div>.card-polaroid {
width: 168px;
padding: 10px 10px 36px;
border-radius: 4px;
background: light-dark(#42454c, #e5e6e9);
box-shadow: 0 14px 28px -12px light-dark(rgba(0, 0, 0, 0.18), rgba(0, 0, 0, 0.6)), 0 2px 4px light-dark(rgba(0, 0, 0, 0.08), rgba(0, 0, 0, 0.25));
transform: rotate(-3deg);
transition: transform 0.5s cubic-bezier(0.34, 1.4, 0.64, 1);
cursor: pointer;
position: relative;
}
.card-polaroid:hover, .card-polaroid:focus-visible { transform: rotate(1deg) translateY(-4px); }
.card-polaroid:focus-visible { outline: 2px solid light-dark(#42454c, #e5e6e9); outline-offset: 4px; }
.card-polaroid__photo {
position: relative;
height: 132px;
overflow: hidden;
background: linear-gradient(light-dark(#6d7179, #7d8087), light-dark(#ebedf0, #232529) 60%, light-dark(#f3f4f7, #1a1c1f));
}
.card-polaroid__scene { position: absolute; inset: 0; filter: blur(9px) contrast(0.5); transition: filter 2.4s cubic-bezier(0.22, 1, 0.36, 1); }
.card-polaroid__scene::before {
content: '';
position: absolute;
left: 58%;
top: 26px;
width: 32px;
height: 32px;
border-radius: 50%;
background: light-dark(#373a40, #f1f2f3);
box-shadow: 0 0 24px 6px color-mix(in oklab, light-dark(#373a40, #f1f2f3) 35%, transparent);
}
.card-polaroid__scene::after {
content: '';
position: absolute;
left: -10%;
right: -10%;
bottom: 0;
height: 64px;
background: light-dark(#fbfcfd, #121417);
clip-path: polygon(0 100%, 0 60%, 14% 40%, 30% 62%, 48% 20%, 64% 54%, 80% 34%, 100% 58%, 100% 100%);
}
/* The undeveloped film: a milky layer that clears as the image comes up. */
.card-polaroid__film {
position: absolute;
inset: 0;
background: light-dark(#ebedf0, #232529);
transition: opacity 2.4s cubic-bezier(0.4, 0, 0.2, 1);
}
.card-polaroid:hover .card-polaroid__scene, .card-polaroid:focus-visible .card-polaroid__scene { filter: none; }
.card-polaroid:hover .card-polaroid__film, .card-polaroid:focus-visible .card-polaroid__film { opacity: 0; }
.card-polaroid__caption {
position: absolute;
left: 14px;
right: 14px;
bottom: 10px;
color: light-dark(#ebedf0, #232529);
font-family: 'Segoe Print', 'Bradley Hand', 'Comic Sans MS', cursive;
font-size: 13px;
transform: rotate(-1deg);
}
@media (prefers-reduced-motion: reduce) {
.card-polaroid, .card-polaroid__scene, .card-polaroid__film { transition-duration: 0.01s; }
}Goal: Create a "Polaroid" card.
Visual behavior: An instant photo that develops as you hover: the milky film clears and the blurred, flat image sharpens and gains contrast over a couple of seconds, while the print tilts and lifts. A handwritten caption sits on the white border.
Technique:
- The photo is a gradient sky with a glowing sun (::before) and a ridge silhouette (::after, a clip-path polygon).
- At rest the scene has filter: blur(9px) contrast(0.5) and a solid film layer covers it; on hover the filter transitions to none and the film fades out, both over 2.4s, like a developing print.
- The print sits at rotate(-3deg) and springs to rotate(1deg) and a small lift.
Accessibility: Focusable, with role="img" and an aria-label describing the photo; focus develops it like hover. Under prefers-reduced-motion it develops instantly.
My stack: {{USER_STACK}}
My styling: {{USER_STYLING}}
My constraints: {{USER_CONSTRAINTS}}
My colors: neutral only. Black, white and grays, no hues. Use the color values above as written, or my project's closest neutral tokens.
Return a single self-contained component. No dependencies.Goal: Create a "Polaroid" card.
Visual behavior: An instant photo that develops as you hover: the milky film clears and the blurred, flat image sharpens and gains contrast over a couple of seconds, while the print tilts and lifts. A handwritten caption sits on the white border.
Technique:
- The photo is a gradient sky with a glowing sun (::before) and a ridge silhouette (::after, a clip-path polygon).
- At rest the scene has filter: blur(9px) contrast(0.5) and a solid film layer covers it; on hover the filter transitions to none and the film fades out, both over 2.4s, like a developing print.
- The print sits at rotate(-3deg) and springs to rotate(1deg) and a small lift.
Accessibility: Focusable, with role="img" and an aria-label describing the photo; focus develops it like hover. Under prefers-reduced-motion it develops instantly.
My stack: {{USER_STACK}}
My styling: {{USER_STYLING}}
My constraints: {{USER_CONSTRAINTS}}
My colors: use my project's existing color tokens. The color values above are grayscale placeholders. Map each one by its role (surface, text, border, accent, glow) to my theme, and keep their relative lightness so contrast and the effect still read.
Return a single self-contained component. No dependencies.Paste into Claude, ChatGPT or Cursor after filling in {{USER_STACK}}, {{USER_STYLING}} and {{USER_CONSTRAINTS}}. Neutral keeps the greys you see here; Match my project asks the model to map them onto your own colour tokens.
Free to use in any project, commercial included. MIT-0, no attribution required. Licence
About this component
An instant photo that develops as you hover: the milky film clears and the blurred, flat image sharpens and gains contrast over a couple of seconds, while the print tilts and lifts. A handwritten caption sits on the white border.
- Dependencies
- None
- Difficulty
- Beginner
- Triggers
- hover, fill, card
- Techniques
- filter, css transition, clip path, pseudo elements
- Reduced motion
- Respected
- Theme
- Light and dark (follows your page's color-scheme)
Focusable, with role="img" and an aria-label describing the photo; focus develops it like hover. Under prefers-reduced-motion it develops instantly.