{
  "name": "card-lightbox",
  "dependencies": [
    "motion-v"
  ],
  "registryDependencies": [],
  "files": [
    {
      "name": "CardLightbox.vue",
      "content": "<script setup lang=\"ts\">\nimport { onMounted, onUnmounted, ref } from 'vue';\nimport { AnimatePresence, motion } from 'motion-v';\nimport type { Item } from './types';\nimport { XIcon } from 'lucide-vue-next';\n\ntype Props = {\n  items: Item[];\n};\n\nconst props = defineProps<Props>();\n\nconst openId = ref<string | null>(null);\n\nonMounted(() => {\n  document.addEventListener('keydown', handleEscKeydown);\n});\n\nonUnmounted(() => {\n  document.removeEventListener('keydown', handleEscKeydown);\n});\n\nfunction getTheme(id: string) {\n  return props.items.find(item => item.id === id)?.theme;\n}\n\nfunction open(id: string) {\n  openId.value = id;\n}\n\nfunction close() {\n  openId.value = null;\n}\n\nfunction handleEscKeydown(event: KeyboardEvent) {\n  if (event.key === 'Escape') {\n    close();\n  }\n}\n</script>\n\n<template>\n  <div class=\"card-lightbox\">\n    <ul class=\"card-lightbox__grid\">\n      <motion.button\n        v-for=\"card in props.items\"\n        :key=\"card.id\"\n        :data-open=\"openId === card.id\"\n        :class=\"[\n          'card-lightbox__card',\n          {\n            'theme-dark': getTheme(card.id) === 'dark',\n            'theme-light': getTheme(card.id) === 'light'\n          }\n        ]\"\n        @click=\"() => open(card.id)\"\n      >\n        <motion.div\n          class=\"card-lightbox__card-content animate-fade-in\"\n          :layout-id=\"`card-container-${card.id}`\"\n        >\n          <motion.div\n            class=\"card-lightbox__card-image-container\"\n            :layout-id=\"`card-image-container-${card.id}`\"\n          >\n            <motion.img\n              class=\"card-lightbox__card-image\"\n              :src=\"card.image\"\n              alt=\"\"\n            />\n            <div class=\"card-lightbox__image-overlay\" />\n          </motion.div>\n          <motion.div\n            class=\"card-lightbox__card-title-container\"\n            :layout-id=\"`title-container-${card.id}`\"\n            layout=\"position\"\n          >\n            <span class=\"card-lightbox__card-category\">{{ card.category }}</span>\n            <h2 class=\"card-lightbox__card-title\">\n              {{ card.title }}\n            </h2>\n          </motion.div>\n        </motion.div>\n      </motion.button>\n    </ul>\n    <AnimatePresence>\n      <motion.div\n        v-if=\"openId\"\n        :initial=\"{ opacity: 0 }\"\n        :animate=\"{ opacity: 1 }\"\n        :exit=\"{ opacity: 0 }\"\n        :transition=\"{ duration: 0.2 }\"\n        style=\"pointer-events: auto\"\n        class=\"card-lightbox__expanded-overlay\"\n        @click=\"close\"\n      />\n    </AnimatePresence>\n    <AnimatePresence>\n      <div\n        v-if=\"openId\"\n        :class=\"[\n          'card-lightbox__expanded-container',\n          {\n            'theme-dark': getTheme(openId) === 'dark',\n            'theme-light': getTheme(openId) === 'light'\n          }\n        ]\"\n      >\n        <motion.div\n          class=\"card-lightbox__expanded-content\"\n          :layout-id=\"`card-container-${openId}`\"\n        >\n          <motion.div\n            class=\"card-lightbox__expanded-image-container\"\n            :layout-id=\"`card-image-container-${openId}`\"\n          >\n            <motion.img\n              class=\"card-lightbox__expanded-image\"\n              :src=\"props.items.find(item => item.id === openId)?.image\"\n              alt=\"\"\n              :layout-id=\"`card-image-${openId}`\"\n              :initial=\"{ opacity: 0, filter: 'blur(12px)' }\"\n              :animate=\"{ opacity: 1, filter: 'blur(0px)' }\"\n              :exit=\"{ opacity: 0, filter: 'blur(12px)' }\"\n              :transition=\"{ duration: 0.5 }\"\n            />\n            <div class=\"card-lightbox__image-overlay\" />\n            <motion.div\n              class=\"card-lightbox__expanded-title-container\"\n              :layout-id=\"`title-container-${openId}`\"\n              layout=\"position\"\n            >\n              <span class=\"card-lightbox__expanded-category\">{{ props.items.find(item => item.id === openId)?.category }}</span>\n              <h2 class=\"card-lightbox__expanded-title\">\n                {{ props.items.find(item => item.id === openId)?.title }}\n              </h2>\n            </motion.div>\n            <motion.div\n              class=\"card-lightbox__expanded-close-button\"\n              @click=\"close\"\n            >\n              <XIcon />\n            </motion.div>\n          </motion.div>\n          <motion.div class=\"card-lightbox__expanded-body\">\n            <div v-html=\"props.items.find(item => item.id === openId)?.content\" />\n          </motion.div>\n        </motion.div>\n      </div>\n    </AnimatePresence>\n  </div>\n</template>\n\n<style scoped>\n.card-lightbox {\n  margin: 0 auto;\n  width: 100%;\n  max-width: 990px;\n  display: flex;\n  flex-direction: column;\n  container-type: inline-size;\n}\n\n.card-lightbox,\n.card-lightbox * {\n  box-sizing: border-box;\n}\n\n.card-lightbox__grid {\n  display: grid;\n  grid-template-columns: repeat(10, 1fr);\n  gap: 20px;\n  padding: 0;\n  margin: 0;\n  list-style: none;\n}\n\n.card-lightbox__card {\n  position: relative;\n  padding: 0px;\n  height: 420px;\n  grid-column: span 4;\n  box-sizing: border-box;\n  overflow: hidden;\n}\n\n.card-lightbox__card:focus-visible {\n  border-radius: 20px;\n  box-shadow:\n    0 0 0 2px hsl(var(--background)),\n    0 0 0 4px hsl(var(--ring));\n  outline: none;\n}\n\n.card-lightbox__card:nth-child(4n + 1),\n.card-lightbox__card:nth-child(4n + 4) {\n  grid-column: span 6;\n}\n\n.theme-light .card-lightbox__image-overlay {\n  content: '';\n  position: absolute;\n  inset: 0;\n  background: linear-gradient(160deg, rgba(0, 0, 0, 0.95) -8%, rgba(0, 0, 0, 0) 30%);\n  z-index: 1;\n  pointer-events: none;\n}\n\n.theme-dark .card-lightbox__image-overlay {\n  content: '';\n  position: absolute;\n  inset: 0;\n  background: linear-gradient(160deg, rgba(255, 255, 255, 0.95) -8%, rgba(255, 255, 255, 0) 30%);\n  z-index: 1;\n  pointer-events: none;\n}\n\n.card-lightbox__expanded-container {\n  width: 100%;\n  height: 100%;\n  pointer-events: none;\n  display: flex;\n  flex-direction: column;\n  top: 0;\n  left: 0;\n  right: 0;\n  position: fixed;\n  z-index: 51;\n  overflow: hidden;\n  padding: 40px 0;\n  justify-content: center;\n  align-items: center;\n}\n\n.card-lightbox__card-content,\n.card-lightbox__expanded-content {\n  pointer-events: auto;\n  user-select: none;\n  position: relative;\n  border-radius: 20px;\n  background: hsl(var(--card));\n  overflow: hidden;\n  width: 100%;\n  height: 100%;\n  margin: 0 auto;\n}\n\n.card-lightbox__expanded-content {\n  width: unset;\n  max-width: 700px;\n  overflow: hidden;\n  pointer-events: auto;\n  overflow-y: auto;\n  height: 70dvh;\n}\n\n.card-lightbox__card-image-container,\n.card-lightbox__expanded-image-container {\n  overflow: hidden;\n  height: 420px;\n  position: relative;\n  width: 100%;\n}\n\n.card-lightbox__card-image,\n.card-lightbox__expanded-image {\n  z-index: 1;\n  width: 100%;\n  height: 100%;\n  object-fit: cover;\n  pointer-events: none;\n  position: absolute;\n  top: 0;\n  left: 0;\n  transform: none;\n}\n\n.card-lightbox__card-title-container {\n  z-index: 2;\n  position: absolute;\n  top: 0px;\n  left: 0px;\n  max-width: 300px;\n  padding: 20px;\n  text-align: left;\n}\n\n.card-lightbox__expanded-title-container {\n  z-index: 2;\n  position: absolute;\n  top: 0;\n  left: 0;\n  padding: 20px;\n}\n\n.card-lightbox__expanded-close-button {\n  z-index: 2;\n  position: absolute;\n  top: 0;\n  right: 0;\n  padding: 20px;\n  cursor: pointer;\n}\n\n.card-lightbox__card-title,\n.card-lightbox__expanded-title {\n  color: hsl(0 0% 100%);\n  margin: 8px 0;\n  font-size: 20px;\n  font-weight: 600;\n}\n\n.card-lightbox__card-category,\n.card-lightbox__expanded-category {\n  color: hsl(0 0% 100%);\n  background: hsl(0 0% 100% / 0.12);\n  backdrop-filter: blur(8px);\n  padding: 4px 8px;\n  border-radius: 12px;\n  font-size: 12px;\n  text-transform: uppercase;\n}\n\n.theme-dark .card-lightbox__card-category,\n.theme-dark .card-lightbox__expanded-category {\n  background: hsl(240 10% 3.9% / 0.2);\n}\n\n.card-lightbox__expanded-overlay {\n  inset: 0;\n  z-index: 50;\n  position: fixed;\n  backdrop-filter: blur(12px);\n  background: rgba(0, 0, 0, 0.2);\n  will-change: opacity;\n}\n\n.card-lightbox__expanded-body {\n  padding: 35px;\n  max-width: 700px;\n  width: 100vw;\n}\n\n.theme-dark .card-lightbox__card-category,\n.theme-dark .card-lightbox__card-title,\n.theme-dark .card-lightbox__expanded-title-container,\n.theme-dark .card-lightbox__expanded-title,\n.theme-dark .card-lightbox__expanded-category {\n  color: hsl(240 10% 3.9% / 0.9);\n}\n\n.theme-dark svg {\n  stroke: hsl(240 10% 3.9% / 0.9);\n}\n\n.theme-light svg {\n  stroke: hsl(0 0% 100%);\n}\n\n.card-lightbox__expanded-body {\n  color: hsl(var(--primary));\n}\n\n@media only screen and (max-width: 990px) {\n  .card-lightbox__expanded-container {\n    padding: 0;\n  }\n\n  .card-lightbox__expanded-content {\n    overflow: hidden;\n    pointer-events: auto;\n    overflow-y: auto;\n    width: 100vw;\n    max-width: 100vw;\n    height: 100dvh;\n    border-radius: 0;\n  }\n\n  .card-lightbox__card-image-container,\n  .card-lightbox__expanded-image-container {\n    height: 280px;\n  }\n}\n\n@container (max-width: 600px) {\n  #sandbox {\n    align-items: stretch;\n  }\n\n  .card-lightbox {\n    padding: 60px 20px;\n    padding-left: 10px;\n    padding-right: 10px;\n  }\n\n  .card-lightbox__grid {\n    gap: 10px;\n    grid-template-columns: 1fr;\n  }\n\n  .card-lightbox__card {\n    grid-column: auto / auto;\n    height: 280px;\n    width: 100%;\n  }\n\n  .card-lightbox__card:nth-child(4n + 1),\n  .card-lightbox__card:nth-child(4n + 4) {\n    grid-column: auto / auto;\n  }\n\n  .card-lightbox__card-image {\n    width: 100%;\n    height: 100%;\n    object-fit: cover;\n    position: absolute;\n    inset: 0;\n  }\n}\n</style>\n"
    },
    {
      "name": "index.ts",
      "content": "export { default as CardLightbox } from './CardLightbox.vue';\nexport * from './types';\n"
    },
    {
      "name": "types.ts",
      "content": "import type { Component } from 'vue';\n\nexport interface Item {\n  id: string;\n  category: string;\n  title: string;\n  content: string | Component;\n  theme: 'light' | 'dark';\n  image: string;\n}\n"
    }
  ],
  "type": "components:ui"
}