"use client";
import { useState } from "react";
import { LenticularCompare } from "@/components/ui/lenticular-compare";
const studies = [
{
name: "Coast",
src: "https://cdn.21st.dev/assets/mirror/cf/cf326af6b2be3c5915024de34d605f209e030bec387b2a18b936095d906dba65.webp",
alt: "Limestone architecture beside a cobalt-blue sea",
filter: "sepia(.42) saturate(.63) contrast(1.12) brightness(.83)",
},
{
name: "Dunes",
src: "https://cdn.21st.dev/assets/mirror/18/1857ca64dd082de84283b1a9e804aee83c56ec7dd480dfdc0384f573aacbf270.webp",
alt: "Burnt-orange sand dunes crossed by a dark road",
filter: "hue-rotate(-18deg) saturate(.55) contrast(1.22) brightness(.78)",
},
{
name: "Water",
src: "https://cdn.21st.dev/assets/mirror/12/124a2f0a8f44ac6c1fc5fe60068eebabedbe424adced72942895362c021dfe22.webp",
alt: "A pale geometric platform above deep teal water",
filter:
"sepia(.3) hue-rotate(318deg) saturate(.67) contrast(1.19) brightness(.77)",
},
];
export default function LenticularDemo() {
const [selected, setSelected] = useState(0),
study = studies[selected];
return (
<div className="lp-demo">
<style>{DEMO_CSS}</style>
<main className="demo-shell">
<header className="demo-header">
<h1>
Lenticular<span> /</span>
</h1>
<span className="study-number">
0{selected + 1}
<i />
03
</span>
</header>
<LenticularCompare
key={study.name}
before={{ src: study.src, alt: study.alt, label: "Original" }}
after={{
src: study.src,
alt: study.alt + " with a muted, darker color grade",
label: "Grade",
filter: study.filter,
}}
/>
<footer className="preset-footer">
<div className="preset-list" role="group" aria-label="Image studies">
{studies.map((item, i) => (
<button
key={item.name}
className={selected === i ? "active" : ""}
aria-pressed={selected === i}
onClick={() => setSelected(i)}
>
<span className="preset-image">
<img src={item.src} alt="" />
</span>
<span className="preset-name">
<small>0{i + 1}</small>
{item.name}
</span>
<span className="preset-marker" />
</button>
))}
</div>
<span className="footer-count">
{String(selected + 1).padStart(2, "0")} /{" "}
{String(studies.length).padStart(2, "0")}
</span>
</footer>
</main>
</div>
);
}
const DEMO_CSS =
'.lp-demo{box-sizing:border-box;width:100%;min-height:100%;background:#111211;color:#edeee7;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;}\n.lp-demo * {\n box-sizing: border-box;\n scrollbar-width: none;\n}\n.lp-demo *::-webkit-scrollbar {\n display: none;\n}\n.lp-demo button,.lp-demo input {\n font: inherit;\n}\n.lp-demo button {\n background: none;\n border: 0;\n color: inherit;\n cursor: pointer;\n padding: 0;\n -webkit-tap-highlight-color: transparent;\n}\n.lp-demo button:focus-visible,.lp-demo input:focus-visible {\n outline: 2px solid #d1efaa;\n outline-offset: 5px;\n}\n.lp-demo button:disabled {\n cursor: not-allowed;\n opacity: 0.5;\n}\n.lp-demo h1,.lp-demo p {\n margin: 0;\n}\n.lp-demo .demo-shell {\n width: min(100% - 100px, 1180px);\n margin: auto;\n padding: 42px 0 35px;\n}\n.lp-demo .demo-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 45px;\n}\n.lp-demo .demo-header h1 {\n font:\n 500 43px/1 ui-sans-serif,\n sans-serif;\n letter-spacing: -2px;\n}\n.lp-demo .demo-header h1 > span {\n font-weight: 300;\n color: #666e5d;\n margin-left: 4px;\n}\n.lp-demo .study-number {\n display: flex;\n align-items: center;\n gap: 15px;\n font:\n 11px ui-monospace,\n monospace;\n color: #a9b19e;\n}\n.lp-demo .study-number i {\n width: 26px;\n height: 1px;\n background: #535e46;\n transform: rotate(-28deg);\n}\n.lp-demo .preset-footer {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding-top: 29px;\n}\n.lp-demo .preset-list {\n display: flex;\n gap: 34px;\n}\n.lp-demo .preset-list > button {\n display: flex;\n align-items: center;\n gap: 12px;\n position: relative;\n min-height: 52px;\n padding: 3px 0;\n color: #8c9c7b;\n text-align: left;\n transition: color 0.25s;\n}\n.lp-demo .preset-list > button.active {\n color: #e1eccf;\n}\n.lp-demo .preset-image {\n display: block;\n width: 66px;\n height: 44px;\n overflow: hidden;\n background: #343e29;\n position: relative;\n}\n.lp-demo .preset-image:after {\n content: "";\n position: absolute;\n inset: 0;\n border: 1px solid #eff4e317;\n}\n.lp-demo .preset-image img {\n width: 100%;\n height: 100%;\n display: block;\n object-fit: cover;\n opacity: 0.55;\n filter: saturate(0.7);\n transition:\n opacity 0.3s,\n filter 0.3s,\n transform 0.5s;\n}\n.lp-demo .preset-list > button.active .preset-image img,.lp-demo .preset-list > button:hover .preset-image img {\n opacity: 1;\n filter: saturate(1);\n}\n.lp-demo .preset-list > button:hover .preset-image img {\n transform: scale(1.08);\n}\n.lp-demo .preset-name {\n display: grid;\n gap: 5px;\n font-size: 12px;\n}\n.lp-demo .preset-name small {\n font:\n 8px ui-monospace,\n monospace;\n letter-spacing: 1px;\n color: #7e9368;\n}\n.lp-demo .preset-marker {\n position: absolute;\n bottom: -8px;\n left: 0;\n right: 0;\n height: 1px;\n background: #a8c385;\n transform: scaleX(0);\n transform-origin: left;\n transition: transform 0.3s;\n}\n.lp-demo .preset-list > button.active .preset-marker {\n transform: scaleX(1);\n}\n.lp-demo .footer-count {\n font:\n 9px ui-monospace,\n monospace;\n color: #82966d;\n letter-spacing: 1px;\n}\n@media (min-width: 1600px) {\n .lp-demo .demo-shell {\n width: min(100% - 160px, 1340px);\n padding-top: 50px;\n }\n .lp-demo .demo-header {\n margin-bottom: 55px;\n }\n .lp-demo .demo-header h1 {\n font-size: 52px;\n }\n .lp-demo .preset-image {\n width: 80px;\n height: 54px;\n }\n .lp-demo .preset-name {\n font-size: 14px;\n }\n .lp-demo .preset-name small {\n font-size: 10px;\n }\n .lp-demo .preset-list {\n gap: 44px;\n }\n}\n@media (max-width: 1000px) {\n .lp-demo .demo-shell {\n width: calc(100% - 64px);\n padding-top: 32px;\n }\n .lp-demo .demo-header {\n margin-bottom: 31px;\n }\n .lp-demo .demo-header h1 {\n font-size: 38px;\n }\n .lp-demo .preset-list {\n gap: 25px;\n }\n .lp-demo .preset-image {\n width: 60px;\n height: 40px;\n }\n .lp-demo .preset-name {\n font-size: 11px;\n }\n .lp-demo .preset-footer {\n padding-top: 23px;\n }\n}\n@media (max-width: 700px) {\n .lp-demo .demo-shell {\n width: calc(100% - 36px);\n padding-top: 28px;\n padding-bottom: 30px;\n }\n .lp-demo .demo-header {\n margin-bottom: 37px;\n }\n .lp-demo .demo-header h1 {\n font-size: 33px;\n letter-spacing: -1.7px;\n }\n .lp-demo .study-number {\n font-size: 10px;\n gap: 12px;\n }\n .lp-demo .study-number i {\n width: 22px;\n }\n .lp-demo .preset-footer {\n padding-top: 24px;\n }\n .lp-demo .preset-list {\n gap: 19px;\n flex: 1;\n justify-content: space-between;\n }\n .lp-demo .preset-list > button {\n display: block;\n min-width: 75px;\n }\n .lp-demo .preset-image {\n width: 100%;\n height: 53px;\n }\n .lp-demo .preset-name {\n display: flex;\n gap: 7px;\n align-items: baseline;\n margin-top: 11px;\n font-size: 12px;\n }\n .lp-demo .preset-name small {\n font-size: 9px;\n }\n .lp-demo .footer-count {\n display: none;\n }\n .lp-demo .preset-marker {\n bottom: -11px;\n }\n .lp-demo .preset-list > button {\n max-width: 100px;\n flex: 1;\n }\n .lp-demo .preset-name small {\n color: #98ad81;\n }\n}\n@media (max-width: 360px) {\n .lp-demo .demo-shell {\n width: calc(100% - 28px);\n }\n .lp-demo .demo-header h1 {\n font-size: 29px;\n }\n .lp-demo .demo-header {\n margin-bottom: 28px;\n }\n .lp-demo .preset-list {\n gap: 13px;\n }\n .lp-demo .preset-list > button {\n min-width: 0;\n }\n .lp-demo .preset-name {\n font-size: 11px;\n gap: 5px;\n }\n .lp-demo .preset-name small {\n font-size: 8px;\n }\n .lp-demo .preset-image {\n height: 47px;\n }\n}\n@media (prefers-reduced-motion: reduce) {\n .lp-demo *,.lp-demo *:before,.lp-demo *:after {\n animation: none !important;\n transition: none !important;\n scroll-behavior: auto !important;\n }\n .lp-demo .preset-list > button:hover .preset-image img {\n transform: none;\n }\n}\n/* Optical imagery carries the color; the frame remains neutral. */\n.lp-demo .demo-header h1 > span {\n color: #686b65;\n}\n.lp-demo .study-number {\n color: #a9ada5;\n}\n.lp-demo .study-number i {\n background: #555951;\n}\n.lp-demo .preset-list > button {\n color: #a5ae98;\n}\n.lp-demo .preset-list > button.active {\n color: #eaf0e0;\n}\n.lp-demo .preset-name small {\n color: #98a687;\n}\n.lp-demo .preset-marker {\n background: #d4ddc5;\n}\n.lp-demo .footer-count {\n color: #a1af91;\n}\n.lp-demo button:focus-visible,.lp-demo input:focus-visible {\n outline-color: #e2ebd4;\n}\n/* Keep the complete optical interaction in a normal desktop viewport. */\n.lp-demo .demo-shell {\n width: min(\n 1180px,\n calc(100% - 80px),\n max(640px, calc((100svh - 282px) * 1.5))\n );\n padding: 24px 0 18px;\n}\n.lp-demo .demo-header {\n margin-bottom: 20px;\n}\n.lp-demo .demo-header h1 {\n font-size: 34px;\n}\n.lp-demo .preset-footer {\n padding-top: 18px;\n}\n.lp-demo .preset-image {\n width: 62px;\n height: 42px;\n}\n.lp-demo .preset-list {\n gap: 30px;\n}\n@media (max-width: 700px) {\n .lp-demo .demo-shell {\n width: calc(100% - 36px);\n padding: 28px 0;\n }\n .lp-demo .demo-header {\n margin-bottom: 27px;\n }\n .lp-demo .demo-header h1 {\n font-size: 33px;\n }\n .lp-demo .preset-image {\n width: 100%;\n height: 53px;\n }\n .lp-demo .preset-footer {\n padding-top: 22px;\n }\n .lp-demo .preset-list {\n gap: 19px;\n }\n}\n@media (max-width: 360px) {\n .lp-demo .demo-shell {\n width: calc(100% - 28px);\n }\n .lp-demo .demo-header h1 {\n font-size: 29px;\n }\n .lp-demo .preset-list {\n gap: 13px;\n }\n .lp-demo .preset-image {\n height: 47px;\n }\n}\n';