diff --git a/bookwyrm/static/css/bookwyrm.css b/bookwyrm/static/css/bookwyrm.css index aa49e640..f8d3b531 100644 --- a/bookwyrm/static/css/bookwyrm.css +++ b/bookwyrm/static/css/bookwyrm.css @@ -8,6 +8,41 @@ body { flex-direction: column; } +button { + border: none; + margin: 0; + padding: 0; + width: auto; + overflow: visible; + background: transparent; + + /* inherit font, color & alignment from ancestor */ + color: inherit; + font: inherit; + text-align: inherit; + + /* Normalize `line-height`. Cannot be changed from `normal` in Firefox 4+. */ + line-height: normal; + + /* Corrects font smoothing for webkit */ + -webkit-font-smoothing: inherit; + -moz-osx-font-smoothing: inherit; + + /* Corrects inability to style clickable `input` types in iOS */ + -webkit-appearance: none; +} + +button::-moz-focus-inner { + /* Remove excess padding and border in Firefox 4+ */ + border: 0; + padding: 0; +} + +/* Better accessibility for keyboard users */ +*:focus-visible { + outline-style: auto !important; +} + .image { overflow: hidden; } @@ -29,10 +64,30 @@ body { overflow-x: auto; } +/* stylelint-disable no-descending-specificity */ +.modal-card:focus { + outline-style: auto; +} + +.modal-card:focus:not(:focus-visible) { + outline-style: initial; +} + +.modal-card:focus-visible { + outline-style: auto; +} +/* stylelint-enable no-descending-specificity */ + .modal-card.is-fullwidth { min-width: 75% !important; } +@media only screen and (min-width: 769px) { + .modal-card.is-thin { + width: 350px !important; + } +} + .modal-card-body { max-height: 70vh; } diff --git a/bookwyrm/static/js/bookwyrm.js b/bookwyrm/static/js/bookwyrm.js index c79471fe..9fbd4dec 100644 --- a/bookwyrm/static/js/bookwyrm.js +++ b/bookwyrm/static/js/bookwyrm.js @@ -45,6 +45,12 @@ let BookWyrm = new class { 'change', this.disableIfTooLarge.bind(this) )); + + document.querySelectorAll('button[data-modal-open]') + .forEach(node => node.addEventListener( + 'click', + this.handleModalButton.bind(this) + )); document.querySelectorAll('[data-duplicate]') .forEach(node => node.addEventListener( @@ -420,6 +426,97 @@ let BookWyrm = new class { } } + /** + * Handle the modal component. + * + * @param {Event} event - Event fired by an element + * with the `data-modal-open` attribute + * pointing to a modal by its id. + * @return {undefined} + * + * See https://github.com/bookwyrm-social/bookwyrm/pull/1633 + * for information about using the modal. + */ + handleModalButton(event) { + const modalButton = event.currentTarget; + const targetModalId = modalButton.dataset.modalOpen; + const htmlElement = document.querySelector('html'); + const modal = document.getElementById(targetModalId); + + if (!modal) { + return; + } + + // Helper functions + function handleModalOpen(modalElement) { + htmlElement.classList.add('is-clipped'); + modalElement.classList.add('is-active'); + modalElement.getElementsByClassName('modal-card')[0].focus(); + + const closeButtons = modalElement.querySelectorAll("[data-modal-close]"); + + closeButtons.forEach((button) => { + button.addEventListener( + 'click', + function() { handleModalClose(modalElement) } + ); + }); + + document.addEventListener( + 'keydown', + function(event) { + if (event.key === 'Escape') { + handleModalClose(modalElement); + } + } + ); + + modalElement.addEventListener('keydown', handleFocusTrap) + } + + function handleModalClose(modalElement) { + modalElement.removeEventListener('keydown', handleFocusTrap) + htmlElement.classList.remove('is-clipped'); + modalElement.classList.remove('is-active'); + modalButton.focus(); + } + + function handleFocusTrap(event) { + if (event.key !== 'Tab') { + return; + } + + const focusableEls = event.currentTarget.querySelectorAll( + [ + 'a[href]:not([disabled])', + 'button:not([disabled])', + 'textarea:not([disabled])', + 'input:not([type="hidden"]):not([disabled])', + 'select:not([disabled])', + 'details:not([disabled])', + '[tabindex]:not([tabindex="-1"]):not([disabled])' + ].join(',') + ); + const firstFocusableEl = focusableEls[0]; + const lastFocusableEl = focusableEls[focusableEls.length - 1]; + + if (event.shiftKey ) /* Shift + tab */ { + if (document.activeElement === firstFocusableEl) { + lastFocusableEl.focus(); + event.preventDefault(); + } + } else /* Tab */ { + if (document.activeElement === lastFocusableEl) { + firstFocusableEl.focus(); + event.preventDefault(); + } + } + } + + // Open modal + handleModalOpen(modal); + } + /** * Display pop up window. * diff --git a/bookwyrm/templates/layout.html b/bookwyrm/templates/layout.html index d7710a2a..43e8eb22 100644 --- a/bookwyrm/templates/layout.html +++ b/bookwyrm/templates/layout.html @@ -38,7 +38,7 @@ -