natura11y 2.2.0 → 2.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/natura11y.js CHANGED
@@ -1 +1 @@
1
- !function(){"use strict";let t,e=0,i=document.querySelector("html");const s=s=>{t=document.activeElement,e=window.scrollY,i.style.setProperty("--scroll-position",`-${e}px`),i.classList.add("has-overlay"),s&&"true"===s.getAttribute("aria-hidden")&&s.setAttribute("aria-hidden",!1),o(s)},a=s=>{i.removeAttribute("style"),i.classList.remove("has-overlay"),i.classList.length||i.removeAttribute("class"),s&&"false"===s.getAttribute("aria-hidden")&&s.setAttribute("aria-hidden",!0),window.scrollTo({top:e,behavior:"instant"}),t.focus()},n=function(){return[...(arguments.length>0&&void 0!==arguments[0]?arguments[0]:document).querySelectorAll(["a[href]","area","button","details","frame","iframe","input","object","summary","textarea","select",'[tabindex]:not([tabindex="-1"])'])].filter((t=>!t.hasAttribute("disabled")&&!t.getAttribute("aria-hidden")))},o=function(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:t,i=n(t),s=i[0],o=i[i.length-1];e.setAttribute("tabindex","-1"),e.focus(),t.addEventListener("keydown",(e=>{switch(e.code){case"Tab":document.activeElement===o&&(e.shiftKey||(e.preventDefault(),s.focus())),document.activeElement===s&&e.shiftKey&&(e.preventDefault(),o.focus());break;case"Escape":a(t)}}))};class l{#t=document.querySelectorAll(".accordion");#e(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:document,e=arguments.length>1&&void 0!==arguments[1]&&arguments[1];n(t).forEach((t=>{t.setAttribute("tabindex",e?0:-1)}))}#i=(t,e,i)=>s=>{s.preventDefault(),s.stopPropagation();for(const t of i)t.classList.remove("show"),t!==e&&(t.classList.remove("shown"),t.style.maxHeight=null,t.previousElementSibling.setAttribute("aria-expanded",!1),t.setAttribute("aria-hidden",!0),this.#e(t,!1));e.classList.toggle("shown");let a=t.getAttribute("aria-expanded");"true"===a?(t.setAttribute("aria-expanded",!1),e.setAttribute("aria-hidden",!0),this.#e(e,!1)):"false"===a&&(t.setAttribute("aria-expanded",!0),e.setAttribute("aria-hidden",!1),this.#e(e,!0)),e.style.maxHeight?e.style.maxHeight=null:(e.style.maxHeight=e.scrollHeight+"px",e.setAttribute("aria-hidden",!1));let n=new Event("accTrigger",{bubbles:!0});document.dispatchEvent(n)};#s=(t,e,i)=>t=>{const s=s=>{t.preventDefault();let a=i+s;-1===s&&a<0?e[e.length-1].focus():1===s&&a>=e.length?e[0].focus():e[a].focus()};switch(t.code){case"ArrowLeft":case"ArrowUp":s(-1);break;case"ArrowRight":case"ArrowDown":s(1)}};#a=(t,e,i)=>s=>{"Enter"===s.code&&"BUTTON"!==s.target.tagName&&this.#i(t,e,i)(s)};init(){this.#t.forEach((t=>{const e=Array.from(t.querySelectorAll(':scope > [data-accordion="button"]')),i=t.querySelectorAll(':scope > [data-accordion="panel"]');t.addEventListener("click",(t=>{const s=t.target.closest('[data-accordion="button"]');if(!s)return;if(-1===e.indexOf(s))return;const a=s.nextElementSibling;this.#i(s,a,i)(t)})),t.addEventListener("keydown",(t=>{const i=t.target.closest('[data-accordion="button"]');if(!i)return;const s=e.indexOf(i);-1!==s&&this.#s(i,e,s)(t)})),t.addEventListener("keyup",(t=>{const s=t.target.closest('[data-accordion="button"]');if(!s)return;e.indexOf(s);const a=s.nextElementSibling;this.#a(s,a,i)(t)})),e.forEach(((t,e)=>{const i=t.nextElementSibling;let s=t.getAttribute("aria-expanded");t.setAttribute("tabindex",0),"true"===s?(i.style.maxHeight=i.scrollHeight+"px",i.classList.add("show"),this.#e(i,!0)):(t.setAttribute("aria-expanded",!1),i.style.maxHeight=null,i.setAttribute("aria-hidden",!0),this.#e(i,!1))}))}))}}class r{#n=document.querySelectorAll(".alert--dismissable");#o='\n <button class="button button--icon-only">\n <span class="icon icon-close" aria-label="Close" aria-hidden="true">\n </button>\n ';#l=t=>e=>{e.preventDefault(),t.classList.add("dismissed"),document.querySelector(".dismissed").addEventListener("animationend",(()=>{t.remove()}))};init(){this.#n.forEach((t=>{t.insertAdjacentHTML("afterbegin",this.#o),t.querySelector("button").addEventListener("click",this.#l(t))}))}}class d{#r=document.querySelectorAll(".button--icon-only");#d=t=>e=>setTimeout((()=>{t.forEach((t=>{t.classList.remove("tooltip-show")})),e.target.classList.add("tooltip-show")}),300);#c=()=>(t,e)=>{clearTimeout(e),t.target.classList.remove("tooltip-show")};#h(t,e){const i=e.offsetWidth/2,s=t.offsetLeft,a=window.innerWidth-(t.offsetLeft+t.offsetWidth);i>s&&e.classList.add("left"),i>a&&e.classList.add("right")}#u=(t,e)=>{const i=`\n <span class="button__tooltip">\n ${e}\n </span>\n `;if(e){t.insertAdjacentHTML("beforeend",i);const e=t.querySelector(".button__tooltip");let s;this.#h(t,e),window.addEventListener("resize",(()=>this.#h(t,e))),t.addEventListener("mouseenter",(t=>{s=this.#d(this.#r)(t)})),t.addEventListener("focusin",(t=>{s=this.#d(this.#r)(t)})),t.addEventListener("mouseleave",(t=>{this.#c()(t,s)})),t.addEventListener("focusout",(t=>{this.#c()(t,s)}))}};init(){this.#r.forEach((t=>{const e=t.getAttribute("aria-label");this.#u(t,e)}))}}class c{#b=document.querySelectorAll("[data-target-toggle]");#g(t,e){t.setAttribute("aria-expanded",!1),e.classList.remove("shown")}#p(t,e,i){t.setAttribute("aria-expanded",!0),e.classList.add("shown"),i&&i.focus()}#s(t,e,i){return s=>{switch(s.code){case"Tab":document.activeElement===i&&s.shiftKey&&(s.preventDefault(),t.focus());break;case"Escape":this.#g(t,e)}}}#m(t,e){const i=t.target.getAttribute("data-target-toggle").replace(/#/,""),s=document.getElementById(i),a=n(s)[0],o=e.getAttribute("aria-expanded");"true"===o?this.#g(e,s):"false"===o&&this.#p(e,s,s.hasAttribute("data-focus-first")?a:null),s.addEventListener("keydown",this.#s(e,s,a))}init(){this.#b.forEach((t=>{t.setAttribute("aria-expanded",!1),t.addEventListener("click",(e=>{if(this.#m(e,t),t.hasAttribute("data-target-close")){const t=e.target.getAttribute("data-target-close").replace(/#/,""),i=document.getElementById(t),s=document.querySelector(`[data-target-toggle="#${t}"]`);this.#g(s,i)}}))}))}}class h{#f=document.querySelectorAll(".form-entry");#L=["is-invalid"];#v=!1;#y(){return""===(arguments.length>0&&void 0!==arguments[0]?arguments[0]:null)}#x(t){t.closest(".form-entry").classList.add(...this.#L)}#E(t){t.closest(".form-entry").classList.remove(...this.#L)}#A(t){return this.#y(t.value)?(this.#x(t),!0):(this.#E(t),!1)}#w(t){return e=>{e.target.closest(t).classList.add("active")}}#S(t){return e=>{e.target.closest(t).classList.remove("active")}}#C(t,e){this.#v&&e&&this.#A(t),""!==t.value?t.closest(".form-entry").classList.add("has-value"):t.closest(".form-entry").classList.remove("has-value")}#_(t){const e=t.querySelectorAll(["email","input","select","tel","textarea"]);let i=t.hasAttribute("data-required");e.forEach((t=>this.#q(t,i)))}#q(t,e){const i=t.closest(".form-entry").querySelector(".form-entry__field__input");let s=".form-entry";if("INPUT"===t.tagName){const e=t.getAttribute("type");"radio"!==e&&"checkbox"!==e||t.disabled&&t.closest("label").classList.add("disabled")}t.addEventListener("focusin",this.#w(s)),t.addEventListener("focusout",this.#S(s)),e&&(t.setAttribute("required","true"),t.setAttribute("aria-required",!0)),t.addEventListener("change",(()=>this.#C(t,e))),i&&i.addEventListener("click",this.handleClickOnInputText)}handleClickOnInputText(t){let e=t.target.tagName,i=t.target.closest(".form-entry__field__input").querySelector("input");"SPAN"===e&&i.focus()}init(){this.#f.forEach((t=>this.#_(t)))}}class u{#T=document.querySelectorAll("form[novalidate]");#L=["is-invalid"];#v=!1;#y(){return""===(arguments.length>0&&void 0!==arguments[0]?arguments[0]:null)}#x(t){t.closest(".form-entry").classList.add(...this.#L)}#E(t){t.closest(".form-entry").classList.remove(...this.#L)}#A(t){return this.#y(t.value)?(this.#x(t),!0):(this.#E(t),!1)}#k(t,e){return null===t&&(t="This field is Required"),`\n <small class="form-entry__feedback">\n <span class="icon icon-warn" aria-hidden="true"></span>\n <span class="message">\n <strong>${t}</strong> ${void 0!==e?e:""}\n </span>\n </small>\n `}#H(t,e){t.forEach((t=>{let i=t.closest(".form-entry"),s=i.querySelector(".form-entry__field__label");i.classList.add("is-invalid");const a=i.querySelector(".form-entry__feedback"),n=i.querySelector(".form-entry__help");let o;n&&(o=n.innerHTML.toString());let l=i.getAttribute("data-error-message"),r=[l,o];e.push(r),null===a&&s.insertAdjacentHTML("afterend",this.#k(l,o))}))}#I(t){let e=t.querySelector('[class*="alert"], [class*="invalid"]');if(e){e.hasAttribute("data-alert")&&(e.style.display="block");let t=e.offsetTop-16;window.scrollTo({top:t,behavior:"smooth"})}}#M(t){t.addEventListener("submit",(e=>{e.preventDefault(),this.#v=!0;let i=[],s=t.querySelectorAll("input, select, textarea");s.forEach((t=>{t.addEventListener("input",(()=>this.#A(t)))})),s.forEach((t=>{this.#A(t)}));let a=t.querySelectorAll(":invalid");this.#H(a,i),i.length>0&&e.preventDefault(),this.#I(t)}))}init(){this.#T.forEach((t=>this.#M(t)))}}class b{#D=document.querySelectorAll(".file-upload");#F(t){return function(e){const[i]=e.target.files,{name:s,size:a}=i,n=`\n <span class="file-upload__data">\n <span class="file-name">${s}</span>\n <span class="file-size">${(a/1e3).toFixed(2)} kb</span>\n </span>\n `,o=t.querySelector(".file-upload__data");o&&o.remove(),t.insertAdjacentHTML("beforeend",n)}}dragOver(t){t.target.closest(".form-entry").classList.add("active")}dragOff(t){t.target.closest(".form-entry").classList.remove("active")}dropped(t){t.target.closest(".form-entry").classList.remove("active")}#O(t){t.querySelector('input[type="file"]').addEventListener("change",this.#F(t)),t.addEventListener("dragenter",this.dragOver.bind(this)),t.addEventListener("dragleave",this.dragOff.bind(this)),t.addEventListener("dragend",this.dragOff.bind(this)),t.addEventListener("drop",this.dropped.bind(this))}init(){this.#D.forEach((t=>this.#O(t)))}}class g{#B=document.querySelectorAll("[data-lightbox]");#U='\n\t\t<div class="lightbox__buttons button-group">\n\t\t<button class="button button--icon-only" data-lightbox-previous>\n\t\t\t<span class="icon icon-arrow-left" aria-label="Previous" aria-hidden="true"></span>\n\t\t</button>\n\t\t<button class="button button--icon-only" data-lightbox-next>\n\t\t\t<span class="icon icon-arrow-right" aria-label="Next" aria-hidden="true"></span>\n\t\t</button>\n\t\t<button class="button button--icon-only" data-lightbox-close>\n\t\t\t<span class="icon icon-close" aria-label="Close" aria-hidden="true"></span>\n\t\t</button>\n\t\t</div>\n\t\t<figure class="lightbox__container" aria-live="polite" aria-atomic="true">\n\t\t<div class="lightbox__media"></div> \n\t\t<figcaption class="lightbox__caption"></figcaption>\n\t\t</figure>\n \t';#N='\n\t\t<video controls>\n\t\t<source type="video/mp4">\n\t\t</video>\n\t';#P='\n\t\t<iframe\n\t\t\tframeborder="0"\n\t\t\tallow="autoplay; fullscreen;"\n\t\t\tallowfullscreen\n\t\t></iframe>\n\t';#$='\n\t\t<div class="lightbox__media__loader">\n\t\t\t<span class="icon icon-loading icon--rotate" aria-hidden="true"></span>\n\t\t</div>\n\t\t<div class="lightbox__media__error" style="display: none;">\n\t\t\t<span class="icon icon-warn" aria-hidden="true"></span>\n\t\t\t<p>Failed to load content. Please try again later.</p>\n\t\t</div>\n\t';#K='<img src="https://source.unsplash.com/1600x900" />';#V=[];#R=t=>e=>{document.querySelector(".lightbox")||(e.preventDefault(),this.lightbox=this.#W(),this.currentLB=t,s(this.lightbox),this.#z(t))};#j=t=>{t.stopPropagation(),t.target!==t.currentTarget&&"click"===t.type||(a(this.lightbox),this.lightbox.parentElement.removeChild(this.lightbox),window.removeEventListener("keyup",this.#Y))};#G=(()=>{var t=this;return function(){let e=arguments.length>0&&void 0!==arguments[0]&&arguments[0];t.lightbox.querySelector(".lightbox__caption").style.display=e?"block":"none"}})();#J=t=>{if(t.preventDefault(),t.target.hasAttribute("data-lightbox-previous"))this.#Q(-1);else{if(!t.target.hasAttribute("data-lightbox-next"))return;this.#Q(1)}};#Y=t=>{if(t.preventDefault(),!(this.#V.length<=1)||"ArrowLeft"!==t.code&&"ArrowRight"!==t.code)switch(t.code){case"ArrowLeft":this.#Q(-1),this.lightbox.querySelector("[data-lightbox-previous]").focus();break;case"ArrowRight":this.#Q(1),this.lightbox.querySelector("[data-lightbox-next]").focus();break;case"Escape":this.#j(t);break;default:return}};#Q(t){this.currentLB+=t,this.currentLB<0?this.currentLB=this.#V.length-1:this.currentLB>=this.#V.length&&(this.currentLB=0),this.#z(this.currentLB)}#z(t){const e=this.lightbox.querySelector(".lightbox__media"),i=this.lightbox.querySelector(".lightbox__caption");let s;e.innerHTML="";const{lbType:a,lbSrc:n,lbAlt:o,lbCaption:l}=this.#V[t],r=null!==l;switch(this.#G(r),a){case"image":s=this.#X(e,n,o);break;case"video":s=this.#Z(e,n)}r&&(i.innerHTML=l)}#X(t,e,i){t.hasAttribute("style")&&t.removeAttribute("style"),t.innerHTML=this.#K;const s=this.#tt();t.appendChild(s);const a=t.querySelector("img");return a.alt=i,a.src=e,this.#et(a,s),a}#Z(t,e){const i=/youtube/i.test(e),s=/vimeo/i.test(e);let a;if(i||s)t.innerHTML=this.#P,a=t.querySelector("iframe"),a.src=e;else{t.innerHTML=this.#N;const i=this.#tt();t.appendChild(i),a=t.querySelector("source");const s=t.querySelector("video");s.addEventListener("loadedmetadata",(()=>{let e=s.videoWidth,i=s.videoHeight;t.style.maxWidth=`${e}px`,t.style.aspectRatio=`${e} / ${i}`})),this.#et(a,i),a.src=e}return a}#W(){const t=document.createElement("div");t.classList.add("lightbox"),t.setAttribute("aria-hidden",!0),t.innerHTML=this.#U,document.body.appendChild(t);const e=t.querySelector("[data-lightbox-previous]"),i=t.querySelector("[data-lightbox-next]"),s=t.querySelector("[data-lightbox-close]");return this.#V.length<=1&&(e.setAttribute("disabled",!0),i.setAttribute("disabled",!0),e.style.display="none",i.style.display="none"),t.addEventListener("click",this.#j),s.addEventListener("click",this.#j),e.addEventListener("click",this.#J),i.addEventListener("click",this.#J),window.addEventListener("keyup",this.#Y),t}#it(t){let e=null,i="";return null!==t.querySelector("img")&&(e=t.querySelector("img").src||null,i=t.querySelector("img").alt||""),{lbType:t.getAttribute("data-lightbox")||"image",lbSrc:t.getAttribute("data-lightbox-src")||e,lbCaption:t.getAttribute("data-lightbox-caption")||null,lbAlt:t.getAttribute("data-lightbox-alt")||i}}#st(){this.#B.forEach((t=>{this.#V.push(this.#it(t))}))}#tt=()=>{const t=document.createElement("div");return t.className="lightbox__media__loader",t.innerHTML=this.#$,t};#et=(t,e)=>{const i="SOURCE"===t.nodeName?"loadeddata":"load";t.closest("SOURCE"===t.nodeName?"video":"img").addEventListener(i,(()=>{e&&e.parentNode&&e.parentNode.removeChild(e),null!==this.#V[this.currentLB].lbCaption&&this.#G(!0)})),t.onerror=()=>{const i=e.querySelector(".lightbox__media__loader"),s=e.querySelector(".lightbox__media__error");t.style.display="none",this.#G(!1),i.style.display="none",s.style.display="block"}};#at(){const t=new IntersectionObserver(((t,e)=>{t.forEach((t=>{if(t.isIntersecting){const i=t.target,s=i.dataset.lightboxSrc||i.src;if(!s)return;e.unobserve(i);const a=new Image;a.onload=()=>{document.body.appendChild(a)},a.onerror=()=>{console.error(`Failed to load image: ${s}`)},a.src=s,a.style.display="none",this.#V[Number(i.dataset.index)].hiddenImage=a}}))}),{root:null,rootMargin:"0px",threshold:.1});Array.from(this.#B).filter((t=>"image"===t.getAttribute("data-lightbox"))).forEach(((e,i)=>{const s=e.querySelector("img");s&&(s.dataset.index=i,t.observe(s))}))}#nt(){this.#B.forEach(((t,e)=>{t.addEventListener("click",this.#R(e))}))}init(){this.#st(),this.#nt(),this.#at()}}class p{#ot=document.querySelectorAll(".modal");#lt=document.querySelectorAll("[data-modal-open]");#rt=new Map;#dt(t,e){window.addEventListener("click",e),this.#rt.set(t,e)}#ct(t){const e=this.#rt.get(t);e&&(window.removeEventListener("click",e),this.#rt.delete(t))}openModal(t){if(!t)return void console.warn("Modal target not found.");s(t);const e=t.querySelector(".modal__content");if(!e)return void console.warn("Modal content not found.");t.classList.contains("modal--scroll-all")&&(t.scrollTop=0);t.querySelectorAll("[data-modal-close]").forEach((e=>{e.addEventListener("click",(()=>{a(t),this.#ct(t)})),e.setAttribute("aria-label","Close Modal Window")})),"true"===t.dataset.modalCloseOutside&&this.#dt(t,(i=>{e.contains(i.target)||(a(t),this.#ct(t))}))}init(){this.#ot.forEach((t=>{const e=t.querySelector(".modal__content");e.setAttribute("role","dialog"),e.setAttribute("aria-modal",!0),t.setAttribute("aria-hidden",!0)})),this.#lt.forEach((t=>{t.addEventListener("click",(t=>{const e=t.target.getAttribute("data-modal-open").replace(/#/,""),i=document.getElementById(e);this.openModal(i),t.stopPropagation()}))}))}}class m{#ht=document.querySelectorAll('[data-toggle="dropdown"]');#ut(t,e){e.classList.toggle("shown"),t.setAttribute("aria-expanded","true"===t.getAttribute("aria-expanded")?"false":"true")}#bt(t,e){e.classList.remove("shown"),t.setAttribute("aria-expanded","false")}init(){window.addEventListener("click",(t=>{this.#ht.forEach((e=>{let i=e.closest("li"),s=e.nextElementSibling;i.contains(t.target)||this.#bt(e,s)}))})),this.#ht.forEach((t=>{let e=t.nextElementSibling;e?(t.setAttribute("aria-expanded","false"),t.setAttribute("aria-haspopup","true"),t.addEventListener("click",(i=>{i.preventDefault(),this.#ut(t,e)}))):console.warn(`No dropdown menu found for dropdown button ${t}`)}))}}class f{#gt=document.querySelectorAll('[class*="table--stack"]');#pt=document.querySelectorAll(".table-scroll");#mt(t){const e=t.querySelectorAll("thead th"),i=t.querySelectorAll("tbody tr");let s=[];e.forEach((t=>{if(""!==t.textContent){const e=t.textContent.trim();s.push(e)}})),i.forEach((t=>{t.querySelectorAll("td").forEach(((t,e)=>{t.innerHTML=this.#ft(t.innerHTML),t.setAttribute("data-header",s[e])}))}))}#ft(t){return`\n\t\t\t<div class="td-content">\n\t\t\t\t${t}\n\t\t\t</div>\n\t\t`}#Lt(){this.#pt.forEach((t=>{let e=t.querySelector(".table-scroll__container"),i=t.offsetWidth;e.scrollWidth>i?t.setAttribute("data-scroll",!0):t.setAttribute("data-scroll",!1),e.addEventListener("scroll",(()=>{e.scrollLeft>1?e.setAttribute("data-scrolling",!0):e.setAttribute("data-scrolling",!1)}),{passive:!0})}))}init(){this.#gt.forEach((t=>{this.#mt(t)})),this.#Lt(),window.addEventListener("resize",this.#Lt.bind(this))}}class L{#vt=document.querySelectorAll(".tabs");#yt(t,e,i,s){t.preventDefault();let a=e+s;-1===s&&a<0?i[i.length-1].focus():1===s&&a>=i.length?i[0].focus():i[a].focus()}#xt(t,e){t.forEach((t=>{t.setAttribute("aria-selected","false")})),e.forEach((t=>{t.classList.remove("shown"),t.setAttribute("hidden","")}))}activateTab(t,e,i){this.#xt(e,i),t.setAttribute("aria-selected","true");let s=t.getAttribute("aria-controls"),a=document.getElementById(s);a.classList.add("shown"),a.removeAttribute("hidden")}init(){this.#vt.forEach((t=>{const e=t.querySelectorAll('[role="tab"]'),i=t.querySelectorAll('[role="tabpanel"]');e.forEach(((t,s)=>{t.addEventListener("click",(t=>{let s=t.target;this.activateTab(s,e,i)})),t.addEventListener("keydown",(t=>{switch(t.code){case"Home":t.preventDefault(),e[0].focus();break;case"End":t.preventDefault(),e[e.length-1].focus();break;case"ArrowLeft":this.#yt(t,s,e,-1);break;case"ArrowRight":this.#yt(t,s,e,1)}}))}))}))}}document.addEventListener("DOMContentLoaded",(()=>{(new l).init(),(new r).init(),(new d).init(),(new c).init(),(new h).init(),(new u).init(),(new b).init(),(new g).init(),(new p).init(),(new m).init(),(new f).init(),(new L).init()}))}();
1
+ !function(){"use strict";let t,e=0,i=document.querySelector("html");const s=s=>{t=document.activeElement,e=window.scrollY,i.style.setProperty("--scroll-position",`-${e}px`),i.classList.add("has-overlay"),s&&"true"===s.getAttribute("aria-hidden")&&s.setAttribute("aria-hidden",!1),n(s)},a=s=>{i.removeAttribute("style"),i.classList.remove("has-overlay"),i.classList.length||i.removeAttribute("class"),s&&"false"===s.getAttribute("aria-hidden")&&s.setAttribute("aria-hidden",!0),window.scrollTo({top:e,behavior:"instant"}),t&&t.focus()},o=function(){return[...(arguments.length>0&&void 0!==arguments[0]?arguments[0]:document).querySelectorAll(["a[href]","area","button","details","frame","iframe","input","object","summary","textarea","select",'[tabindex]:not([tabindex="-1"])',"video","audio"])].filter((t=>!t.hasAttribute("disabled")))},n=function(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:t,i=o(t),s=i[0],n=i[i.length-1];e.setAttribute("tabindex","-1"),e.focus(),t.addEventListener("keydown",(e=>{switch(e.code){case"Tab":document.activeElement===n&&(e.shiftKey||(e.preventDefault(),s.focus())),document.activeElement===s&&e.shiftKey&&(e.preventDefault(),n.focus());break;case"Escape":a(t)}}))};class l{#t=document.querySelectorAll(".accordion");#e(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:document,e=arguments.length>1&&void 0!==arguments[1]&&arguments[1];o(t).forEach((t=>{t.setAttribute("tabindex",e?0:-1)}))}#i=(t,e,i)=>s=>{s.preventDefault(),s.stopPropagation();for(const t of i)t.classList.remove("show"),t!==e&&(t.classList.remove("shown"),t.style.maxHeight=null,t.previousElementSibling.setAttribute("aria-expanded",!1),t.setAttribute("aria-hidden",!0),this.#e(t,!1));e.classList.toggle("shown");let a=t.getAttribute("aria-expanded");"true"===a?(t.setAttribute("aria-expanded",!1),e.setAttribute("aria-hidden",!0),this.#e(e,!1)):"false"===a&&(t.setAttribute("aria-expanded",!0),e.setAttribute("aria-hidden",!1),this.#e(e,!0)),e.style.maxHeight?e.style.maxHeight=null:(e.style.maxHeight=e.scrollHeight+"px",e.setAttribute("aria-hidden",!1));let o=new Event("accTrigger",{bubbles:!0});document.dispatchEvent(o)};#s=(t,e,i)=>t=>{const s=s=>{t.preventDefault();let a=i+s;-1===s&&a<0?e[e.length-1].focus():1===s&&a>=e.length?e[0].focus():e[a].focus()};switch(t.code){case"ArrowLeft":case"ArrowUp":s(-1);break;case"ArrowRight":case"ArrowDown":s(1)}};#a=(t,e,i)=>s=>{"Enter"===s.code&&"BUTTON"!==s.target.tagName&&this.#i(t,e,i)(s)};init(){this.#t.forEach((t=>{const e=Array.from(t.querySelectorAll(':scope > [data-accordion="button"]')),i=t.querySelectorAll(':scope > [data-accordion="panel"]');t.addEventListener("click",(t=>{const s=t.target.closest('[data-accordion="button"]');if(!s)return;if(-1===e.indexOf(s))return;const a=s.nextElementSibling;this.#i(s,a,i)(t)})),t.addEventListener("keydown",(t=>{const i=t.target.closest('[data-accordion="button"]');if(!i)return;const s=e.indexOf(i);-1!==s&&this.#s(i,e,s)(t)})),t.addEventListener("keyup",(t=>{const s=t.target.closest('[data-accordion="button"]');if(!s)return;e.indexOf(s);const a=s.nextElementSibling;this.#a(s,a,i)(t)})),e.forEach(((t,e)=>{const i=t.nextElementSibling;let s=t.getAttribute("aria-expanded");t.setAttribute("tabindex",0),"true"===s?(i.style.maxHeight=i.scrollHeight+"px",i.classList.add("show"),this.#e(i,!0)):(t.setAttribute("aria-expanded",!1),i.style.maxHeight=null,i.setAttribute("aria-hidden",!0),this.#e(i,!1))}))}))}}class r{#o=document.querySelectorAll(".alert--dismissable");#n='\n <button class="button button--icon-only">\n <span class="icon icon-close" aria-label="Close" aria-hidden="true">\n </button>\n ';#l=t=>e=>{e.preventDefault(),t.classList.add("dismissed"),document.querySelector(".dismissed").addEventListener("animationend",(()=>{t.remove()}))};init(){this.#o.forEach((t=>{t.insertAdjacentHTML("afterbegin",this.#n),t.querySelector("button").addEventListener("click",this.#l(t))}))}}class d{#r=document.querySelectorAll(".button--icon-only");#d=t=>e=>setTimeout((()=>{t.forEach((t=>{t.classList.remove("tooltip-show")})),e.target.classList.add("tooltip-show")}),300);#c=()=>(t,e)=>{clearTimeout(e),t.target.classList.remove("tooltip-show")};#h(t,e){const i=e.offsetWidth/2,s=t.offsetLeft,a=window.innerWidth-(t.offsetLeft+t.offsetWidth);i>s&&e.classList.add("left"),i>a&&e.classList.add("right")}#u=(t,e)=>{const i=`\n <span class="button__tooltip">\n ${e}\n </span>\n `;if(e){t.insertAdjacentHTML("beforeend",i);const e=t.querySelector(".button__tooltip");let s;this.#h(t,e),window.addEventListener("resize",(()=>this.#h(t,e))),t.addEventListener("mouseenter",(t=>{s=this.#d(this.#r)(t)})),t.addEventListener("focusin",(t=>{s=this.#d(this.#r)(t)})),t.addEventListener("mouseleave",(t=>{this.#c()(t,s)})),t.addEventListener("focusout",(t=>{this.#c()(t,s)}))}};init(){this.#r.forEach((t=>{const e=t.getAttribute("aria-label");this.#u(t,e)}))}}class c{#b=document.querySelectorAll("[data-target-toggle]");#g(t,e){t.setAttribute("aria-expanded",!1),e.classList.remove("shown")}#p(t,e,i){t.setAttribute("aria-expanded",!0),e.classList.add("shown"),i&&i.focus()}#s(t,e,i){return s=>{switch(s.code){case"Tab":document.activeElement===i&&s.shiftKey&&(s.preventDefault(),t.focus());break;case"Escape":this.#g(t,e)}}}#m(t,e){const i=t.target.getAttribute("data-target-toggle").replace(/#/,""),s=document.getElementById(i),a=o(s)[0],n=e.getAttribute("aria-expanded");"true"===n?this.#g(e,s):"false"===n&&this.#p(e,s,s.hasAttribute("data-focus-first")?a:null),s.addEventListener("keydown",this.#s(e,s,a))}init(){this.#b.forEach((t=>{t.setAttribute("aria-expanded",!1),t.addEventListener("click",(e=>{if(this.#m(e,t),t.hasAttribute("data-target-close")){const t=e.target.getAttribute("data-target-close").replace(/#/,""),i=document.getElementById(t),s=document.querySelector(`[data-target-toggle="#${t}"]`);this.#g(s,i)}}))}))}}class h{#f=document.querySelectorAll(".form-entry");#L=["is-invalid"];#v=!1;#y(){return""===(arguments.length>0&&void 0!==arguments[0]?arguments[0]:null)}#x(t){t.closest(".form-entry").classList.add(...this.#L)}#E(t){t.closest(".form-entry").classList.remove(...this.#L)}#A(t){return this.#y(t.value)?(this.#x(t),!0):(this.#E(t),!1)}#w(t){return e=>{e.target.closest(t).classList.add("active")}}#S(t){return e=>{e.target.closest(t).classList.remove("active")}}#C(t,e){this.#v&&e&&this.#A(t),""!==t.value?t.closest(".form-entry").classList.add("has-value"):t.closest(".form-entry").classList.remove("has-value")}#_(t){const e=t.querySelectorAll(["email","input","select","tel","textarea"]);let i=t.hasAttribute("data-required");e.forEach((t=>this.#q(t,i)))}#q(t,e){const i=t.closest(".form-entry").querySelector(".form-entry__field__input");let s=".form-entry";if("INPUT"===t.tagName){const e=t.getAttribute("type");"radio"!==e&&"checkbox"!==e||t.disabled&&t.closest("label").classList.add("disabled")}t.addEventListener("focusin",this.#w(s)),t.addEventListener("focusout",this.#S(s)),e&&(t.setAttribute("required","true"),t.setAttribute("aria-required",!0)),t.addEventListener("change",(()=>this.#C(t,e))),i&&i.addEventListener("click",this.handleClickOnInputText)}handleClickOnInputText(t){let e=t.target.tagName,i=t.target.closest(".form-entry__field__input").querySelector("input");"SPAN"===e&&i.focus()}init(){this.#f.forEach((t=>this.#_(t)))}}class u{#T=document.querySelectorAll("form[novalidate]");#L=["is-invalid"];#v=!1;#y(){return""===(arguments.length>0&&void 0!==arguments[0]?arguments[0]:null)}#x(t){t.closest(".form-entry").classList.add(...this.#L)}#E(t){t.closest(".form-entry").classList.remove(...this.#L)}#A(t){return this.#y(t.value)?(this.#x(t),!0):(this.#E(t),!1)}#k(t,e){return null===t&&(t="This field is Required"),`\n <small class="form-entry__feedback">\n <span class="icon icon-warn" aria-hidden="true"></span>\n <span class="message">\n <strong>${t}</strong> ${void 0!==e?e:""}\n </span>\n </small>\n `}#H(t,e){t.forEach((t=>{let i=t.closest(".form-entry"),s=i.querySelector(".form-entry__field__label");i.classList.add("is-invalid");const a=i.querySelector(".form-entry__feedback"),o=i.querySelector(".form-entry__help");let n;o&&(n=o.innerHTML.toString());let l=i.getAttribute("data-error-message"),r=[l,n];e.push(r),null===a&&s.insertAdjacentHTML("afterend",this.#k(l,n))}))}#D(t){let e=t.querySelector('[class*="alert"], [class*="invalid"]');if(e){e.hasAttribute("data-alert")&&(e.style.display="block");let t=e.offsetTop-16;window.scrollTo({top:t,behavior:"smooth"})}}#I(t){t.addEventListener("submit",(e=>{e.preventDefault(),this.#v=!0;let i=[],s=t.querySelectorAll("input, select, textarea");s.forEach((t=>{t.addEventListener("input",(()=>this.#A(t)))})),s.forEach((t=>{this.#A(t)}));let a=t.querySelectorAll(":invalid");this.#H(a,i),i.length>0&&e.preventDefault(),this.#D(t)}))}init(){this.#T.forEach((t=>this.#I(t)))}}class b{#M=document.querySelectorAll(".file-upload");#F(t){return function(e){const[i]=e.target.files,{name:s,size:a}=i,o=`\n <span class="file-upload__data">\n <span class="file-name">${s}</span>\n <span class="file-size">${(a/1e3).toFixed(2)} kb</span>\n </span>\n `,n=t.querySelector(".file-upload__data");n&&n.remove(),t.insertAdjacentHTML("beforeend",o)}}dragOver(t){t.target.closest(".form-entry").classList.add("active")}dragOff(t){t.target.closest(".form-entry").classList.remove("active")}dropped(t){t.target.closest(".form-entry").classList.remove("active")}#O(t){t.querySelector('input[type="file"]').addEventListener("change",this.#F(t)),t.addEventListener("dragenter",this.dragOver.bind(this)),t.addEventListener("dragleave",this.dragOff.bind(this)),t.addEventListener("dragend",this.dragOff.bind(this)),t.addEventListener("drop",this.dropped.bind(this))}init(){this.#M.forEach((t=>this.#O(t)))}}class g{#B=document.querySelectorAll("[data-lightbox]");#N='\n\t\t<figure class="lightbox__container" aria-live="polite" aria-atomic="true">\n\t\t\t<div class="lightbox__media"></div> \n\t\t\t<figcaption class="lightbox__caption"></figcaption>\n\t\t</figure>\n\t\t<div class="lightbox__controls">\n\t\t\t<button class="button button--icon-only" data-lightbox-previous>\n\t\t\t\t<span class="icon icon-arrow-left" aria-label="Previous" aria-hidden="true"></span>\n\t\t\t</button>\n\t\t\t<button class="button button--icon-only" data-lightbox-next>\n\t\t\t\t<span class="icon icon-arrow-right" aria-label="Next" aria-hidden="true"></span>\n\t\t\t</button>\n\t\t\t<button class="button button--icon-only" data-lightbox-close>\n\t\t\t\t<span class="icon icon-close" aria-label="Close" aria-hidden="true"></span>\n\t\t\t</button>\n\t\t</div>\n \t';#U='\n\t\t<video controls tabindex="0">\n\t\t\t<source type="video/mp4">\n\t\t</video>\n\t';#P='\n\t\t<iframe\n\t\t\tframeborder="0"\n\t\t\tallow="autoplay; fullscreen;"\n\t\t\tallowfullscreen\n\t\t\tcontrols\n\t\t\ttabindex="0"\n\t\t></iframe>\n\t';#$='\n\t\t<div class="lightbox__media__loader">\n\t\t\t<span class="icon icon-loading icon--rotate" aria-hidden="true"></span>\n\t\t</div>\n\t\t<div class="lightbox__media__error" style="display: none;">\n\t\t\t<span class="icon icon-warn" aria-hidden="true"></span>\n\t\t\t<p>Failed to load content. Please try again later.</p>\n\t\t</div>\n\t';#K='<img src="https://source.unsplash.com/1600x900" />';#V=[];#R=t=>e=>{document.querySelector(".lightbox")||(e.preventDefault(),this.lightbox=this.#W(),this.currentLB=t,this.#z(t),s(this.lightbox))};#j=t=>{t.stopPropagation(),t.target!==t.currentTarget&&"click"===t.type||(a(this.lightbox),this.lightbox.parentElement.removeChild(this.lightbox),window.removeEventListener("keyup",this.#Y))};#G=(()=>{var t=this;return function(){let e=arguments.length>0&&void 0!==arguments[0]&&arguments[0];t.lightbox.querySelector(".lightbox__caption").style.display=e?"block":"none"}})();#J=t=>{if(t.preventDefault(),t.target.hasAttribute("data-lightbox-previous"))this.#Q(-1);else{if(!t.target.hasAttribute("data-lightbox-next"))return;this.#Q(1)}};#Y=t=>{if(t.preventDefault(),!(this.#V.length<=1)||"ArrowLeft"!==t.code&&"ArrowRight"!==t.code)switch(t.code){case"ArrowLeft":this.#Q(-1),this.lightbox.querySelector("[data-lightbox-previous]").focus();break;case"ArrowRight":this.#Q(1),this.lightbox.querySelector("[data-lightbox-next]").focus();break;case"Escape":this.#j(t);break;default:return}};#X=t=>{t.setAttribute("tabindex",0),t.addEventListener("focus",(e=>{e.preventDefault(),t.children[0].focus(),o(t.children[0])}))};#Q(t){this.currentLB+=t,this.currentLB<0?this.currentLB=this.#V.length-1:this.currentLB>=this.#V.length&&(this.currentLB=0),this.#z(this.currentLB)}#z(t){const e=this.lightbox.querySelector(".lightbox__media"),i=this.lightbox.querySelector(".lightbox__caption");let s;e.innerHTML="";const{lbType:a,lbSrc:o,lbAlt:l,lbCaption:r}=this.#V[t],d=null!==r;switch(this.#G(d),a){case"image":s=this.#Z(e,o,l);break;case"video":s=this.#tt(e,o)}this.#X(e),d&&(i.innerHTML=r),n(this.lightbox)}#Z(t,e,i){t.hasAttribute("style")&&t.removeAttribute("style"),t.innerHTML=this.#K;const s=this.#et();t.appendChild(s);const a=t.querySelector("img");return a.alt=i,a.src=e,this.#it(a,s),a}#tt(t,e){const i=/youtube/i.test(e),s=/vimeo/i.test(e);let a;if(i||s)t.innerHTML=this.#P,a=t.querySelector("iframe"),a.src=e;else{t.innerHTML=this.#U;const i=this.#et();t.appendChild(i),a=t.querySelector("source");const s=t.querySelector("video");s.addEventListener("loadedmetadata",(()=>{let e=s.videoWidth,i=s.videoHeight;t.style.maxWidth=`${e}px`,t.style.aspectRatio=`${e} / ${i}`})),this.#it(a,i),a.src=e}return a}#W(){const t=document.createElement("div");t.classList.add("lightbox"),t.setAttribute("aria-hidden",!0),t.setAttribute("aria-live","polite"),t.innerHTML=this.#N,document.body.appendChild(t);const e=t.querySelector("[data-lightbox-previous]"),i=t.querySelector("[data-lightbox-next]"),s=t.querySelector("[data-lightbox-close]");return this.#V.length<=1&&(e.setAttribute("disabled",!0),i.setAttribute("disabled",!0),e.style.display="none",i.style.display="none"),t.addEventListener("click",this.#j),s.addEventListener("click",this.#j),e.addEventListener("click",this.#J),i.addEventListener("click",this.#J),window.addEventListener("keyup",this.#Y),t}#st(t){if(!t)return void console.error("No lightbox button provided");let e=null,i="";if(null!==t.querySelector("img")){const s=t.querySelector("img");e=s.src||null,i=s.alt||""}const s=t.getAttribute("data-lightbox")||"image",a=t.getAttribute("data-lightbox-src")||e,o=t.getAttribute("data-lightbox-caption")||null,n=t.getAttribute("data-lightbox-alt")||i;if(null!==a)return{lbType:s,lbSrc:a,lbCaption:o,lbAlt:n};console.error("No source provided for lightbox")}#at(){this.#B.forEach((t=>{this.#V.push(this.#st(t))}))}#et=()=>{const t=document.createElement("div");return t.className="lightbox__media__loader",t.innerHTML=this.#$,t};#it=(t,e)=>{const i="SOURCE"===t.nodeName?"loadeddata":"load";t.closest("SOURCE"===t.nodeName?"video":"img").addEventListener(i,(()=>{e&&e.parentNode&&e.parentNode.removeChild(e),null!==this.#V[this.currentLB].lbCaption&&this.#G(!0)})),t.onerror=()=>{const i=e.querySelector(".lightbox__media__loader"),s=e.querySelector(".lightbox__media__error");t.style.display="none",this.#G(!1),i.style.display="none",s.style.display="block"}};#ot(){const t=new IntersectionObserver(((t,e)=>{t.forEach((t=>{if(t.isIntersecting){const i=t.target,s=i.dataset.lightboxSrc||i.src;if(!s)return;e.unobserve(i);const a=new Image;a.onload=()=>{document.body.appendChild(a)},a.onerror=()=>{console.error(`Failed to load image: ${s}`)},a.src=s,a.style.display="none",this.#V[Number(i.dataset.index)].hiddenImage=a}}))}),{root:null,rootMargin:"0px",threshold:.1});Array.from(this.#B).filter((t=>"image"===t.getAttribute("data-lightbox"))).forEach(((e,i)=>{const s=e.querySelector("img");s&&(s.dataset.index=i,t.observe(s))}))}#nt(){this.#B.forEach(((t,e)=>{t.addEventListener("click",this.#R(e))}))}init(){this.#at(),this.#nt(),this.#ot()}}class p{#lt=document.querySelectorAll(".modal");#rt=document.querySelectorAll("[data-modal-open]");#dt=new Map;#ct(t,e){window.addEventListener("click",e),this.#dt.set(t,e)}#ht(t){const e=this.#dt.get(t);e&&(window.removeEventListener("click",e),this.#dt.delete(t))}openModal(t){if(!t)return void console.warn("Modal target not found.");s(t);const e=t.querySelector(".modal__content");if(!e)return void console.warn("Modal content not found.");t.classList.contains("modal--scroll-all")&&(t.scrollTop=0);t.querySelectorAll("[data-modal-close]").forEach((e=>{e.addEventListener("click",(()=>{a(t),this.#ht(t)})),e.setAttribute("aria-label","Close Modal Window")})),"true"===t.dataset.modalCloseOutside&&this.#ct(t,(i=>{e.contains(i.target)||(a(t),this.#ht(t))}))}init(){this.#lt.forEach((t=>{const e=t.querySelector(".modal__content");e.setAttribute("role","dialog"),e.setAttribute("aria-modal",!0),t.setAttribute("aria-hidden",!0)})),this.#rt.forEach((t=>{t.addEventListener("click",(t=>{const e=t.target.getAttribute("data-modal-open").replace(/#/,""),i=document.getElementById(e);this.openModal(i),t.stopPropagation()}))}))}}class m{#ut=document.querySelectorAll('[data-toggle="dropdown"]');#bt(t,e){e.classList.toggle("shown"),t.setAttribute("aria-expanded","true"===t.getAttribute("aria-expanded")?"false":"true")}#gt(t,e){e.classList.remove("shown"),t.setAttribute("aria-expanded","false")}init(){window.addEventListener("click",(t=>{this.#ut.forEach((e=>{let i=e.closest("li"),s=e.nextElementSibling;i.contains(t.target)||this.#gt(e,s)}))})),this.#ut.forEach((t=>{let e=t.nextElementSibling;e?(t.setAttribute("aria-expanded","false"),t.setAttribute("aria-haspopup","true"),t.addEventListener("click",(i=>{i.preventDefault(),this.#bt(t,e)}))):console.warn(`No dropdown menu found for dropdown button ${t}`)}))}}class f{#pt=document.querySelectorAll('[class*="table--stack"]');#mt=document.querySelectorAll(".table-scroll");#ft(t){const e=t.querySelectorAll("thead th"),i=t.querySelectorAll("tbody tr");let s=[];e.forEach((t=>{if(""!==t.textContent){const e=t.textContent.trim();s.push(e)}})),i.forEach((t=>{t.querySelectorAll("td").forEach(((t,e)=>{t.innerHTML=this.#Lt(t.innerHTML),t.setAttribute("data-header",s[e])}))}))}#Lt(t){return`\n\t\t\t<div class="td-content">\n\t\t\t\t${t}\n\t\t\t</div>\n\t\t`}#vt(){this.#mt.forEach((t=>{let e=t.querySelector(".table-scroll__container"),i=t.offsetWidth;e.scrollWidth>i?t.setAttribute("data-scroll",!0):t.setAttribute("data-scroll",!1),e.addEventListener("scroll",(()=>{e.scrollLeft>1?e.setAttribute("data-scrolling",!0):e.setAttribute("data-scrolling",!1)}),{passive:!0})}))}init(){this.#pt.forEach((t=>{this.#ft(t)})),this.#vt(),window.addEventListener("resize",this.#vt.bind(this))}}class L{#yt=document.querySelectorAll(".tabs");#xt(t,e,i,s){t.preventDefault();let a=e+s;-1===s&&a<0?i[i.length-1].focus():1===s&&a>=i.length?i[0].focus():i[a].focus()}#Et(t,e){t.forEach((t=>{t.setAttribute("aria-selected","false")})),e.forEach((t=>{t.classList.remove("shown"),t.setAttribute("hidden","")}))}activateTab(t,e,i){this.#Et(e,i),t.setAttribute("aria-selected","true");let s=t.getAttribute("aria-controls"),a=document.getElementById(s);a.classList.add("shown"),a.removeAttribute("hidden")}init(){this.#yt.forEach((t=>{const e=t.querySelectorAll('[role="tab"]'),i=t.querySelectorAll('[role="tabpanel"]');e.forEach(((t,s)=>{t.addEventListener("click",(t=>{let s=t.target;this.activateTab(s,e,i)})),t.addEventListener("keydown",(t=>{switch(t.code){case"Home":t.preventDefault(),e[0].focus();break;case"End":t.preventDefault(),e[e.length-1].focus();break;case"ArrowLeft":this.#xt(t,s,e,-1);break;case"ArrowRight":this.#xt(t,s,e,1)}}))}))}))}}document.addEventListener("DOMContentLoaded",(()=>{(new l).init(),(new r).init(),(new d).init(),(new c).init(),(new h).init(),(new u).init(),(new b).init(),(new g).init(),(new p).init(),(new m).init(),(new f).init(),(new L).init()}))}();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "natura11y",
3
- "version": "2.2.0",
3
+ "version": "2.2.2",
4
4
  "description": "Natura11y is an open-source front-end ecosystem for creating modern, accessible websites.",
5
5
  "private": false,
6
6
  "scripts": {
@@ -1,5 +1,7 @@
1
1
  import { handleOverlayOpen, handleOverlayClose } from './utilities/overlay';
2
2
 
3
+ import { focusTrap, getFocusableElements } from './utilities/focus';
4
+
3
5
  export default class Lightbox {
4
6
 
5
7
  // Private properties
@@ -7,26 +9,26 @@ export default class Lightbox {
7
9
  #lightboxTargetList = document.querySelectorAll('[data-lightbox]');
8
10
 
9
11
  #lightboxHTML = `
10
- <div class="lightbox__buttons button-group">
11
- <button class="button button--icon-only" data-lightbox-previous>
12
- <span class="icon icon-arrow-left" aria-label="Previous" aria-hidden="true"></span>
13
- </button>
14
- <button class="button button--icon-only" data-lightbox-next>
15
- <span class="icon icon-arrow-right" aria-label="Next" aria-hidden="true"></span>
16
- </button>
17
- <button class="button button--icon-only" data-lightbox-close>
18
- <span class="icon icon-close" aria-label="Close" aria-hidden="true"></span>
19
- </button>
20
- </div>
21
12
  <figure class="lightbox__container" aria-live="polite" aria-atomic="true">
22
- <div class="lightbox__media"></div>
23
- <figcaption class="lightbox__caption"></figcaption>
13
+ <div class="lightbox__media"></div>
14
+ <figcaption class="lightbox__caption"></figcaption>
24
15
  </figure>
16
+ <div class="lightbox__controls">
17
+ <button class="button button--icon-only" data-lightbox-previous>
18
+ <span class="icon icon-arrow-left" aria-label="Previous" aria-hidden="true"></span>
19
+ </button>
20
+ <button class="button button--icon-only" data-lightbox-next>
21
+ <span class="icon icon-arrow-right" aria-label="Next" aria-hidden="true"></span>
22
+ </button>
23
+ <button class="button button--icon-only" data-lightbox-close>
24
+ <span class="icon icon-close" aria-label="Close" aria-hidden="true"></span>
25
+ </button>
26
+ </div>
25
27
  `;
26
28
 
27
29
  #lightboxVideoHTML = `
28
- <video controls>
29
- <source type="video/mp4">
30
+ <video controls tabindex="0">
31
+ <source type="video/mp4">
30
32
  </video>
31
33
  `;
32
34
 
@@ -35,6 +37,8 @@ export default class Lightbox {
35
37
  frameborder="0"
36
38
  allow="autoplay; fullscreen;"
37
39
  allowfullscreen
40
+ controls
41
+ tabindex="0"
38
42
  ></iframe>
39
43
  `;
40
44
 
@@ -65,9 +69,8 @@ export default class Lightbox {
65
69
 
66
70
  this.currentLB = index;
67
71
 
68
- handleOverlayOpen(this.lightbox);
69
-
70
72
  this.#updateLightbox(index);
73
+ handleOverlayOpen(this.lightbox);
71
74
  };
72
75
 
73
76
  #handleLightboxClose = (e) => {
@@ -126,6 +129,18 @@ export default class Lightbox {
126
129
  }
127
130
  };
128
131
 
132
+ // Add this private method in your class
133
+ #handleVideoFocus = (lightboxElement) => {
134
+ const handleFocusEvent = (event) => {
135
+ event.preventDefault();
136
+ lightboxElement.children[0].focus();
137
+ getFocusableElements(lightboxElement.children[0]);
138
+ }
139
+
140
+ lightboxElement.setAttribute('tabindex', 0);
141
+ lightboxElement.addEventListener('focus', handleFocusEvent);
142
+ };
143
+
129
144
  #updateDirection(dir) {
130
145
  this.currentLB += dir;
131
146
 
@@ -144,6 +159,7 @@ export default class Lightbox {
144
159
 
145
160
  // Clear the previous lightbox content before inserting a new one
146
161
  lightboxElement.innerHTML = '';
162
+
147
163
 
148
164
  let lightboxElementTarget;
149
165
 
@@ -170,15 +186,21 @@ export default class Lightbox {
170
186
  lightboxElement,
171
187
  lbSrc
172
188
  );
189
+
173
190
  break;
174
191
 
175
192
  default:
176
193
  break;
177
194
  }
178
195
 
196
+ // Handle video focus
197
+ this.#handleVideoFocus(lightboxElement);
198
+
179
199
  if (shouldDisplayCaption) {
180
200
  lightboxCaption.innerHTML = lbCaption;
181
201
  }
202
+
203
+ focusTrap(this.lightbox);
182
204
  }
183
205
 
184
206
  #updateLightboxImage(lightboxElement, lbSrc, lbAlt) {
@@ -202,56 +224,57 @@ export default class Lightbox {
202
224
  }
203
225
 
204
226
  #updateLightboxVideo(lightboxElement, lbSrc) {
205
- // Check if the string contains 'youtube' (case-insensitive)
206
- const hasYouTube = /youtube/i.test(lbSrc);
227
+ // Check if the string contains 'youtube' (case-insensitive)
228
+ const hasYouTube = /youtube/i.test(lbSrc);
207
229
 
208
- // Check if the string contains 'vimeo' (case-insensitive)
209
- const hasVimeo = /vimeo/i.test(lbSrc);
230
+ // Check if the string contains 'vimeo' (case-insensitive)
231
+ const hasVimeo = /vimeo/i.test(lbSrc);
210
232
 
211
- let lightboxElementTarget;
233
+ let lightboxElementTarget;
212
234
 
213
- if (hasYouTube || hasVimeo) {
235
+ if (hasYouTube || hasVimeo) {
214
236
 
215
- // If the video is from YouTube or Vimeo, use an iframe
216
- lightboxElement.innerHTML = this.#lightboxVideoIframeHTML;
217
- lightboxElementTarget = lightboxElement.querySelector('iframe');
218
- lightboxElementTarget.src = lbSrc;
219
-
220
- } else {
237
+ // If the video is from YouTube or Vimeo, use an iframe
238
+ lightboxElement.innerHTML = this.#lightboxVideoIframeHTML;
239
+ lightboxElementTarget = lightboxElement.querySelector('iframe');
240
+ lightboxElementTarget.src = lbSrc;
241
+
242
+ } else {
221
243
 
222
- // If the video is not from YouTube or Vimeo, use a video element
223
- lightboxElement.innerHTML = this.#lightboxVideoHTML;
244
+ // If the video is not from YouTube or Vimeo, use a video element
245
+ lightboxElement.innerHTML = this.#lightboxVideoHTML;
224
246
 
225
- const loader = this.#createLoader();
226
- lightboxElement.appendChild(loader);
227
-
228
- lightboxElementTarget = lightboxElement.querySelector('source');
229
-
230
- const video = lightboxElement.querySelector('video');
247
+ const loader = this.#createLoader();
248
+ lightboxElement.appendChild(loader);
249
+
250
+ lightboxElementTarget = lightboxElement.querySelector('source');
251
+
252
+ const video = lightboxElement.querySelector('video');
231
253
 
232
- video.addEventListener('loadedmetadata', () => {
233
- // The intrinsic width and height of the video
234
- let intrinsicWidth = video.videoWidth;
235
- let intrinsicHeight = video.videoHeight;
254
+ video.addEventListener('loadedmetadata', () => {
255
+ // The intrinsic width and height of the video
256
+ let intrinsicWidth = video.videoWidth;
257
+ let intrinsicHeight = video.videoHeight;
236
258
 
237
- // The aspect ratio of the video
238
- lightboxElement.style.maxWidth = `${intrinsicWidth}px`;
239
- lightboxElement.style.aspectRatio = `${intrinsicWidth} / ${intrinsicHeight}`;
240
- });
259
+ // The aspect ratio of the video
260
+ lightboxElement.style.maxWidth = `${intrinsicWidth}px`;
261
+ lightboxElement.style.aspectRatio = `${intrinsicWidth} / ${intrinsicHeight}`;
262
+ });
241
263
 
242
- this.#handleMediaLoading(lightboxElementTarget, loader);
264
+ this.#handleMediaLoading(lightboxElementTarget, loader);
243
265
 
244
- lightboxElementTarget.src = lbSrc;
245
- }
266
+ lightboxElementTarget.src = lbSrc;
267
+ }
246
268
 
247
- return lightboxElementTarget;
248
- }
269
+ return lightboxElementTarget;
270
+ }
249
271
 
250
272
  #createLightbox() {
251
273
  const lightbox = document.createElement('div');
252
274
 
253
275
  lightbox.classList.add('lightbox');
254
276
  lightbox.setAttribute('aria-hidden', true);
277
+ lightbox.setAttribute('aria-live', 'polite');
255
278
  lightbox.innerHTML = this.#lightboxHTML;
256
279
 
257
280
  document.body.appendChild(lightbox);
@@ -279,23 +302,32 @@ export default class Lightbox {
279
302
  }
280
303
 
281
304
  #setLightboxProperties(lightboxButton) {
305
+
306
+ if (!lightboxButton) {
307
+ console.error("No lightbox button provided");
308
+ return;
309
+ }
310
+
282
311
  let defaultSrc = null;
283
312
  let defaultAlt = '';
284
313
 
285
314
  const hasImage = lightboxButton.querySelector('img') !== null;
286
315
 
287
316
  if (hasImage) {
288
- defaultSrc = lightboxButton.querySelector('img').src || null;
289
- defaultAlt = lightboxButton.querySelector('img').alt || '';
317
+ const img = lightboxButton.querySelector('img');
318
+ defaultSrc = img.src || null;
319
+ defaultAlt = img.alt || '';
290
320
  }
291
321
 
292
322
  const lbType = lightboxButton.getAttribute('data-lightbox') || 'image';
293
- const lbSrc =
294
- lightboxButton.getAttribute('data-lightbox-src') || defaultSrc;
295
- const lbCaption =
296
- lightboxButton.getAttribute('data-lightbox-caption') || null;
297
- const lbAlt =
298
- lightboxButton.getAttribute('data-lightbox-alt') || defaultAlt;
323
+ const lbSrc = lightboxButton.getAttribute('data-lightbox-src') || defaultSrc;
324
+ const lbCaption = lightboxButton.getAttribute('data-lightbox-caption') || null;
325
+ const lbAlt = lightboxButton.getAttribute('data-lightbox-alt') || defaultAlt;
326
+
327
+ if (lbSrc === null) {
328
+ console.error("No source provided for lightbox");
329
+ return;
330
+ }
299
331
 
300
332
  return {
301
333
  lbType: lbType,
@@ -380,8 +412,7 @@ export default class Lightbox {
380
412
  hiddenLargeImage.src = src;
381
413
  hiddenLargeImage.style.display = 'none';
382
414
 
383
- this.#lightboxes[Number(lazyImage.dataset.index)].hiddenImage =
384
- hiddenLargeImage;
415
+ this.#lightboxes[Number(lazyImage.dataset.index)].hiddenImage = hiddenLargeImage;
385
416
  }
386
417
  });
387
418
  }, options);
@@ -28,11 +28,12 @@ export const getFocusableElements = (element = document) => {
28
28
  'textarea',
29
29
  'select',
30
30
  '[tabindex]:not([tabindex="-1"])',
31
+ 'video',
32
+ 'audio'
31
33
  ];
32
34
 
33
35
  return [...element.querySelectorAll(els)].filter((el) => {
34
- return !el.hasAttribute('disabled') &&
35
- !el.getAttribute('aria-hidden');
36
+ return !el.hasAttribute('disabled');
36
37
  });
37
38
  }
38
39
 
@@ -42,6 +43,7 @@ export const getFocusableElements = (element = document) => {
42
43
 
43
44
  export const focusTrap = (element, firstFocusTarget = element) => {
44
45
  let focusableElements = getFocusableElements(element);
46
+
45
47
  let firstFocusableElement = focusableElements[0];
46
48
  let lastFocusableElement = focusableElements[focusableElements.length - 1];
47
49
 
@@ -49,5 +49,7 @@ export const handleOverlayClose = (element) => {
49
49
 
50
50
  window.scrollTo({ top: scrollPosition, behavior: 'instant' });
51
51
 
52
- lastFocusedElement.focus();
52
+ if(lastFocusedElement) {
53
+ lastFocusedElement.focus();
54
+ }
53
55
  }
@@ -20,9 +20,11 @@ In this file:
20
20
 
21
21
  :root {
22
22
  --accordion-button-padding-x: var(--spacer-3);
23
+
23
24
  --accordion-button-padding-y: var(--button-padding-y);
24
25
 
25
26
  --accordion-panel-padding-x: var(--spacer-3);
27
+
26
28
  --accordion-panel-padding-y: var(--spacer-3);
27
29
 
28
30
  --accordion-active-color: currentColor;
@@ -20,6 +20,7 @@ In this file:
20
20
 
21
21
  :root {
22
22
  --alert-padding-x: var(--spacer-2);
23
+
23
24
  --alert-padding-y: var(--spacer-2);
24
25
  }
25
26
 
@@ -20,6 +20,7 @@ In this file:
20
20
 
21
21
  :root {
22
22
  --button-padding-x: 1em;
23
+
23
24
  --button-padding-y: 0.75em;
24
25
 
25
26
  --button-font-weight: var(--body-font-weight-bold);
@@ -34,9 +35,12 @@ In this file:
34
35
 
35
36
  --button-icon-only-size: 2.5em;
36
37
 
37
- --button-icon-over-text-width: 5rem;
38
- --button-icon-over-text-border-radius: var(--button-border-radius);
39
-
38
+ --button-icon-over-text-width: 4em;
39
+
40
+ --button-icon-over-text-border-radius: var(--button-border-radius);
41
+
42
+ --button-icon-over-text-font-size: 0.675em;
43
+
40
44
  --button-group-gap: 0.5em;
41
45
  }
42
46
 
@@ -70,8 +74,7 @@ In this file:
70
74
  @layer component {
71
75
 
72
76
  .button {
73
- --button-opacity-hover: 0.75;
74
-
77
+ --button-opacity-hover: 0.75;
75
78
  --subtle-fill-opacity: 0.05;
76
79
 
77
80
  position: relative;
@@ -245,10 +248,10 @@ In this file:
245
248
 
246
249
  display: flex;
247
250
  flex-direction: column;
248
-
251
+
249
252
  text-align: center;
250
253
 
251
- gap: 0.25em;
254
+ gap: 0.375em;
252
255
 
253
256
  width: var(--button-icon-over-text-width);
254
257
 
@@ -272,13 +275,12 @@ In this file:
272
275
  [class*="__text"] {
273
276
  min-width: 100%;
274
277
 
275
- font-size: var(--font-size-sm);
278
+ font-size: var(--button-icon-over-text-font-size);
276
279
 
277
280
  font-weight: var(--body-font-weight-bold);
278
281
  }
279
282
 
280
283
  &:focus-visible {
281
-
282
284
  outline: unset;
283
285
 
284
286
  > [class*="icon"] {
@@ -286,7 +288,6 @@ In this file:
286
288
  outline-offset: var(--focus-outline-offset);
287
289
  }
288
290
  }
289
-
290
291
  }
291
292
 
292
293
  // Button Group
@@ -303,9 +304,8 @@ In this file:
303
304
  > [class*="button"]:not(.button--icon-only) {
304
305
  --button-padding-y: 0;
305
306
 
306
- align-self: stretch;
307
-
308
307
  display: flex;
308
+ align-self: stretch;
309
309
  align-items: center;
310
310
  flex-shrink: 0;
311
311
 
@@ -13,6 +13,7 @@ In this file:
13
13
 
14
14
  :root {
15
15
  --card-padding-x: var(--spacer-3);
16
+
16
17
  --card-padding-y: var(--spacer-3);
17
18
  }
18
19
 
@@ -22,6 +22,7 @@ In this file:
22
22
 
23
23
  :root {
24
24
  --container-padding-x-sm: var(--spacer-2);
25
+
25
26
  --container-padding-x-lg: var(--spacer-3);
26
27
 
27
28
  --narrow-width: 40rem; // Approximately 55 characters wide for readability
@@ -24,18 +24,25 @@ In this file:
24
24
 
25
25
  :root {
26
26
  --form-field-padding-x: var(--spacer-3);
27
+
27
28
  --form-field-padding-y: var(--button-padding-y);
28
29
 
29
30
  --form-field-border-radius: 0.25em;
31
+
30
32
  --form-field-border-color: currentColor;
31
33
 
32
34
  // Icons
33
35
 
34
36
  --icon-required: "\f14a";
37
+
35
38
  --icon-checkbox-unchecked: "\f10d";
39
+
36
40
  --icon-checkbox-checked: "\f10c";
41
+
37
42
  --icon-radio-unchecked: "\f138";
43
+
38
44
  --icon-radio-checked: "\f137";
45
+
39
46
  --icon-disabled: "\f115";
40
47
  }
41
48
 
@@ -31,6 +31,7 @@ $rows: math.div($columns, 2);
31
31
  --grid-divider-border-width: var(--border-width);
32
32
 
33
33
  --grid-divider-border-x-color: var(--canvas-border);
34
+
34
35
  --grid-divider-border-y-color: var(--canvas-border);
35
36
 
36
37
  --grid-sidebar-width: var(--article-sidebar-width);
@@ -14,6 +14,7 @@ In this file:
14
14
 
15
15
  :root {
16
16
  --icon-size: 1.25em;
17
+
17
18
  --icon-font-family: 'natura11y-icons';
18
19
  }
19
20