Last active
July 20, 2026 15:00
-
-
Save The-LukeZ/c315c091578564b6bfc00ea577778d4a to your computer and use it in GitHub Desktop.
Astro Starlight Image Lightbox Integration
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Import it somewhere top-level in like the MarkdownContent.astro override | |
| <script src="../../scripts/imageLightbox.js"></script> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* Image lightbox open/close transitions. */ | |
| #image-lightbox { | |
| opacity: 0; | |
| scale: 0.95; | |
| } | |
| #image-lightbox[open] { | |
| opacity: 1; | |
| scale: 1; | |
| } | |
| #image-lightbox::backdrop { | |
| background: rgba(0, 0, 0, 0); | |
| } | |
| #image-lightbox[open]::backdrop { | |
| background: rgba(0, 0, 0, 0.85); | |
| } | |
| @media (prefers-reduced-motion: no-preference) { | |
| #image-lightbox { | |
| transition: | |
| opacity 0.25s ease, | |
| scale 0.25s ease, | |
| overlay 0.25s ease allow-discrete, | |
| display 0.25s ease allow-discrete; | |
| } | |
| #image-lightbox::backdrop { | |
| transition: | |
| background 0.25s ease, | |
| overlay 0.25s ease allow-discrete, | |
| display 0.25s ease allow-discrete; | |
| } | |
| @starting-style { | |
| #image-lightbox[open] { | |
| opacity: 0; | |
| scale: 0.95; | |
| } | |
| #image-lightbox[open]::backdrop { | |
| background: rgba(0, 0, 0, 0); | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // This is a POC | |
| // Click any <img> inside an ImageWrapper to open it full-size in a <dialog>. | |
| // Dialog element is created on demand and appended to <body>, outside the | |
| // normal component tree, since it needs to escape ImageWrapper's overflow-clip. | |
| let dialog; | |
| function getDialog() { | |
| if (dialog) return dialog; | |
| dialog = document.createElement("dialog"); | |
| dialog.id = "image-lightbox"; | |
| const figure = document.createElement("figure"); | |
| figure.id = "image-lightbox-figure"; | |
| const img = document.createElement("img"); | |
| img.id = "image-lightbox-img"; | |
| figure.appendChild(img); | |
| const caption = document.createElement("figcaption"); | |
| caption.id = "image-lightbox-caption"; | |
| figure.appendChild(caption); | |
| dialog.appendChild(figure); | |
| Object.assign(dialog.style, { | |
| position: "fixed", | |
| inset: "0", | |
| margin: "auto", | |
| padding: "0", | |
| border: "none", | |
| background: "transparent", | |
| width: "fit-content", | |
| height: "fit-content", | |
| maxWidth: "100vw", | |
| maxHeight: "100vh", | |
| }); | |
| Object.assign(figure.style, { | |
| margin: "0", | |
| display: "flex", | |
| flexDirection: "column", | |
| alignItems: "center", | |
| gap: "0.5rem", | |
| maxWidth: "100vw", | |
| maxHeight: "100vh", | |
| }); | |
| Object.assign(img.style, { | |
| display: "block", | |
| width: "auto", | |
| height: "auto", | |
| maxWidth: "100vw", | |
| maxHeight: "calc(100vh - 3rem)", | |
| objectFit: "contain", | |
| }); | |
| Object.assign(caption.style, { | |
| color: "white", | |
| textAlign: "center", | |
| fontSize: "0.875rem", | |
| }); | |
| // Click anywhere except the caption text closes the dialog. | |
| dialog.addEventListener("click", (event) => { | |
| if (event.target === caption) return; | |
| dialog.close(); | |
| }); | |
| document.body.appendChild(dialog); | |
| return dialog; | |
| } | |
| document.addEventListener("click", (event) => { | |
| const target = event.target; | |
| if ( | |
| !(target instanceof HTMLElement) || | |
| target.tagName.toLowerCase() !== "img" | |
| ) | |
| return; | |
| const wrapper = target.closest('[data-type="image-wrapper"]'); | |
| if (!wrapper) return; | |
| const dlg = getDialog(); | |
| const dlgImg = dlg.querySelector("img"); | |
| const dlgCaption = dlg.querySelector("figcaption"); | |
| dlgImg.src = target.currentSrc || target.src; | |
| dlgImg.alt = target.alt; | |
| dlgCaption.textContent = target.alt; | |
| dlgCaption.style.display = target.alt ? "block" : "none"; | |
| dlg.showModal(); | |
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| // I have a component to wrap images for better styling as well - do what you want. | |
| interface Props { | |
| size?: "sm" | "md" | "lg" | "xl" | "full"; | |
| class?: string; | |
| } | |
| const { size = "md", class: customClasses = "" } = Astro.props; // Default width for optimization | |
| const sizeClasses = (_size: Props["size"] = "md") => | |
| ({ | |
| sm: "max-w-sm", | |
| md: "max-w-md", | |
| lg: "max-w-lg", | |
| xl: "max-w-xl", | |
| full: "max-w-full", | |
| })[_size]; | |
| --- | |
| <div | |
| class={`mx-auto overflow-clip ${sizeClasses(size)} ${customClasses}`.trim()} | |
| data-type="image-wrapper" | |
| > | |
| <slot /> | |
| </div> | |
| <style> | |
| [data-type="image-wrapper"] :global(img) { | |
| cursor: zoom-in; | |
| } | |
| </style> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment