TopUp was a historical jQuery lightbox. Developers used it for years. We now look at how this old tool works. Then we build a modern native dialog.
The web changes fast, but core needs stay the same. We need to show content above the page. We need to keep our context safe. The tools we use may change.
This guide keeps old and new worlds separate. First, we see what old lightboxes did well. Then we build a modal with only standard tools. You get checks you can run right now.
What old lightboxes did right
Old lightbox scripts shared a simple plan. A script watched for clicks on links or buttons. It showed an overlay element and hid the main page. It forced the browser to focus inside the new layer.
Libraries like Magnific-Popup refined this plan over time. The result was good for users. People saw a clear dialog. The background went dim, and the Escape key worked.
The cost was high for developers. Every project had to rewrite the same rules. They had to handle keyboard inputs and focus traps. They had to lock the page scroll.
Historical archives of TopUp show its old code. These links prove it was a jQuery lightbox. They do not claim it is supported today. Treat this code like a museum piece.
Good habits to keep
Old libraries did some things well. You should keep these good habits.
A visible close button sits in the dialog. Escape works as a quick way out for keyboard users. Focus returns to the button that opened the dialog. A backdrop tells the eye the page behind is paused.
These four behaviors define a good modal. We get all four from the browser itself now. The difference is who writes the code: you, or the platform.
Native dialog: what the platform gives you
The <dialog> element is part of the HTML standard. When you call dialog.showModal(), the browser does several things at once. It renders the dialog above the page.
It dims the background with a default backdrop. It marks the rest of the document inert. It moves focus inside the dialog.
Calling dialog.close() reverses all of that. Focus returns to the element that opened it. The backdrop disappears. The document becomes interactive again.
You do not need to write that logic. That is the whole point of using a standard element instead of a library. The browser does the hard work for you.
You style the dimmed area with the CSS pseudo-element ::backdrop. This pseudo-element only exists when the dialog is open as a modal. You do not need a separate div for the overlay layer.
dialog::backdrop {
background: rgba(15, 23, 42, 0.65);
backdrop-filter: blur(2px);
}
Two lines of CSS replace complex z-index work. Old lightboxes needed an extra overlay div for this. The blur effect is optional for you.
Choosing show() versus showModal()
The element offers two open methods. The difference matters a lot.
show() opens the dialog without a backdrop. It does not make the background inert. It does not trap focus.
showModal() does all three things. Almost every lightbox scenario wants showModal().
If your dialog fails the backdrop check later, check this first. A one-character mistake in the method name is a common bug source.
Self-contained example
The snippet below is complete and simple. Save it as an HTML file. Open it directly in your browser.
It needs no library and no build step. It needs no network request. Every line has a purpose you can read.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dialog Demo</title>
<style>
dialog {
padding: 24px;
border: 1px solid #cbd5e1;
border-radius: 10px;
max-width: 440px;
width: min(90vw, 440px);
box-shadow: 0 20px 50px rgba(0,0,0,0.25);
}
dialog::backdrop {
background: rgba(15, 23, 42, 0.6);
}
dialog h2 { margin-top: 0; }
.actions { display: flex; gap: 8px; justify-content: flex-end; }
</style>
</head>
<body>
<main>
<h1>Release notes</h1>
<p>Click below to open a modal built with a native dialog element.</p>
<button id="openBtn">Read notes</button>
</main>
<dialog id="notesDialog" aria-labelledby="notesTitle">
<h2 id="notesTitle">Notes for this track</h2>
<p>Recorded in a single take. Mix by the house engineer.</p>
<p>Press Escape or click Close to return to the page.</p>
<div class="actions">
<button id="closeBtn" type="button">Close</button>
</div>
</dialog>
<script>
const dialog = document.getElementById('notesDialog');
const openBtn = document.getElementById('openBtn');
const closeBtn = document.getElementById('closeBtn');
openBtn.addEventListener('click', () => dialog.showModal());
closeBtn.addEventListener('click', () => dialog.close());
</script>
</body>
</html>
Reading the script
The script holds three references. Two listeners connect buttons to the two standard methods.
There is no focus code here. There is no scroll lock code. There is no extra backdrop element in the HTML.
If you removed the script, the dialog would stay hidden. Everything dynamic comes from the API.
The aria-labelledby attribute gives screen readers a name. Without it, a reader announces “dialog” with no context. Adding one sentence of labeling helps a lot.
Styling notes
Use width: min(90vw, 440px) so the dialog fits well. It never exceeds the viewport size. On a phone, it shrinks gracefully.
On a desktop, it stays at a readable measure. Pair that with a border radius and a soft shadow. The dialog looks like a card floating above the page.
Keep the heading margin at zero inside the dialog. The default browser margin on h1 often pushes text down. It can break the vertical rhythm of a compact modal.
Concrete browser checks
Open the saved file in at least two browsers. Walk through each check below. Each one verifies a behavior that old scripts had to build.
- Backdrop present. Click “Read notes”. Confirm the page behind is dimmed. Click the heading behind the dialog. It should not respond.
- Focus inside. Check that focus starts inside the dialog. Tab moves between buttons inside the dialog only.
- Escape closes. Press Escape once. The dialog should close and disappear.
- Focus returns. After Escape, focus should land back on “Read notes”. Press Enter to check. The dialog should open again.
- Backdrop styling applies. Inspect the computed background of
::backdrop in dev tools. It should match the CSS you wrote. - Inert background. Click outside the dialog. The browser should block the click. The rest of the document is inert.
If any step fails, check your method name. You must call showModal() and not show(). The non-modal method skips the backdrop and focus trap.
Quick keyboard-only walkthrough
Put the mouse aside. Use Tab to reach “Read notes”. Press Enter to open it. Check that focus is inside.
Tab once to reach Close. Press Enter to close. Check that focus returns to “Read notes”. This quick pass catches many issues early.
Testing with a screen reader
If you have a screen reader, open the dialog. Listen for the dialog name from aria-labelledby. Hear the heading and paragraphs.
Close it. Listen for the focus announcement on the opener button. That sequence confirms both labeling and focus return.
Where historical TopUp fits today
You will not add TopUp to a new project. The archived documentation still serves a purpose, though.
They show what a jQuery lightbox looked like. They show how gallery navigation was wired. They show which problems developers cared about back then.
If you maintain an older site with a lightbox script, plan a change. Replace simple content modals first. The native dialog covers them fully.
Leave gallery-style components for last. They need layout work before swapping the behavior layer.
Keep the distinction sharp in your notes. Reference the historical project only for past behavior. For new work, reference the standard element and the CSS pseudo-element.
Common migration pitfalls
Watch for two traps. First, a page that opens a dialog from inside another dialog. Nesting works, but focus return can feel strange.
Second, forms inside a dialog. The browser will not submit the surrounding page. You need an explicit cancel action. This way users are never trapped.
Next step
Run the six checks above in your browser today. Make sure they all pass. Then try one variation.
Swap the single dialog for two dialogs. Confirm that closing the first returns focus to its own opener. That small extension proves you understand the focus rules well.
TopUp and JavaScript lightboxes: a practical guide
TopUp was a historical jQuery lightbox. Developers used it for years. We now look at how this old tool works. Then we build a modern native dialog.
The web changes fast, but core needs stay the same. We need to show content above the page. We need to keep our context safe. The tools we use may change.
This guide keeps old and new worlds separate. First, we see what old lightboxes did well. Then we build a modal with only standard tools. You get checks you can run right now.
What old lightboxes did right
Old lightbox scripts shared a simple plan. A script watched for clicks on links or buttons. It showed an overlay element and hid the main page. It forced the browser to focus inside the new layer.
Libraries like Magnific-Popup refined this plan over time. The result was good for users. People saw a clear dialog. The background went dim, and the Escape key worked.
The cost was high for developers. Every project had to rewrite the same rules. They had to handle keyboard inputs and focus traps. They had to lock the page scroll.
Historical archives of TopUp show its old code. These links prove it was a jQuery lightbox. They do not claim it is supported today. Treat this code like a museum piece.
Good habits to keep
Old libraries did some things well. You should keep these good habits.
A visible close button sits in the dialog. Escape works as a quick way out for keyboard users. Focus returns to the button that opened the dialog. A backdrop tells the eye the page behind is paused.
These four behaviors define a good modal. We get all four from the browser itself now. The difference is who writes the code: you, or the platform.
Native dialog: what the platform gives you
The
<dialog>element is part of the HTML standard. When you calldialog.showModal(), the browser does several things at once. It renders the dialog above the page.It dims the background with a default backdrop. It marks the rest of the document inert. It moves focus inside the dialog.
Calling
dialog.close()reverses all of that. Focus returns to the element that opened it. The backdrop disappears. The document becomes interactive again.You do not need to write that logic. That is the whole point of using a standard element instead of a library. The browser does the hard work for you.
You style the dimmed area with the CSS pseudo-element
::backdrop. This pseudo-element only exists when the dialog is open as a modal. You do not need a separate div for the overlay layer.Two lines of CSS replace complex z-index work. Old lightboxes needed an extra overlay div for this. The blur effect is optional for you.
Choosing show() versus showModal()
The element offers two open methods. The difference matters a lot.
show()opens the dialog without a backdrop. It does not make the background inert. It does not trap focus.showModal()does all three things. Almost every lightbox scenario wantsshowModal().If your dialog fails the backdrop check later, check this first. A one-character mistake in the method name is a common bug source.
Self-contained example
The snippet below is complete and simple. Save it as an HTML file. Open it directly in your browser.
It needs no library and no build step. It needs no network request. Every line has a purpose you can read.
Reading the script
The script holds three references. Two listeners connect buttons to the two standard methods.
There is no focus code here. There is no scroll lock code. There is no extra backdrop element in the HTML.
If you removed the script, the dialog would stay hidden. Everything dynamic comes from the API.
The
aria-labelledbyattribute gives screen readers a name. Without it, a reader announces “dialog” with no context. Adding one sentence of labeling helps a lot.Styling notes
Use
width: min(90vw, 440px)so the dialog fits well. It never exceeds the viewport size. On a phone, it shrinks gracefully.On a desktop, it stays at a readable measure. Pair that with a border radius and a soft shadow. The dialog looks like a card floating above the page.
Keep the heading margin at zero inside the dialog. The default browser margin on
h1often pushes text down. It can break the vertical rhythm of a compact modal.Concrete browser checks
Open the saved file in at least two browsers. Walk through each check below. Each one verifies a behavior that old scripts had to build.
::backdropin dev tools. It should match the CSS you wrote.If any step fails, check your method name. You must call
showModal()and notshow(). The non-modal method skips the backdrop and focus trap.Quick keyboard-only walkthrough
Put the mouse aside. Use Tab to reach “Read notes”. Press Enter to open it. Check that focus is inside.
Tab once to reach Close. Press Enter to close. Check that focus returns to “Read notes”. This quick pass catches many issues early.
Testing with a screen reader
If you have a screen reader, open the dialog. Listen for the dialog name from
aria-labelledby. Hear the heading and paragraphs.Close it. Listen for the focus announcement on the opener button. That sequence confirms both labeling and focus return.
Where historical TopUp fits today
You will not add TopUp to a new project. The archived documentation still serves a purpose, though.
They show what a jQuery lightbox looked like. They show how gallery navigation was wired. They show which problems developers cared about back then.
If you maintain an older site with a lightbox script, plan a change. Replace simple content modals first. The native dialog covers them fully.
Leave gallery-style components for last. They need layout work before swapping the behavior layer.
Keep the distinction sharp in your notes. Reference the historical project only for past behavior. For new work, reference the standard element and the CSS pseudo-element.
Common migration pitfalls
Watch for two traps. First, a page that opens a dialog from inside another dialog. Nesting works, but focus return can feel strange.
Second, forms inside a dialog. The browser will not submit the surrounding page. You need an explicit cancel action. This way users are never trapped.
Next step
Run the six checks above in your browser today. Make sure they all pass. Then try one variation.
Swap the single dialog for two dialogs. Confirm that closing the first returns focus to its own opener. That small extension proves you understand the focus rules well.