Files
hipa-fallback-site/app.js
T
2026-08-25 14:09:16 +06:00

98 lines
3.3 KiB
JavaScript

/* ============================================================
hipa — the family archive
The sign-in form is a placeholder. It has no backend: nothing is
validated, stored, logged, or sent anywhere. Every submit ends in the
same message. Wire it to a real endpoint before treating it as a login.
============================================================ */
(function () {
'use strict';
var root = document.documentElement;
root.className += ' js';
/* -- develop sequence -------------------------------------------------
Prints come up first, staggered along the sheet. Each date stamp
flicks on after its own print has settled. */
var STAGGER = 90; // ms between prints
var LEAD = 260; // ms before the sheet starts
var SETTLE = 620; // ms a print takes to settle before its stamp lights
document.querySelectorAll('.frame').forEach(function (frame, i) {
var at = LEAD + i * STAGGER;
frame.querySelector('.frame-print').style.animationDelay = at + 'ms';
frame.querySelectorAll('.frame-stamp, .frame-run').forEach(function (el) {
el.style.animationDelay = (at + SETTLE) + 'ms';
});
});
[['.eyebrow', 0], ['.lede-title', 60], ['.lede-sub', 160],
['.gate', 220], ['.ticker', 320]].forEach(function (pair) {
var el = document.querySelector(pair[0]);
if (el) el.style.animationDelay = pair[1] + 'ms';
});
/* -- show / hide the passphrase --------------------------------------- */
var pass = document.getElementById('pass');
var reveal = document.querySelector('[data-reveal]');
reveal.addEventListener('click', function () {
var shown = pass.type === 'text';
pass.type = shown ? 'password' : 'text';
reveal.textContent = shown ? 'Show' : 'Hide';
reveal.setAttribute('aria-pressed', String(!shown));
pass.focus();
});
/* -- the gate --------------------------------------------------------- */
var form = document.getElementById('signin');
var gate = document.getElementById('gate');
var notice = document.getElementById('notice');
var unlock = form.querySelector('.unlock');
var email = document.getElementById('email');
function refuse(message) {
notice.textContent = message;
gate.classList.remove('is-refused');
void gate.offsetWidth; // restart the shake
gate.classList.add('is-refused');
}
form.addEventListener('submit', function (e) {
e.preventDefault();
if (!email.value.trim()) {
refuse('Enter the email address the invite was sent to.');
email.focus();
return;
}
if (!pass.value) {
refuse('Enter your passphrase to open the archive.');
pass.focus();
return;
}
unlock.classList.remove('is-firing');
void unlock.offsetWidth;
unlock.classList.add('is-firing');
notice.textContent = '';
window.setTimeout(function () {
refuse('That passphrase does not match this archive. Check the email address too.');
pass.value = '';
}, 520);
});
unlock.addEventListener('animationend', function () {
unlock.classList.remove('is-firing');
});
document.querySelector('[data-passkey]').addEventListener('click', function () {
refuse('No passkey is registered on this device. Sign in with your passphrase, then add one from Settings.');
});
})();