The TopUp project is a great example of a JavaScript lightbox from old days. This guide builds a fresh viewer for learning. We do not change the historical TopUp files.
Old pages often used clever scripts. They needed extra libraries to work well. Modern browsers now offer strong built-in tools.
We will build a simple image viewer. We use the native HTML dialog element. This skips the weight of old plugins.
Two points are clear here. First, this is a new demo. Second, it does not prove old TopUp works now.
Fast pages are key for web pros. Every script adds load time. Native HTML skips that cost.
We follow a simple order here. First comes the structure markup. Then we style the window.
After that, we add behavior. Finally, we test it in a browser. Each step is short and easy.
You can use a text editor. No build tools are needed. The code is plain and simple.
Why we choose native dialog
The dialog element is the right tool. It gives powerful features without complex code. Read details on MDN and the HTML spec.
The browser does the hard work for us. We do not write scroll lock code. We do not trap focus by hand.
The standard methods work smoothly. dialog.showModal() shows the box. It also adds a dark backdrop.
dialog.close() hides the box right away. Standard features work out of the box. Press Escape to close it fast.
Focus returns to the trigger button too. The ::backdrop styles the dark overlay. These parts are in the HTML spec.
They are not tied to one vendor. Your code stays safe as browsers change. This is a solid choice.
Compare this with your own modal. You would lock body scroll by hand. You would trap Tab and Shift+Tab focus.
You would handle Escape on your own too. Each task has edge cases. Native dialog solves them in one line.
Just call showModal() and you are done. Screen readers announce the dialog region. They know what to focus first.
Writing the HTML structure
First, we need a list of images. In a real project, this is a gallery. For this demo, we keep it simple.
We use an img tag in a button. This makes the image clickable. Keyboard users can tab to it easily.
Buttons are the right choice here. A div click is not focusable. An a tag implies a page move.
The alt text on each thumb works twice. It helps screen readers on the page. It also gives the big image description.
Next, we set the dialog element. It holds a close button and large image. We put this at the end of body.
This keeps the main reading flow clean. Users see the gallery first. The modal shows only when they ask.
Build an image lightbox with a native HTML dialog
The TopUp project is a great example of a JavaScript lightbox from old days. This guide builds a fresh viewer for learning. We do not change the historical TopUp files.
Old pages often used clever scripts. They needed extra libraries to work well. Modern browsers now offer strong built-in tools.
We will build a simple image viewer. We use the native HTML
dialogelement. This skips the weight of old plugins.Two points are clear here. First, this is a new demo. Second, it does not prove old TopUp works now.
Fast pages are key for web pros. Every script adds load time. Native HTML skips that cost.
We follow a simple order here. First comes the structure markup. Then we style the window.
After that, we add behavior. Finally, we test it in a browser. Each step is short and easy.
You can use a text editor. No build tools are needed. The code is plain and simple.
Why we choose native dialog
The
dialogelement is the right tool. It gives powerful features without complex code. Read details on MDN and the HTML spec.The browser does the hard work for us. We do not write scroll lock code. We do not trap focus by hand.
The standard methods work smoothly.
dialog.showModal()shows the box. It also adds a dark backdrop.dialog.close()hides the box right away. Standard features work out of the box. PressEscapeto close it fast.Focus returns to the trigger button too. The
::backdropstyles the dark overlay. These parts are in the HTML spec.They are not tied to one vendor. Your code stays safe as browsers change. This is a solid choice.
Compare this with your own modal. You would lock body scroll by hand. You would trap
TabandShift+Tabfocus.You would handle
Escapeon your own too. Each task has edge cases. Native dialog solves them in one line.Just call
showModal()and you are done. Screen readers announce the dialog region. They know what to focus first.Writing the HTML structure
First, we need a list of images. In a real project, this is a gallery. For this demo, we keep it simple.
We use an
imgtag in a button. This makes the image clickable. Keyboard users can tab to it easily.Buttons are the right choice here. A
divclick is not focusable. Anatag implies a page move.The
alttext on each thumb works twice. It helps screen readers on the page. It also gives the big image description.Next, we set the
dialogelement. It holds a close button and large image. We put this at the end of body.This keeps the main reading flow clean. Users see the gallery first. The modal shows only when they ask.
Here is the markup you need:
Note the
data-srcattribute here. We store the large image path there. The thumbnail stays light this way.The empty
srcon the big image helps. It avoids a preload on page load. JavaScript fills it when the dialog opens.This keeps initial page weight very low. Add
aria-labelto the close button. Screen readers announce its purpose clearly.Something like “Close image” works well. Good labels help every user. Small details add up to big wins.
Styling the modal window
CSS gives the modal its look. The
dialoghas default styles. We override them to fit our design.Default styles make a white box. That is fine for forms. For an image, we want the pic to float.
We center the dialog on screen. We also handle the close button. The backdrop is the most important part.
The
::backdropshows only when open. We use it to dim the page. This tells the user to focus on image.Put the close button in a corner. Fixed position keeps it on view. Give it a good tap target for phones.
The
transparentbackground removes white box. The image itself is the main focus. The dark backdrop makes it pop.Using
vwandvhkeeps image responsive. It will not overflow small screens. Tall and wide shots both fit.Consider a thin border or light shadow. This separates bright image from bright backdrop. Small touches boost readability for all.
Connecting the JavaScript
Now we add behavior to our page. We pick the buttons and dialog. Then we add a click listener to each.
When user clicks a thumb, we act. We update the large image source first. Then we call
showModal()to open.This triggers CSS animation and backdrop. We need a way to close it too. The close button calls
close()method.The
Escapekey is handled by browser. Storing image source in data attr is clean. We read it back withgetAttribute.No string parsing is required at all. Event delegation is an option for big galleries. Attach one listener to the list container.
Check
event.targetfor nearest button to click. This keeps your code light and fast. Fewer listeners means less memory use.The last block handles background click. If user clicks dialog itself, we close. We must not close if they click image.
Keep
alttext in sync with new source. Reading it from the button works well. This keeps descriptions true for screen readers.Event listeners attach once at page load. Buttons opened later still respond fine. There is no need to rebind handlers.
Checking your work
Testing is the final step for us. You must check behavior in a browser. Open your file in Chrome or Firefox.
Run these specific checks right now:
Escape. Does box close then?Focus must return to clicked button. This is default behavior of
showModal(). If it fails, HTML structure may be wrong.Try the same flow with a screen reader. NVDA on Windows works well here. VoiceOver on macOS does too.
They should announce dialog and read alt. Check console for errors too. A missing ID throws null reference error.
Fix typos before moving on at all. Also test with JavaScript turned off. Gallery should still show small thumbs.
The modal will not open then. That is an okay graceful fallback. Good pages work a bit without JS.
Comparing with other tools
We built this from scratch for learning. Some projects use libraries for more features. Magnific Popup is a popular one.
However, native code means zero deps. You do not need to manage packages. You have full control over the CSS.
Old TopUp relied on jQuery a lot. Native dialog removes that requirement now. Our new example stands on its own.
Old lightboxes often ship with more. They add galleries, video, and social. You can add those one bit at a time.
Start with the working core first. Then add bells and whistles later. Keep it simple as you grow.
Handling image loading
Real galleries often load very slow. You can add a loading state. Check if image is complete before open.
Or use the
loading="lazy"attribute here. This keeps the main page fast. It works well with thumbnail approach.Preloading large image on hover helps too. The download starts before the click. Dialog then opens with pic ready.
Show a spinner or skeleton while load. Announce the state through
aria-liveregion. Users on slow net like the feedback.Adding keyboard navigation
The native dialog handles
Escapekey fine. For arrow keys, you need custom code. Add a listener forkeydownevents.You could change image source on arrows. This makes a simple slide show effect. Keep focus inside dialog while open.
Remember to update
alttext each change. Else screen reader reads old info. Keep slide data in array or data attr.Only catch arrow keys while dialog open. Guard with
dialog.opento be safe. That one check stops stray handlers.Where to go next
You now have a working lightbox. You know how
showModal()works fine. You know how::backdropworks too.The next step is multiple images. Create an array of sources to use. Write a small function to swap src.
This turns single viewer into gallery. You can then try transitions later. Keep native
dialogas your base tool.Try pairing this with real audio files. Let lightbox show art beside a player. That combo keeps listening clean and clear.
Read the main references next for more. Check MDN on dialog first. Then read the HTML spec.