Instagram Gallery Lightbox — Free HTML CSS JS Snippet

Instagram Gallery Lightbox · Modals · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Square aspect grid
aspect-ratio keeps cells perfectly square.
Hover like counts
A scrim reveals engagement stats per tile.
Blurred lightbox
backdrop-filter dims and frosts the page.
Cross-dissolve swaps
Photos fade between each navigation.
Looping prev/next
Modulo wrapping cycles past the ends.
Keyboard and swipe
Arrows, Escape, and touch flicks.
Backdrop dismiss
Clicking outside the photo closes it.
Modal focus management
Focus moves in and restores on close.

About this UI Snippet

Instagram Gallery Lightbox — Square Grid with a Full Photo Viewer

Screenshot of the Instagram Gallery Lightbox snippet rendered live

This snippet recreates the Instagram profile experience: a tidy three-column grid of square photos that show like counts on hover, and a full-screen lightbox that opens on click with previous/next navigation, keyboard arrows, swipe gestures, and the usual dismissal patterns. It's built in plain HTML, CSS, and vanilla JavaScript, with accessibility and touch handled.

The square grid

The grid is a CSS grid-template-columns: repeat(3, 1fr) with a tight 5px gap, exactly like Instagram's profile layout. Each cell uses aspect-ratio: 1 so it stays perfectly square regardless of column width, and overflow: hidden with a cover-sized background keeps images cropped to the square without distortion. On hover, a dark scrim fades in (::after) and a centered overlay reveals the photo's like count and tag — the signature Instagram hover, which surfaces engagement stats without cluttering the resting grid.

Data-driven cells

All photos live in a PHOTOS array (here, gradient placeholders with like counts and tags, swappable for real image URLs). The grid is generated in a loop, with each cell's background set through a --bg custom property and its like count formatted with toLocaleString() so large numbers read as "3,402". Clicking a cell opens the lightbox at that index.

The lightbox

The viewer is a fixed-position overlay with a blurred, dimmed backdrop (backdrop-filter: blur). It toggles via an .open class that transitions opacity and visibility together — using visibility ensures the closed lightbox isn't focusable or interactive, which a plain opacity fade wouldn't guarantee. Inside, a large square photo shows the current image, with a caption row displaying the like count and the position indicator ("3 / 9").

Smooth image swaps

When you navigate, render() briefly fades the photo to opacity: 0, swaps the --bg after 120ms, then fades back in — so moving between images cross-dissolves instead of hard-cutting. The go(d) function wraps the index with modulo arithmetic ((current + d + length) % length), so Next from the last photo loops to the first and Previous from the first loops to the last.

Every dismissal and navigation path

The lightbox supports the full set of expected interactions: the close button, clicking the backdrop (but not the photo, via an e.target === box check), the Escape key, the on-screen prev/next arrows, and the Left/Right arrow keys. On touch, touchstart/touchend measure horizontal swipe distance and call go() past a 50px threshold, so you can flick between photos on a phone. On small screens the arrows reposition to overlay the photo edges.

Focus management

When the lightbox opens it stores the previously focused element, moves focus to the Next button, and restores focus to the original trigger on close — the correct focus pattern for a modal dialog, which is marked up with role="dialog" and aria-modal="true".

Customizing it

Replace the gradient placeholders with real image URLs in PHOTOS, change the grid to four columns, adjust the swipe threshold, or add captions and avatars to the lightbox. Pair it with a focus cards section or a photo gallery elsewhere on the page for a complete media experience.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSA three-column square photo grid renders like an Instagram profile.
  2. 2
    Hover a tileA scrim fades in showing the like count and tag.
  3. 3
    Click a photoA blurred full-screen lightbox opens on that image.
  4. 4
    NavigateUse the arrows, Left/Right keys, or swipe to move between photos.
  5. 5
    Close itClick the backdrop, press the X, or hit Escape.
  6. 6
    Swap in real photosReplace the PHOTOS array with image URLs.

Real-world uses

Common Use Cases

Profile photo grids
An Instagram-style alternative to a photo gallery.
Portfolio galleries
Open work in a viewer below a portfolio hero.
Product image grids
Pair with a product card detail page.
Event recaps
Browse shots with a focus cards section nearby.
Travel blogs
Showcase trips with swipeable full-screen photos.
Lightbox demos
A reference for accessible modal image viewers.

Got questions?

Frequently Asked Questions

Each cell uses aspect-ratio: 1, so its height always matches its width no matter how the three flexible columns resize. overflow: hidden with a cover-sized background crops images into the square without stretching them, reproducing Instagram's tidy profile grid at any viewport width.

The go(d) function computes (current + d + length) % length. The modulo wraps the index, so pressing Next on the last photo lands on the first and Previous on the first lands on the last. The same function backs the arrow buttons, the Left/Right keys, and swipe gestures.

Fading opacity alone leaves the overlay technically present and focusable while invisible, so keyboard users could tab into hidden controls. Transitioning visibility alongside opacity makes the closed lightbox non-interactive and removes it from the tab order, while still allowing the fade because visibility is transitionable when paired with a delay.

It's marked up as role="dialog" with aria-modal="true". On open it records the previously focused element and moves focus to the Next button; on close it restores focus to the trigger. It closes on Escape and on backdrop click, and all controls have aria-labels. For production you'd also trap Tab focus within the dialog while it's open.

Render the grid from your photos array and keep current index and open state in component state. Move the keyboard and touch listeners into a mount effect with cleanup, and use refs for focus management. Drive the lightbox visibility from state rather than toggling a class. Swap the gradient placeholders for <img> elements with real src values. In Tailwind, use aspect-square, grid-cols-3, and backdrop-blur utilities.