{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "project-gallery-html-demo",
  "title": "Project Gallery",
  "description": "Example showcasing the Project Gallery component with an interactive modal using HTML, CSS, JavaScript and Motion.",
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [
    "https://atlasui.dev/r/project-gallery-html.json"
  ],
  "files": [
    {
      "path": "registry/html/examples/project-gallery-html-demo/index.html",
      "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Project Gallery Html Demo - Atlas UI</title>\n    <link rel=\"stylesheet\" href=\"style.css\" />\n  </head>\n  <body>\n    <section id=\"project-gallery\" class=\"project-gallery\">\n      <a href=\"#\" class=\"gallery-item\">\n        <div class=\"project-image-container\" style=\"background-color: #efe8d3\">\n          <img src=\"https://atlasui.dev/assets/atlas-ui.png\" alt=\"Atlas UI\" />\n        </div>\n        <h2>Atlas UI</h2>\n        <p>Frontend</p>\n      </a>\n\n      <a href=\"#\" class=\"gallery-item\">\n        <div class=\"project-image-container\" style=\"background-color: #706d63\">\n          <img src=\"https://atlasui.dev/assets/portfolio.png\" alt=\"Portfolio\" />\n        </div>\n        <h2>Portfolio</h2>\n        <p>Design &amp; Development</p>\n      </a>\n\n      <a href=\"#\" class=\"gallery-item\">\n        <div class=\"project-image-container\" style=\"background-color: #fafbfc\">\n          <img\n            src=\"https://images.unsplash.com/photo-1540652980807-db4655e8fa60?q=80&w=2130&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D\"\n            alt=\"Tokyo Art\"\n          />\n        </div>\n        <h2>Tokyo Art</h2>\n        <p>Photography</p>\n      </a>\n\n      <a href=\"#\" class=\"gallery-item\">\n        <div class=\"project-image-container\" style=\"background-color: #404040\">\n          <img\n            src=\"https://images.unsplash.com/photo-1515705576963-95cad62945b6?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D\"\n            alt=\"Milky Way\"\n          />\n        </div>\n        <h2>Milky Way</h2>\n        <p>Photography</p>\n      </a>\n    </section>\n  </body>\n  <script type=\"module\" src=\"script.js\"></script>\n</html>\n",
      "type": "registry:file",
      "target": "~/project-gallery/index.html"
    },
    {
      "path": "registry/html/examples/project-gallery-html-demo/style.css",
      "content": ":root {\n  --background: light-dark(\n    oklch(100% 0.00011 271.152),\n    oklch(18.22% 0.00002 271.152)\n  );\n  --foreground: light-dark(\n    oklch(18.22% 0.00002 271.152),\n    oklch(98.511% 0.00011 271.152)\n  );\n}\n\nhtml {\n  color-scheme: light dark;\n}\n\n* {\n  margin: 0;\n  padding: 0;\n  box-sizing: border-box;\n}\n\nbody {\n  font-family: sans-serif;\n  background: var(--background);\n  color: var(--foreground);\n  overflow-x: hidden;\n  max-width: 80rem;\n  width: 100%;\n  margin: 0 auto;\n  padding: 0 1.25rem;\n}\n\na {\n  text-decoration: none;\n  color: inherit;\n}\n\n.project-gallery {\n  display: block;\n}\n\n/* Gallery items */\n.gallery-item {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  padding: 6rem;\n  border-top: 1px solid var(--foreground);\n  cursor: pointer;\n  transition: opacity 0.2s linear;\n}\n\n.gallery-item:hover {\n  opacity: 0.4;\n}\n\n.gallery-item h2 {\n  font-size: 3.75rem;\n  font-weight: 400;\n  transition: transform 0.2s linear;\n}\n\n.gallery-item p {\n  transition: transform 0.2s linear;\n}\n\n.gallery-item:hover h2 {\n  transform: translateX(-12px);\n}\n\n.gallery-item:hover p {\n  transform: translateX(12px);\n}\n\n/* Modal and cursor */\n.modal,\n.cursor,\n.cursor-label {\n  position: fixed;\n  pointer-events: none;\n  transform: translate(-50%, -50%) scale(0);\n  z-index: 1000;\n  will-change: transform, left, top;\n  transform-origin: center center;\n}\n\n.modal {\n  width: 400px;\n  height: 350px;\n  overflow: hidden;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.modal-images {\n  position: absolute;\n  width: 100%;\n  transition: top 0.5s cubic-bezier(0.76, 0, 0.24, 1);\n}\n\n.modal-image {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  width: 100%;\n  height: 350px;\n}\n\n.modal-image img {\n  width: 350px;\n  height: auto;\n}\n\n.cursor,\n.cursor-label {\n  width: 80px;\n  height: 80px;\n  border-radius: 50%;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.cursor {\n  background: #2563eb;\n}\n\n.cursor-label {\n  color: white;\n  font-size: 16px;\n}\n\n/* Project image for mobile */\n.project-image-container {\n  display: none;\n}\n\n/* Responsive styles */\n@media (max-width: 768px) {\n  .project-gallery {\n    display: grid;\n    grid-template-columns: repeat(2, minmax(0, 1fr));\n  }\n\n  .gallery-item {\n    flex-direction: column;\n    align-items: flex-start;\n    padding: 1rem;\n    border-top: none;\n    width: 100%;\n    transition: none;\n  }\n\n  .project-image-container {\n    display: block;\n    position: relative;\n    width: 100%;\n    overflow: hidden;\n    aspect-ratio: 4 / 3;\n    margin-bottom: 1rem;\n  }\n\n  .project-image-container img {\n    width: 100%;\n    height: 100%;\n    object-fit: contain;\n    padding: 1rem;\n    aspect-ratio: 4 / 3;\n  }\n\n  .gallery-item h2 {\n    font-size: 1.5rem;\n    font-weight: 700;\n    line-height: 2rem;\n  }\n\n  .gallery-item p {\n    font-size: 0.875rem;\n    font-weight: 300;\n    line-height: 1.25rem;\n  }\n\n  .gallery-item:hover {\n    opacity: 1;\n  }\n\n  .gallery-item:hover h2,\n  .gallery-item:hover p {\n    transform: none;\n  }\n\n  .modal,\n  .cursor,\n  .cursor-label {\n    display: none !important;\n  }\n}\n\n@media (min-width: 640px) and (max-width: 768px) {\n  .project-gallery {\n    grid-template-columns: repeat(2, minmax(0, 1fr));\n  }\n\n  .gallery-item h2 {\n    font-size: 1.875rem;\n    line-height: 2.25rem;\n  }\n}\n\n@media (max-width: 640px) {\n  .project-gallery {\n    grid-template-columns: repeat(1, minmax(0, 1fr));\n  }\n}\n",
      "type": "registry:file",
      "target": "~/project-gallery/style.css"
    },
    {
      "path": "registry/html/examples/project-gallery-html-demo/script.js",
      "content": "import { animate as motionAnimate } from \"motion\";\n\nconst gallery = document.getElementById(\"project-gallery\");\nconst galleryItems = gallery.querySelectorAll(\".gallery-item\");\n\nfunction createInteractiveElements() {\n  // Create Modal\n  const section = document.createElement(\"section\");\n  section.id = \"interactive-modal\";\n  section.className = \"interactive-modal\";\n\n  const modal = document.createElement(\"div\");\n  modal.className = \"modal\";\n  modal.id = \"modal\";\n\n  const modalImages = document.createElement(\"div\");\n  modalImages.className = \"modal-images\";\n  modalImages.id = \"modal-images\";\n\n  galleryItems.forEach((item) => {\n    const container = item.querySelector(\".project-image-container\");\n    const img = container.querySelector(\"img\");\n\n    const modalImageDiv = document.createElement(\"div\");\n    modalImageDiv.className = \"modal-image\";\n    modalImageDiv.style.backgroundColor = container.style.backgroundColor || \"\";\n\n    const modalImg = document.createElement(\"img\");\n    modalImg.src = img.src;\n    modalImg.alt = img.alt;\n\n    modalImageDiv.appendChild(modalImg);\n    modalImages.appendChild(modalImageDiv);\n  });\n\n  modal.appendChild(modalImages);\n  section.appendChild(modal);\n\n  // Create Cursor\n  const cursor = document.createElement(\"div\");\n  cursor.className = \"cursor\";\n  cursor.id = \"cursor\";\n\n  const cursorLabel = document.createElement(\"div\");\n  cursorLabel.className = \"cursor-label\";\n  cursorLabel.id = \"cursor-label\";\n  cursorLabel.textContent = \"View\";\n\n  section.appendChild(cursor);\n  section.appendChild(cursorLabel);\n\n  // Insert after the gallery section\n  gallery.after(section);\n\n  return { modal, modalImages, cursor, cursorLabel };\n}\n\nconst { modal, modalImages, cursor, cursorLabel } = createInteractiveElements();\n\n// State\nlet activeIndex = 0;\nlet isModalVisible = false;\nlet mouseX = 0;\nlet mouseY = 0;\n\n// Animation options\nconst modalAnimationOptions = {\n  duration: 300,\n  easing: \"ease-out\",\n  fill: \"forwards\",\n};\n\nconst cursorAnimationOptions = {\n  duration: 150,\n  easing: \"ease-out\",\n  fill: \"forwards\",\n};\n\n// Helpers\nfunction isMobile() {\n  return window.innerWidth <= 768;\n}\n\n// Animation loop for smooth following\nfunction updatePositions() {\n  if (isModalVisible) {\n    const modalWidth = modal.offsetWidth;\n    const modalHeight = modal.offsetHeight;\n    const cursorWidth = cursor.offsetWidth;\n    const cursorHeight = cursor.offsetHeight;\n\n    const modalLeft = mouseX - modalWidth / 2;\n    const modalTop = mouseY - modalHeight / 2;\n    const cursorLeft = mouseX - cursorWidth / 2;\n    const cursorTop = mouseY - cursorHeight / 2;\n\n    modal.animate(\n      [\n        { left: modal.offsetLeft + \"px\", top: modal.offsetTop + \"px\" },\n        { left: modalLeft + \"px\", top: modalTop + \"px\" },\n      ],\n      modalAnimationOptions,\n    );\n\n    modal.style.left = modalLeft + \"px\";\n    modal.style.top = modalTop + \"px\";\n\n    cursor.animate(\n      [\n        { left: cursor.offsetLeft + \"px\", top: cursor.offsetTop + \"px\" },\n        { left: cursorLeft + \"px\", top: cursorTop + \"px\" },\n      ],\n      cursorAnimationOptions,\n    );\n\n    cursor.style.left = cursorLeft + \"px\";\n    cursor.style.top = cursorTop + \"px\";\n\n    cursorLabel.animate(\n      [\n        {\n          left: cursorLabel.offsetLeft + \"px\",\n          top: cursorLabel.offsetTop + \"px\",\n        },\n        { left: cursorLeft + \"px\", top: cursorTop + \"px\" },\n      ],\n      cursorAnimationOptions,\n    );\n\n    cursorLabel.style.left = cursorLeft + \"px\";\n    cursorLabel.style.top = cursorTop + \"px\";\n  }\n\n  requestAnimationFrame(updatePositions);\n}\n\n// Mouse tracking\nfunction handleMouseMove(e) {\n  mouseX = e.clientX;\n  mouseY = e.clientY;\n}\n\n// Modal show / hide\nfunction showModal(index) {\n  if (isMobile()) return;\n\n  if (index < 0 || index >= galleryItems.length) {\n    console.error(`Invalid project index: ${index}`);\n    return;\n  }\n\n  activeIndex = index;\n  modalImages.style.top = `-${index * 100}%`;\n  isModalVisible = true;\n\n  motionAnimate(modal, {\n    scale: 1,\n    transition: {\n      duration: 0.4,\n      ease: [0.76, 0, 0.24, 1],\n    },\n  });\n\n  motionAnimate(cursor, {\n    scale: 1,\n    transition: {\n      duration: 0.4,\n      ease: [0.76, 0, 0.24, 1],\n    },\n  });\n\n  motionAnimate(cursorLabel, {\n    scale: 1,\n    transition: {\n      duration: 0.4,\n      ease: [0.76, 0, 0.24, 1],\n    },\n  });\n}\n\nfunction hideModal() {\n  if (isMobile()) return;\n\n  isModalVisible = false;\n\n  motionAnimate(modal, {\n    scale: 0,\n    transition: {\n      duration: 0.4,\n      ease: [0.32, 0, 0.67, 0],\n    },\n  });\n\n  motionAnimate(cursor, {\n    scale: 0,\n    transition: {\n      duration: 0.4,\n      ease: [0.32, 0, 0.67, 0],\n    },\n  });\n\n  motionAnimate(cursorLabel, {\n    scale: 0,\n    transition: {\n      duration: 0.4,\n      ease: [0.32, 0, 0.67, 0],\n    },\n  });\n}\n\n// Event listeners\ngalleryItems.forEach((item, index) => {\n  item.addEventListener(\"mouseenter\", (e) => {\n    e.preventDefault();\n    showModal(index);\n  });\n\n  item.addEventListener(\"mouseleave\", hideModal);\n});\n\nwindow.addEventListener(\"mousemove\", handleMouseMove);\n\n// Start the animation loop\nupdatePositions();\n",
      "type": "registry:file",
      "target": "~/project-gallery/script.js"
    }
  ],
  "meta": {
    "framework": "html"
  },
  "type": "registry:example"
}