Files
amare/resources/js/app.js
Manoel Freitas 2fe262ab59 Hardening public blades: overflow, fallbacks, branded errors, JS robustness (#14)
* fix: prevent long strings from overflowing public layout

* fix: fall back to default hero copy and tighten hero line-height

* fix: skip blank testimonials and guard empty portfolio metadata

* feat: add branded 419, 429 and 503 error pages

* fix: reset submit state on bfcache restore, swap label while sending, trap mobile menu focus

* chore: track hardening task status

* fix: restore contact submit state after bfcache

* fix: allow contact links to wrap long unbroken strings
2026-08-06 00:22:53 -03:00

109 lines
3.8 KiB
JavaScript

import './motion.js';
document.addEventListener('DOMContentLoaded', () => {
const menuButton = document.querySelector('[data-menu-button]');
const navigation = document.querySelector('[data-main-nav]');
if (menuButton && navigation) {
const isDesktop = () => window.matchMedia('(min-width: 768px)').matches;
const isMenuOpen = () => navigation.classList.contains('is-open');
const setOpen = (isOpen, { returnFocus = false } = {}) => {
navigation.classList.toggle('is-open', isOpen);
navigation.classList.toggle('hidden', !isOpen && !isDesktop());
navigation.classList.toggle('flex', isOpen || isDesktop());
document.body.classList.toggle('menu-open', isOpen);
menuButton.setAttribute('aria-expanded', String(isOpen));
menuButton.setAttribute('aria-label', isOpen ? 'Fechar menu' : 'Abrir menu');
if (!isOpen && returnFocus) {
menuButton.focus();
}
};
menuButton.addEventListener('click', () => {
const isOpen = menuButton.getAttribute('aria-expanded') !== 'true';
setOpen(isOpen);
if (isOpen) {
const firstLink = navigation.querySelector('a');
if (firstLink) {
firstLink.focus();
}
}
});
navigation.querySelectorAll('a').forEach((link) => {
link.addEventListener('click', () => setOpen(false));
});
navigation.addEventListener('keydown', (event) => {
if (event.key === 'Escape') {
setOpen(false, { returnFocus: true });
return;
}
if (event.key === 'Tab' && !isDesktop() && isMenuOpen()) {
const focusables = navigation.querySelectorAll('a[href], button:not([disabled])');
if (focusables.length === 0) {
return;
}
const first = focusables[0];
const last = focusables[focusables.length - 1];
if (event.shiftKey && document.activeElement === first) {
event.preventDefault();
last.focus();
} else if (!event.shiftKey && document.activeElement === last) {
event.preventDefault();
first.focus();
}
}
});
window.matchMedia('(min-width: 768px)').addEventListener('change', (event) => {
setOpen(false);
if (event.matches) {
navigation.classList.remove('hidden');
navigation.classList.add('flex');
}
});
}
document.querySelectorAll('form[data-contact-form]').forEach((form) => {
const submitButton = form.querySelector('[data-submit-button]');
const resetSubmitState = () => {
form.removeAttribute('aria-busy');
form.removeAttribute('aria-disabled');
if (submitButton) {
submitButton.disabled = false;
submitButton.textContent = submitButton.dataset.defaultLabel ?? 'Enviar briefing';
submitButton.removeAttribute('aria-busy');
}
};
form.addEventListener('submit', () => {
if (submitButton) {
submitButton.dataset.defaultLabel ??= submitButton.textContent.trim();
submitButton.textContent = 'Enviando…';
submitButton.disabled = true;
submitButton.setAttribute('aria-busy', 'true');
}
form.setAttribute('aria-busy', 'true');
});
window.addEventListener('pageshow', (event) => {
if (event.persisted) {
resetSubmitState();
}
});
});
});