A simple age gate asks visitors to confirm their age before viewing a page. It is a self-declaration interface: it does not verify a person’s identity, prevent access to public files, or establish compliance with age-verification requirements.
The example below demonstrates an on-page confirmation dialog without installing a dedicated plugin. Use a stronger, server-enforced solution when content genuinely requires restricted access.
Where to put the code
Use a child theme or your existing developer-managed template system. Put the markup inside the page body, the CSS in the child theme stylesheet, and the JavaScript in an enqueued frontend script. Do not paste PHP into an ordinary WordPress content block or edit the parent theme.
Target only the pages that need the notice. A WooCommerce shop archive is not the same as a normal WordPress page: developers should use appropriate conditionals such as is_shop() and guard WooCommerce functions if the plugin could be inactive.
1. Add the dialog markup
<dialog id="jh-age-gate" aria-labelledby="jh-age-title">
<h2 id="jh-age-title">Are you 18 or older?</h2>
<p>Please confirm your age to continue.</p>
<button type="button" id="jh-age-confirm">Yes, I am 18 or older</button>
<a href="/age-restricted/">No, leave this page</a>
</dialog>Create an unrestricted exit page at /age-restricted/, or change the link to a suitable existing destination. Exclude that destination from the gate so visitors can leave without a loop.
2. Style it for smaller screens
#jh-age-gate {
max-width: min(30rem, calc(100vw - 2rem));
box-sizing: border-box;
padding: 1.5rem;
border: 0;
border-radius: .5rem;
}
#jh-age-gate::backdrop { background: rgb(0 0 0 / 85%); }
#jh-age-gate button, #jh-age-gate a {
display: inline-block;
margin: .5rem .5rem .5rem 0;
}
#jh-age-gate :focus-visible { outline: 3px solid #1976d2; }3. Remember confirmation for the browser session
document.addEventListener('DOMContentLoaded', function () {
const gate = document.getElementById('jh-age-gate');
const confirm = document.getElementById('jh-age-confirm');
if (!gate || !confirm || typeof gate.showModal !== 'function') return;
const key = 'jh_age_confirmed';
let accepted = false;
try { accepted = sessionStorage.getItem(key) === 'yes'; } catch (error) {}
if (accepted) return;
gate.addEventListener('cancel', function (event) { event.preventDefault(); });
confirm.addEventListener('click', function () {
try { sessionStorage.setItem(key, 'yes'); } catch (error) {}
gate.close();
});
gate.showModal();
confirm.focus();
});The modal dialog places the rest of the page outside the normal interaction flow while open. Escape is disabled so visitors must choose confirmation or the exit link. If storage is blocked, confirmation closes the current dialog but may be requested again on navigation. This example uses session storage rather than a permanent cookie.
Important limitations
- Disabling JavaScript or using an unsupported browser can bypass the notice.
- The underlying content is still delivered to the browser and can be accessed directly.
- A remembered answer is not evidence of age.
- Public image, video, and download URLs are not protected by a visual overlay.
Test before publishing
Test a fresh browser session, confirmation, navigation, storage restrictions, the exit destination, mobile zoom, and keyboard operation. Confirm focus returns sensibly after closing. Review contrast and screen-reader behavior, and ensure no content remains permanently blocked because a selector or script failed.
For real account-based restrictions, see restricting WordPress pages by user role. Browser reference: the HTML dialog element.
Get help with implementation
For tailored implementation and testing, explore my WordPress development services, or send your website URL and requirements.
Need help fixing this on your website?
Need help fixing this on your website? Chat with me on WhatsApp.
