Skip to main content

A lightweight snippet to protect a page.

A simple JS/HTML/CSS password lock.

<!-- code provided by Mittun -->

<script>
/**
 * Lightweight JavaScript password gate
 * ----------------------------------------------------------
 * Password: mittun
 *
 * This is front-end only. It is useful for soft-gating free content,
 * preview pages, internal pages, giving day pages, campaign pages, etc.
 *
 * It is NOT true security. Anyone technical can bypass this because
 * the password and page content are still loaded in the browser.
 */

(function () {
  /**
   * Prevent the script from loading twice if accidentally embedded twice.
   */
  if (window.__MITTUN_PASSWORD_GATE_LOADED__) return;
  window.__MITTUN_PASSWORD_GATE_LOADED__ = true;

  /* =========================
     SETTINGS
     ========================= */

  const SETTINGS = {
    /**
     * Change this if you ever want a different password.
     */
    allowedPassword: "mittun",

    /**
     * Unique ID for this gate.
     * Keep this the same across pages if one unlock should apply to multiple pages.
     * Change it per page if each page should require separate access.
     */
    gateId: "giving-day-preview",

    title: "Enter Password",
    message: "Please enter the password to view this page.",
    buttonText: "Unlock Page",

    /**
     * Remember length.
     */
    rememberDays: 7
  };

  /* =========================
     STORAGE HELPERS
     ========================= */

  const localKey = "mittun_gate_local_" + SETTINGS.gateId;

  function isAccessStillValid() {
    const saved = localStorage.getItem(localKey);
    if (!saved) return false;

    try {
      const data = JSON.parse(saved);

      if (data.unlocked === true && Date.now() < data.expiresAt) {
        return true;
      }

      localStorage.removeItem(localKey);
      return false;
    } catch (e) {
      localStorage.removeItem(localKey);
      return false;
    }
  }

  function saveAccessForSevenDays() {
    const expiresAt = Date.now() + SETTINGS.rememberDays * 24 * 60 * 60 * 1000;

    localStorage.setItem(
      localKey,
      JSON.stringify({
        unlocked: true,
        expiresAt: expiresAt
      })
    );
  }

  /**
   * If the user already chose "Don't ask again for 7 days",
   * and the saved access has not expired, do not show the gate.
   */
  if (isAccessStillValid()) return;

  /* =========================
     STYLES
     ========================= */

  const style = document.createElement("style");
  style.innerHTML = `
    body.mittun-password-locked {
      overflow: hidden !important;
    }

    body.mittun-password-locked > *:not(#mittun-password-overlay) {
      filter: blur(5px);
      pointer-events: none;
      user-select: none;
    }

    #mittun-password-overlay {
      position: fixed;
      inset: 0;
      z-index: 2147483647;
      background: rgba(15, 23, 42, 0.72);
      backdrop-filter: blur(8px);
      -webkit-backdrop-filter: blur(8px);
      display: flex;
      align-items: center;
      justify-content: center;
      padding: 24px;
      box-sizing: border-box;
      font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
    }

    #mittun-password-box {
      width: 100%;
      max-width: 440px;
      background: #ffffff;
      border-radius: 18px;
      padding: 28px;
      box-shadow: 0 25px 80px rgba(0, 0, 0, 0.35);
      text-align: center;
      box-sizing: border-box;
    }

    #mittun-password-box h2 {
      margin: 0 0 10px;
      font-size: 26px;
      line-height: 1.2;
      color: #111827;
    }

    #mittun-password-box p {
      margin: 0 0 20px;
      font-size: 15px;
      line-height: 1.5;
      color: #4b5563;
    }

    #mittun-password-input {
      width: 100%;
      padding: 14px 16px;
      border: 1px solid #d1d5db;
      border-radius: 10px;
      font-size: 16px;
      box-sizing: border-box;
      margin-bottom: 16px;
    }

    #mittun-password-input:focus {
      outline: none;
      border-color: #111827;
      box-shadow: 0 0 0 3px rgba(17, 24, 39, 0.12);
    }

    .mittun-remember-options {
      text-align: left;
      background: #f9fafb;
      border: 1px solid #e5e7eb;
      border-radius: 12px;
      padding: 12px 14px;
      margin-bottom: 16px;
    }

    .mittun-remember-option {
      display: flex;
      align-items: center;
      gap: 9px;
      margin: 8px 0;
      font-size: 14px;
      color: #374151;
      cursor: pointer;
    }

    .mittun-remember-option input {
      margin: 0;
    }

    #mittun-password-button {
      width: 100%;
      padding: 14px 18px;
      border: none;
      border-radius: 10px;
      background: #111827;
      color: #ffffff;
      font-size: 16px;
      font-weight: 700;
      cursor: pointer;
    }

    #mittun-password-button:hover {
      opacity: 0.92;
    }

    #mittun-password-error {
      display: none;
      margin-top: 12px;
      color: #b91c1c;
      font-size: 14px;
      font-weight: 600;
    }
  `;
  document.head.appendChild(style);

  /* =========================
     MODAL
     ========================= */

  function lockPage() {
    document.body.classList.add("mittun-password-locked");

    const overlay = document.createElement("div");
    overlay.id = "mittun-password-overlay";

    overlay.innerHTML = `
      <div id="mittun-password-box" role="dialog" aria-modal="true" aria-labelledby="mittun-password-title">
        <h2 id="mittun-password-title">${SETTINGS.title}</h2>
        <p>${SETTINGS.message}</p>

        <input
          id="mittun-password-input"
          type="password"
          placeholder="Enter password"
          autocomplete="off"
        />

        <div class="mittun-remember-options">
          <label class="mittun-remember-option">
            <input type="radio" name="mittun-remember-choice" value="none" checked>
            Ask every time
          </label>

          <label class="mittun-remember-option">
            <input type="radio" name="mittun-remember-choice" value="days">
            Don’t ask again for ${SETTINGS.rememberDays} days
          </label>
        </div>

        <button id="mittun-password-button" type="button">
          ${SETTINGS.buttonText}
        </button>

        <div id="mittun-password-error">
          Incorrect password. Please try again.
        </div>
      </div>
    `;

    document.body.appendChild(overlay);

    const input = document.getElementById("mittun-password-input");
    const button = document.getElementById("mittun-password-button");
    const error = document.getElementById("mittun-password-error");

    function getRememberChoice() {
      const selected = document.querySelector('input[name="mittun-remember-choice"]:checked');
      return selected ? selected.value : "none";
    }

    function unlockIfCorrect() {
      const enteredPassword = input.value.trim();

      if (enteredPassword === SETTINGS.allowedPassword) {
        const rememberChoice = getRememberChoice();

        /**
         * Default behavior:
         * Ask every time = store nothing.
         *
         * If they choose 7 days, save access in localStorage.
         */
        if (rememberChoice === "days") {
          saveAccessForSevenDays();
        }

        document.body.classList.remove("mittun-password-locked");
        overlay.remove();
      } else {
        error.style.display = "block";
        input.value = "";
        input.focus();
      }
    }

    button.addEventListener("click", unlockIfCorrect);

    input.addEventListener("keydown", function (event) {
      if (event.key === "Enter") {
        unlockIfCorrect();
      }
    });

    setTimeout(function () {
      input.focus();
    }, 100);
  }

  /**
   * Run once the page body exists.
   */
  if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", lockPage);
  } else {
    lockPage();
  }
})();
</script>