maverick-wave 3.3.2 → 3.5.0

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.
@@ -1 +1 @@
1
- !function(){"use strict";function e(){document.querySelectorAll(".color-swatch").forEach(e=>{const t=function(e){if(!e||"transparent"===e)return"#000000";const t=e=>Math.max(0,Math.min(255,Math.round(e))).toString(16).padStart(2,"0"),c=e.match(/color\(srgb\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)(?:\s*\/\s*([\d.]+))?\)/);if(c){const e="#"+t(255*c[1])+t(255*c[2])+t(255*c[3]),o=void 0===c[4]?1:parseFloat(c[4]);return(o<1?`${e} / ${o}`:e).toUpperCase()}const o=e.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);if(!o)return e;const n="#"+t(o[1])+t(o[2])+t(o[3]),s=void 0===o[4]?1:parseFloat(o[4]);return(s<1?`${n} / ${s}`:n).toUpperCase()}(window.getComputedStyle(e).backgroundColor),c=e.parentElement.querySelector(".mw-text-muted");c&&(c.textContent=t)})}document.addEventListener("DOMContentLoaded",function(){!function(){const e=document.querySelector(".mw-gallery-track");if(e){const t=document.querySelector(".mw-gallery-dots"),c=e.children,o=document.querySelector(".mw-gallery-desc"),n=document.querySelector(".mw-gallery-navi-prev"),s=document.querySelector(".mw-gallery-navi-next"),l=document.querySelector(".mw-gallery");let r=0;function i(){const t=l.offsetWidth;e.style.transform=`translateX(-${r*t}px)`}function a(e){const t=c.length;r=(e+t)%t,i(),document.querySelectorAll(".mw-gallery-dot").forEach((e,t)=>{e.classList.toggle("mw-active",t===r)}),o.textContent=c[r].dataset.desc}function d(){for(let e=0;e<c.length;e++){const c=document.createElement("span");c.className="mw-gallery-dot"+(0===e?" mw-active":""),c.addEventListener("click",()=>a(e)),t.appendChild(c)}}n.addEventListener("click",()=>a(r-1)),s.addEventListener("click",()=>a(r+1)),window.addEventListener("resize",i);let u=0;e.addEventListener("touchstart",e=>u=e.touches[0].clientX),e.addEventListener("touchend",e=>{const t=e.changedTouches[0].clientX-u;t>50&&a(r-1),t<-50&&a(r+1)}),d(),i()}}(),function(){const t=document.querySelector(".mw-theme-toggle");if(!t)return;const c=document.body,o=t.querySelector(".mw-theme-toggle-slider i"),n=getComputedStyle(document.documentElement).getPropertyValue("--mw-internal-theme-mode").trim()||"switchable";if("switchable"!==n)return console.log(`Theme mode fixed to: ${n}. Disabling toggle.`),o&&(o.className="light"===n?"fas fa-sun":"fas fa-moon"),"light"===n?t.classList.add("active"):t.classList.remove("active"),t.style.opacity="0.4",t.style.pointerEvents="none",t.style.cursor="default",t.setAttribute("aria-disabled","true"),localStorage.removeItem("mw-theme"),void("dark"===n&&c.classList.remove("mw-theme-light"));let s="light"===localStorage.getItem("mw-theme");const l=e=>{c.classList.toggle("mw-theme-light",e),t.classList.toggle("active",e),o&&(o.className=e?"fas fa-sun":"fas fa-moon")};l(s),t.addEventListener("click",()=>{s=!s,l(s),localStorage.setItem("mw-theme",s?"light":"dark"),setTimeout(e,400)})}(),e(),document.querySelectorAll(".mw-accordion-header").forEach(e=>{e.addEventListener("click",function(){this.classList.toggle("active");const e=this.nextElementSibling;e&&e.classList.toggle("active")})}),function(){const e=document.querySelector(".mw-menu-btn"),t=document.querySelector(".mw-navbar");if(!e||!t)return;function c(){e.classList.remove("open"),t.classList.remove("open")}function o(c){c.preventDefault(),c.stopPropagation(),e.classList.toggle("open"),t.classList.toggle("open")}e.addEventListener("click",o),e.addEventListener("touchstart",o,{passive:!1}),document.addEventListener("click",function(o){t.classList.contains("open")&&(t.contains(o.target)||e.contains(o.target)||c())}),document.addEventListener("keydown",function(e){"Escape"===e.key&&c()})}(),function(){const e=document.querySelectorAll(".mw-progress-fill");if(!e.length)return;const t=new IntersectionObserver((e,t)=>{e.forEach(e=>{if(!e.isIntersecting)return;const c=e.target,o=c.dataset.value||0;c.style.width=o+"%",t.unobserve(c)})},{root:null,threshold:.2});e.forEach(e=>t.observe(e))}(),document.querySelectorAll('a[href^="#"]').forEach(e=>{e.addEventListener("click",function(e){const t=this.getAttribute("href");if("#"===t)return;if(!document.querySelector(t))return;const c=document.querySelector(".mw-menu-btn"),o=document.querySelector(".mw-navbar");c&&o&&(c.classList.remove("open"),o.classList.remove("open"))})}),function(){const e=document.querySelectorAll("section[id]"),t=document.querySelectorAll(".mw-navbar-link");if(0===e.length||0===t.length)return;window.addEventListener("scroll",function(e,t){let c;return function(){const o=this,n=arguments;clearTimeout(c),c=setTimeout(()=>e.apply(o,n),t)}}(function(){let c="";e.forEach(e=>{const t=e.offsetTop-100,o=e.clientHeight;scrollY>=t&&scrollY<t+o&&(c=e.getAttribute("id"))}),t.forEach(e=>{e.classList.remove("active");const t=e.getAttribute("href");if(t){const o=t.indexOf("#");if(-1!==o){t.substring(o+1)===c&&e.classList.add("active")}}})},100))}(),document.querySelectorAll(".mw-tabs-nav-item").forEach(e=>{e.addEventListener("click",function(){const e=this.closest(".mw-tabs");if(!e)return;e.querySelectorAll(".mw-tabs-nav-item").forEach(e=>{e.classList.remove("active")}),this.classList.add("active");const t=this.getAttribute("data-tab");if(!t)return;e.querySelectorAll(".mw-tabs-panel").forEach(e=>{e.classList.remove("active")});const c=document.getElementById(t);c&&c.classList.add("active")})}),document.querySelectorAll(".mw-alert-close").forEach(e=>{e.addEventListener("click",function(){const e=this.closest(".mw-alert");e&&(e.classList.add("mw-alert-closing"),setTimeout(()=>{e.classList.add("mw-alert-closed")},300))})}),function(){const e=document.querySelector(".mw-localhost-indicator-activated"),t=document.querySelector(".mw-header");if(e){if("localhost"===window.location.hostname||"127.0.0.1"===window.location.hostname||window.location.hostname.includes("192.168.")){const e=document.createElement("div");e.className="mw-localhost-indicator-pulse",t.prepend(e)}}}(),document.querySelectorAll(".mw-slider-container").forEach(e=>{const t=e.querySelector(".mw-slider"),c=e.querySelector(".mw-slider-value");if(!t||!c)return;const o=()=>{const e=Number(t.value),o=Number(t.max)||100,n=Math.round(e/o*100);t.style.setProperty("--value",n+"%"),c.classList.contains("mw-slider-numeric")?c.setAttribute("data-value",e):c.setAttribute("data-value",n)};t.addEventListener("input",o),o()}),document.querySelectorAll(".mw-modal-close").forEach(e=>{e.addEventListener("click",function(){const e=this.closest(".mw-modal-overlay");e&&e.classList.remove("mw-modal-open")})}),function(){const e=document.getElementById("login-button");e&&e.addEventListener("click",function(){const e=this.querySelector("i");e.classList.contains("fa-lock")?(e.classList.remove("fa-lock"),e.classList.add("fa-lock-open")):(e.classList.remove("fa-lock-open"),e.classList.add("fa-lock"))})}(),document.querySelectorAll(".mw-image-slider").forEach(e=>{const t=e.querySelectorAll(".mw-image-slider-overlay-image"),c=e.querySelectorAll("button[data-index]");if(!t.length||!c.length)return;let o=0;function n(){t.forEach(e=>{const t=parseInt(e.dataset.index);e.classList.toggle("active",t===o)}),c.forEach(e=>{const t=parseInt(e.dataset.index);e.classList.toggle("active",t===o)})}function s(e){o=e,n()}c.forEach(e=>{e.addEventListener("click",()=>{s(parseInt(e.dataset.index))})}),n()}),document.querySelectorAll(".mw-item-list-checkbox, .mw-item-list-checkbox-scroll").forEach(e=>{e.querySelectorAll("li").forEach(e=>{const t=e.querySelector('input[type="checkbox"]'),c=e.querySelector(".mw-checkbox");t&&c&&(t.checked&&e.classList.add("mw-selected"),e.addEventListener("click",function(e){e.target!==t&&e.target.closest(".mw-checkbox")!==c&&toggleCheckbox(this)}),t.addEventListener("change",function(){const e=this.closest("li");this.checked?e.classList.add("mw-selected"):e.classList.remove("mw-selected"),e.dispatchEvent(new CustomEvent("checkboxToggle",{detail:{checked:this.checked,item:e}}))}),c.addEventListener("click",function(e){setTimeout(()=>{const e=this.closest("li");this.querySelector('input[type="checkbox"]').checked?e.classList.add("mw-selected"):e.classList.remove("mw-selected")},0)}))})}),document.addEventListener("checkboxToggle",function(e){console.log("Checkbox toggled:",e.detail.checked,e.detail.item)})}),window.toggleCheckbox=function(e){const t=e.querySelector('input[type="checkbox"]');if(!t)return;const c=t.checked;t.checked=!c,t.checked?e.classList.add("mw-selected"):e.classList.remove("mw-selected"),e.dispatchEvent(new CustomEvent("checkboxToggle",{detail:{checked:t.checked,item:e}}))}}();
1
+ !function(){"use strict";function e(){document.querySelectorAll(".color-swatch").forEach(e=>{const t=function(e){if(!e||"transparent"===e)return"#000000";const t=e=>Math.max(0,Math.min(255,Math.round(e))).toString(16).padStart(2,"0"),c=e.match(/color\(srgb\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)(?:\s*\/\s*([\d.]+))?\)/);if(c){const e="#"+t(255*c[1])+t(255*c[2])+t(255*c[3]),o=void 0===c[4]?1:parseFloat(c[4]);return(o<1?`${e} / ${o}`:e).toUpperCase()}const o=e.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);if(!o)return e;const n="#"+t(o[1])+t(o[2])+t(o[3]),s=void 0===o[4]?1:parseFloat(o[4]);return(s<1?`${n} / ${s}`:n).toUpperCase()}(window.getComputedStyle(e).backgroundColor),c=e.parentElement.querySelector(".mw-text-muted");c&&(c.textContent=t)})}document.addEventListener("DOMContentLoaded",function(){document.querySelectorAll(".mw-gallery").forEach(e=>{const t=e.closest(".mw-gallery-container")||e.parentElement;t&&function(e,t){const c=t.querySelector(".mw-gallery-track");if(!c||!c.children.length)return;const o=c.children,n=e.querySelector(".mw-gallery-dots"),s=e.querySelector(".mw-gallery-desc"),l=t.querySelector(".mw-gallery-navi-prev"),r=t.querySelector(".mw-gallery-navi-next");let a=0;function i(e){const t=o.length;a=(e+t)%t,c.style.transform=`translateX(-${100*a}%)`,n&&n.querySelectorAll(".mw-gallery-dot").forEach((e,t)=>{e.classList.toggle("mw-active",t===a)}),s&&(s.textContent=o[a].dataset.desc||"")}if(n)for(let e=0;e<o.length;e++){const t=document.createElement("span");t.className="mw-gallery-dot"+(0===e?" mw-active":""),t.addEventListener("click",()=>i(e)),n.appendChild(t)}l&&l.addEventListener("click",()=>i(a-1)),r&&r.addEventListener("click",()=>i(a+1));let d=0;c.addEventListener("touchstart",e=>d=e.touches[0].clientX),c.addEventListener("touchend",e=>{const t=e.changedTouches[0].clientX-d;t>50&&i(a-1),t<-50&&i(a+1)}),i(0)}(t,e)}),function(){const t=document.querySelector(".mw-theme-toggle");if(!t)return;const c=document.body,o=t.querySelector(".mw-theme-toggle-slider i"),n=getComputedStyle(document.documentElement).getPropertyValue("--mw-internal-theme-mode").trim()||"switchable";if("switchable"!==n)return console.log(`Theme mode fixed to: ${n}. Disabling toggle.`),o&&(o.className="light"===n?"fas fa-sun":"fas fa-moon"),"light"===n?t.classList.add("active"):t.classList.remove("active"),t.style.opacity="0.4",t.style.pointerEvents="none",t.style.cursor="default",t.setAttribute("aria-disabled","true"),localStorage.removeItem("mw-theme"),void("dark"===n&&c.classList.remove("mw-theme-light"));let s="light"===localStorage.getItem("mw-theme");const l=e=>{c.classList.toggle("mw-theme-light",e),t.classList.toggle("active",e),o&&(o.className=e?"fas fa-sun":"fas fa-moon")};l(s),t.addEventListener("click",()=>{s=!s,l(s),localStorage.setItem("mw-theme",s?"light":"dark"),setTimeout(e,400)})}(),e(),document.querySelectorAll(".mw-accordion-header").forEach(e=>{e.addEventListener("click",function(){this.classList.toggle("active");const e=this.nextElementSibling;e&&e.classList.toggle("active")})}),function(){const e=document.querySelector(".mw-menu-btn"),t=document.querySelector(".mw-navbar");if(!e||!t)return;function c(){e.classList.remove("open"),t.classList.remove("open")}function o(c){c.preventDefault(),c.stopPropagation(),e.classList.toggle("open"),t.classList.toggle("open")}e.addEventListener("click",o),e.addEventListener("touchstart",o,{passive:!1}),document.addEventListener("click",function(o){t.classList.contains("open")&&(t.contains(o.target)||e.contains(o.target)||c())}),document.addEventListener("keydown",function(e){"Escape"===e.key&&c()})}(),function(){const e=document.querySelectorAll(".mw-progress-fill");if(!e.length)return;const t=new IntersectionObserver((e,t)=>{e.forEach(e=>{if(!e.isIntersecting)return;const c=e.target,o=c.dataset.value||0;c.style.width=o+"%",t.unobserve(c)})},{root:null,threshold:.2});e.forEach(e=>t.observe(e))}(),document.querySelectorAll('a[href^="#"]').forEach(e=>{e.addEventListener("click",function(e){const t=this.getAttribute("href");if("#"===t)return;if(!document.querySelector(t))return;const c=document.querySelector(".mw-menu-btn"),o=document.querySelector(".mw-navbar");c&&o&&(c.classList.remove("open"),o.classList.remove("open"))})}),function(){const e=document.querySelectorAll("section[id]"),t=document.querySelectorAll(".mw-navbar-link");if(0===e.length||0===t.length)return;window.addEventListener("scroll",function(e,t){let c;return function(){const o=this,n=arguments;clearTimeout(c),c=setTimeout(()=>e.apply(o,n),t)}}(function(){let c="";e.forEach(e=>{const t=e.offsetTop-100,o=e.clientHeight;scrollY>=t&&scrollY<t+o&&(c=e.getAttribute("id"))}),t.forEach(e=>{e.classList.remove("active");const t=e.getAttribute("href");if(t){const o=t.indexOf("#");if(-1!==o){t.substring(o+1)===c&&e.classList.add("active")}}})},100))}(),document.querySelectorAll(".mw-tabs-nav-item").forEach(e=>{e.addEventListener("click",function(){const e=this.closest(".mw-tabs");if(!e)return;e.querySelectorAll(".mw-tabs-nav-item").forEach(e=>{e.classList.remove("active")}),this.classList.add("active");const t=this.getAttribute("data-tab");if(!t)return;e.querySelectorAll(".mw-tabs-panel").forEach(e=>{e.classList.remove("active")});const c=document.getElementById(t);c&&c.classList.add("active")})}),document.querySelectorAll(".mw-alert-close").forEach(e=>{e.addEventListener("click",function(){const e=this.closest(".mw-alert");e&&(e.classList.add("mw-alert-closing"),setTimeout(()=>{e.classList.add("mw-alert-closed")},300))})}),function(){const e=document.querySelector(".mw-localhost-indicator-activated"),t=document.querySelector(".mw-header");if(e){if("localhost"===window.location.hostname||"127.0.0.1"===window.location.hostname||window.location.hostname.includes("192.168.")){const e=document.createElement("div");e.className="mw-localhost-indicator-pulse",t.prepend(e)}}}(),document.querySelectorAll(".mw-slider-container").forEach(e=>{const t=e.querySelector(".mw-slider"),c=e.querySelector(".mw-slider-value");if(!t||!c)return;const o=()=>{const e=Number(t.value),o=Number(t.max)||100,n=Math.round(e/o*100);t.style.setProperty("--value",n+"%"),c.classList.contains("mw-slider-numeric")?c.setAttribute("data-value",e):c.setAttribute("data-value",n)};t.addEventListener("input",o),o()}),document.querySelectorAll(".mw-modal-close").forEach(e=>{e.addEventListener("click",function(){const e=this.closest(".mw-modal-overlay");e&&e.classList.remove("mw-modal-open")})}),function(){const e=document.getElementById("login-button");e&&e.addEventListener("click",function(){const e=this.querySelector("i");e.classList.contains("fa-lock")?(e.classList.remove("fa-lock"),e.classList.add("fa-lock-open")):(e.classList.remove("fa-lock-open"),e.classList.add("fa-lock"))})}(),document.querySelectorAll(".mw-image-slider").forEach(e=>{const t=e.querySelectorAll(".mw-image-slider-overlay-image"),c=e.querySelectorAll("button[data-index]");if(!t.length||!c.length)return;let o=0;function n(){t.forEach(e=>{const t=parseInt(e.dataset.index);e.classList.toggle("active",t===o)}),c.forEach(e=>{const t=parseInt(e.dataset.index);e.classList.toggle("active",t===o)})}function s(e){o=e,n()}c.forEach(e=>{e.addEventListener("click",()=>{s(parseInt(e.dataset.index))})}),n()}),document.querySelectorAll(".mw-item-list-checkbox, .mw-item-list-checkbox-scroll").forEach(e=>{e.querySelectorAll("li").forEach(e=>{const t=e.querySelector('input[type="checkbox"]'),c=e.querySelector(".mw-checkbox");t&&c&&(t.checked&&e.classList.add("mw-selected"),e.addEventListener("click",function(e){e.target!==t&&e.target.closest(".mw-checkbox")!==c&&toggleCheckbox(this)}),t.addEventListener("change",function(){const e=this.closest("li");this.checked?e.classList.add("mw-selected"):e.classList.remove("mw-selected"),e.dispatchEvent(new CustomEvent("checkboxToggle",{detail:{checked:this.checked,item:e}}))}),c.addEventListener("click",function(e){setTimeout(()=>{const e=this.closest("li");this.querySelector('input[type="checkbox"]').checked?e.classList.add("mw-selected"):e.classList.remove("mw-selected")},0)}))})}),document.addEventListener("checkboxToggle",function(e){console.log("Checkbox toggled:",e.detail.checked,e.detail.item)})}),window.toggleCheckbox=function(e){const t=e.querySelector('input[type="checkbox"]');if(!t)return;const c=t.checked;t.checked=!c,t.checked?e.classList.add("mw-selected"):e.classList.remove("mw-selected"),e.dispatchEvent(new CustomEvent("checkboxToggle",{detail:{checked:t.checked,item:e}}))}}();
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "maverick-wave",
3
- "version": "3.3.2",
3
+ "version": "3.5.0",
4
4
  "config": {
5
- "version_short": "3.3"
5
+ "version_short": "3.5"
6
6
  },
7
7
  "description": "A lightweight, modern CSS framework for building responsive websites with elegance and speed.",
8
8
  "main": "src/js/main.js",
package/src/js/main.js CHANGED
@@ -119,64 +119,77 @@
119
119
 
120
120
  // ===== Gallery Component =====
121
121
  function initGalleries() {
122
- const track = document.querySelector('.mw-gallery-track');
122
+ document.querySelectorAll('.mw-gallery').forEach((gallery) => {
123
+ // Dots and caption sit next to .mw-gallery, not inside it, so the
124
+ // container is the scope — with the parent as a fallback, otherwise a
125
+ // markup without the wrapper would silently stay dead
126
+ const scope =
127
+ gallery.closest('.mw-gallery-container') || gallery.parentElement;
128
+ if (scope) initGallery(scope, gallery);
129
+ });
130
+ }
123
131
 
124
- if (track) {
125
- const dots = document.querySelector('.mw-gallery-dots');
126
- const slides = track.children;
127
- const descBox = document.querySelector('.mw-gallery-desc');
128
- const prevBtn = document.querySelector('.mw-gallery-navi-prev');
129
- const nextBtn = document.querySelector('.mw-gallery-navi-next');
130
- const sliderContainer = document.querySelector('.mw-gallery');
132
+ function initGallery(container, gallery) {
133
+ const track = gallery.querySelector('.mw-gallery-track');
134
+ if (!track || !track.children.length) return;
131
135
 
132
- let current = 0;
136
+ const slides = track.children;
133
137
 
134
- function updateSliderPosition() {
135
- const slideWidth = sliderContainer.offsetWidth;
136
- track.style.transform = `translateX(-${current * slideWidth}px)`;
137
- }
138
+ // Everything below the track is optional — a gallery may well ship
139
+ // without arrows, dots or a caption, and a missing one must not throw
140
+ const dotsBox = container.querySelector('.mw-gallery-dots');
141
+ const descBox = container.querySelector('.mw-gallery-desc');
142
+ const prevBtn = gallery.querySelector('.mw-gallery-navi-prev');
143
+ const nextBtn = gallery.querySelector('.mw-gallery-navi-next');
138
144
 
139
- function goToSlide(index) {
140
- const total = slides.length;
141
- current = (index + total) % total;
142
- updateSliderPosition();
145
+ let current = 0;
143
146
 
144
- document.querySelectorAll('.mw-gallery-dot').forEach((dot, i) => {
147
+ function goToSlide(index) {
148
+ const total = slides.length;
149
+ current = (index + total) % total;
150
+
151
+ // Percentages refer to the track's own width, which is one slide —
152
+ // so no measuring and nothing to recalculate on resize
153
+ track.style.transform = `translateX(-${current * 100}%)`;
154
+
155
+ if (dotsBox) {
156
+ dotsBox.querySelectorAll('.mw-gallery-dot').forEach((dot, i) => {
145
157
  dot.classList.toggle('mw-active', i === current);
146
158
  });
159
+ }
147
160
 
148
- descBox.textContent = slides[current].dataset.desc;
161
+ if (descBox) {
162
+ descBox.textContent = slides[current].dataset.desc || '';
149
163
  }
164
+ }
150
165
 
151
- function initDots() {
152
- for (let i = 0; i < slides.length; i++) {
153
- const dot = document.createElement('span');
154
- dot.className = 'mw-gallery-dot' + (i === 0 ? ' mw-active' : '');
155
- dot.addEventListener('click', () => goToSlide(i));
156
- dots.appendChild(dot);
157
- }
166
+ if (dotsBox) {
167
+ for (let i = 0; i < slides.length; i++) {
168
+ const dot = document.createElement('span');
169
+ dot.className = 'mw-gallery-dot' + (i === 0 ? ' mw-active' : '');
170
+ dot.addEventListener('click', () => goToSlide(i));
171
+ dotsBox.appendChild(dot);
158
172
  }
173
+ }
159
174
 
175
+ if (prevBtn)
160
176
  prevBtn.addEventListener('click', () => goToSlide(current - 1));
177
+ if (nextBtn)
161
178
  nextBtn.addEventListener('click', () => goToSlide(current + 1));
162
179
 
163
- window.addEventListener('resize', updateSliderPosition);
164
-
165
- // Optional: Swipe support
166
- let startX = 0;
167
- track.addEventListener(
168
- 'touchstart',
169
- (e) => (startX = e.touches[0].clientX)
170
- );
171
- track.addEventListener('touchend', (e) => {
172
- const delta = e.changedTouches[0].clientX - startX;
173
- if (delta > 50) goToSlide(current - 1);
174
- if (delta < -50) goToSlide(current + 1);
175
- });
180
+ // Optional: Swipe support
181
+ let startX = 0;
182
+ track.addEventListener(
183
+ 'touchstart',
184
+ (e) => (startX = e.touches[0].clientX)
185
+ );
186
+ track.addEventListener('touchend', (e) => {
187
+ const delta = e.changedTouches[0].clientX - startX;
188
+ if (delta > 50) goToSlide(current - 1);
189
+ if (delta < -50) goToSlide(current + 1);
190
+ });
176
191
 
177
- initDots();
178
- updateSliderPosition();
179
- }
192
+ goToSlide(0);
180
193
  }
181
194
 
182
195
  // ===== Theme Toggle =====
@@ -11,6 +11,24 @@
11
11
  <div class="mw-card mw-card-simple mw-p-2">Small padding</div>
12
12
  <div class="mw-card mw-card-simple mw-p-4 mw-mt-4">Medium padding</div>
13
13
  <div class="mw-card mw-card-simple mw-p-6 mw-mt-4">Large padding</div>
14
+
15
+ <h4 class="mw-mt-6 mw-mb-2">Auto Margins</h4>
16
+ <p class="mw-text-muted mw-text-sm mw-mb-3">
17
+ Every margin utility also takes <code>auto</code> &mdash;
18
+ <code>mw-mx-auto</code> centers a block, <code>mw-ml-auto</code> pushes a
19
+ flex item to the far side.
20
+ </p>
21
+
22
+ <div class="mw-card mw-card-simple mw-mb-4">
23
+ <div class="mw-card mw-card-simple mw-mx-auto" style="width: 60%">
24
+ mw-mx-auto
25
+ </div>
26
+ </div>
27
+
28
+ <div class="mw-card mw-card-simple mw-d-flex mw-items-center mw-gap-3">
29
+ <span>Left</span>
30
+ <span class="mw-tag mw-tag-primary mw-ml-auto">mw-ml-auto</span>
31
+ </div>
14
32
  </div>
15
33
 
16
34
  <div>
@@ -34,7 +52,7 @@
34
52
  </div>
35
53
  </div>
36
54
 
37
- <div class="mw-card mw-card-simple">
55
+ <div class="mw-card mw-card-simple mw-mb-4">
38
56
  <h4 class="mw-mb-2">Alignment</h4>
39
57
  <div
40
58
  class="mw-d-flex mw-justify-center mw-items-center mw-gap-3"
@@ -44,12 +62,26 @@
44
62
  <div class="mw-card mw-card-simple mw-p-2">Centered</div>
45
63
  </div>
46
64
  </div>
65
+
66
+ <div class="mw-card mw-card-simple">
67
+ <h4 class="mw-mb-2">Align Self</h4>
68
+ <p class="mw-text-muted mw-text-sm mw-mb-3">
69
+ <code>mw-items-*</code> sets the rule for the whole container,
70
+ <code>mw-self-*</code> lets a single child break out of it.
71
+ </p>
72
+ <div class="mw-d-flex mw-items-stretch mw-gap-3" style="height: 130px">
73
+ <div class="mw-card mw-card-simple mw-p-2">stretch</div>
74
+ <div class="mw-card mw-card-simple mw-p-2 mw-self-start">start</div>
75
+ <div class="mw-card mw-card-simple mw-p-2 mw-self-center">center</div>
76
+ <div class="mw-card mw-card-simple mw-p-2 mw-self-end">end</div>
77
+ </div>
78
+ </div>
47
79
  </div>
48
80
 
49
81
  <div>
50
82
  <h3 class="mw-mb-4">Gap Utilities</h3>
51
83
  <div class="mw-card mw-card-simple">
52
- <h4 class="mw-mb-3">Different Gap Sizes (from 0 - 10)</h4>
84
+ <h4 class="mw-mb-3">Different Gap Sizes (from 0 - 14)</h4>
53
85
 
54
86
  <div class="mw-mb-4">
55
87
  <p class="mw-mb-2">Small gap (gap-2)</p>
@@ -17,7 +17,7 @@ $secondary-color: #bb3110 !default;
17
17
  $success-color: #218838 !default;
18
18
  $warning-color: #d4a310 !default;
19
19
  // Darkened a little so light button text stays readable on the solid surface
20
- $danger-color: #c82333 !default;
20
+ $danger-color: #db182f !default;
21
21
  $info-color: #17a2b8 !default;
22
22
  $gray-color: #565656 !default;
23
23
  $dark-background: #0b111a !default;
@@ -67,7 +67,7 @@ $form-elements-background: #efefef !default;
67
67
  }
68
68
 
69
69
  // Uniform hover tone for every solid brand/status surface
70
- $hover-shift: 18%;
70
+ $hover-shift: 12%;
71
71
 
72
72
  // Base colors that don't change between themes.
73
73
  // Emitted as --mw-<key>.
@@ -153,6 +153,9 @@ $header-colors: (
153
153
  'burgerbutton-color': token('dark-text-color'),
154
154
  'border': token('border-accent'),
155
155
  );
156
+ // Drives the bar height, the anchor scroll offset and the mobile drawer padding.
157
+ // 59px is the intrinsic height: 42px logo + 2 × spacing('3') + 1px border.
158
+ $header-height: 62px;
156
159
 
157
160
  // Typography
158
161
  $font-family: 'Poppins', sans-serif;
@@ -45,6 +45,9 @@ $_active-prefix: 'dark';
45
45
  // hero background image
46
46
  --mw-hero-background: #{$mw-hero-image};
47
47
 
48
+ // header height
49
+ --mw-header-height: #{$header-height};
50
+
48
51
  // Active theme — aliases onto the dark or light set above
49
52
  @each $name, $value in $_active-theme {
50
53
  --mw-#{$name}: var(--mw-#{$_active-prefix}-#{$name});
@@ -11,7 +11,8 @@
11
11
  html {
12
12
  scroll-behavior: smooth;
13
13
  min-width: 360px;
14
- scroll-padding-top: 70px;
14
+ // Keeps anchor targets clear of the fixed header, plus a bit of breathing room
15
+ scroll-padding-top: calc(var(--mw-header-height) + #{spacing('4')});
15
16
  -webkit-text-size-adjust: 100%;
16
17
  text-size-adjust: 100%;
17
18
  }
@@ -14,17 +14,20 @@
14
14
  &-track {
15
15
  display: flex;
16
16
  transition: transform 0.3s ease;
17
- height: 100%;
17
+
18
+ @media (prefers-reduced-motion: reduce) {
19
+ transition: none;
20
+ }
18
21
  }
19
22
 
20
23
  &-slide {
21
24
  width: 100%;
22
25
  flex-shrink: 0;
23
- height: 100%;
26
+ aspect-ratio: 16 / 9;
24
27
 
25
28
  img {
26
29
  width: 100%;
27
- height: 580px;
30
+ height: 100%;
28
31
  object-fit: cover;
29
32
  display: block;
30
33
  }
@@ -34,32 +37,45 @@
34
37
  position: absolute;
35
38
  top: 50%;
36
39
  transform: translateY(-50%);
37
- color: rgba(23, 23, 23, 0.8); // fixed color
38
- background: rgba(240, 240, 240, 0.2); // fixed color
39
- border: none;
40
- font-size: font-size('5xl');
41
- cursor: pointer;
42
- z-index: z-index('menu');
43
- user-select: none;
44
- border-radius: radius('md');
40
+ width: 52px;
41
+ height: 108px;
45
42
  display: flex;
46
43
  align-items: center;
47
44
  justify-content: center;
48
- height: calc(580px * 0.9);
49
- width: 8%;
45
+ padding: spacing('0');
46
+ border: none;
47
+ border-radius: radius('md');
48
+ background: rgb(0 0 0 / 40%);
49
+ color: rgb(255 255 255 / 75%);
50
+ font-size: font-size('xl');
51
+ line-height: 1;
52
+ cursor: pointer;
53
+ z-index: z-index('lift');
54
+ user-select: none;
55
+ transition: var(--mw-transition);
56
+
57
+ &::before {
58
+ content: '';
59
+ position: absolute;
60
+ inset: -8px;
61
+ border-radius: inherit;
62
+ }
50
63
 
51
64
  &:hover {
52
- background: rgba(223, 223, 223, 0.4); // fixed color
65
+ background: rgb(0 0 0 / 85%);
66
+ }
67
+
68
+ &:focus-visible {
69
+ outline: 2px solid var(--mw-primary-color);
70
+ outline-offset: 2px;
53
71
  }
54
72
 
55
73
  &-prev {
56
- left: 3px;
57
- z-index: z-index('menu');
74
+ left: spacing('4');
58
75
  }
59
76
 
60
77
  &-next {
61
- right: 3px;
62
- z-index: z-index('menu');
78
+ right: spacing('4');
63
79
  }
64
80
  }
65
81
 
@@ -97,26 +113,8 @@
97
113
  }
98
114
  }
99
115
 
100
- @include media-down('lg') {
101
- .mw-gallery {
102
- &-slide img {
103
- height: 470px;
104
- }
105
- &-navi {
106
- font-size: font-size('3xl');
107
- height: calc(470px * 0.9);
108
- }
109
- }
110
- }
111
-
112
116
  @include media-down('md') {
113
117
  .mw-gallery {
114
- &-slide img {
115
- height: 380px;
116
- }
117
- &-navi {
118
- height: calc(380px * 0.9);
119
- }
120
118
  &-desc {
121
119
  font-size: font-size('sm');
122
120
  }
@@ -132,14 +130,12 @@
132
130
 
133
131
  @include media-down('sm') {
134
132
  .mw-gallery {
135
- &-slide img {
136
- height: 270px;
133
+ &-slide {
134
+ aspect-ratio: 4 / 3;
137
135
  }
138
136
  &-navi {
139
- font-size: font-size('2xl');
140
- height: calc(270px * 0.9);
141
- border-radius: radius('sm');
142
- width: 11%;
137
+ width: 46px;
138
+ height: 88px;
143
139
  }
144
140
  &-desc {
145
141
  font-size: font-size('xs');
@@ -12,6 +12,13 @@
12
12
  padding: spacing('3') spacing('0');
13
13
  transition: var(--mw-transition);
14
14
 
15
+ // Flex, so the container stays vertically centered once the height token
16
+ // exceeds the intrinsic content height. min-height, so a too-small token
17
+ // never clips a taller logo or a wrapping action row.
18
+ display: flex;
19
+ align-items: center;
20
+ min-height: var(--mw-header-height);
21
+
15
22
  .mw-container {
16
23
  display: flex;
17
24
  justify-content: space-between;
@@ -162,10 +169,6 @@
162
169
  }
163
170
  }
164
171
 
165
- // Header bar height: 42px logo + 2 × spacing('3') padding + 1px border.
166
- // Overridable via --mw-header-height for projects with a taller bar.
167
- $_header-height: 59px;
168
-
169
172
  // Off-canvas drawer for the collapsed navbar (mobile only)
170
173
  @mixin navbar-off-canvas {
171
174
  position: fixed;
@@ -195,8 +198,7 @@ $_header-height: 59px;
195
198
  // Keeps a rubber-band scroll inside the panel from moving the page
196
199
  overscroll-behavior: contain;
197
200
  padding: calc(
198
- var(--mw-header-height, #{$_header-height}) + #{spacing('6')} +
199
- env(safe-area-inset-top)
201
+ var(--mw-header-height) + #{spacing('6')} + env(safe-area-inset-top)
200
202
  )
201
203
  spacing('4') calc(#{spacing('6')} + env(safe-area-inset-bottom));
202
204
  }
@@ -76,3 +76,22 @@
76
76
  align-items: stretch;
77
77
  }
78
78
  }
79
+
80
+ // Align self — the per-item counterpart to .mw-items-*
81
+ .mw-self {
82
+ &-start {
83
+ align-self: flex-start;
84
+ }
85
+
86
+ &-end {
87
+ align-self: flex-end;
88
+ }
89
+
90
+ &-center {
91
+ align-self: center;
92
+ }
93
+
94
+ &-stretch {
95
+ align-self: stretch;
96
+ }
97
+ }
@@ -1,7 +1,15 @@
1
+ @use 'sass:map';
1
2
  @use '../abstracts' as *;
2
3
 
4
+ $_margin-values: map.merge(
5
+ $spacing,
6
+ (
7
+ 'auto': auto,
8
+ )
9
+ );
10
+
3
11
  // Margin utilities
4
- @each $key, $value in $spacing {
12
+ @each $key, $value in $_margin-values {
5
13
  .mw-m-#{$key} {
6
14
  margin: $value;
7
15
  }
@@ -35,46 +43,44 @@
35
43
 
36
44
  // Padding utilities
37
45
  @each $key, $value in $spacing {
38
- .mw-p-#{$key} {
39
- padding: $value;
40
- }
46
+ @if $value >= 0 {
47
+ .mw-p-#{$key} {
48
+ padding: $value;
49
+ }
41
50
 
42
- .mw-pt-#{$key} {
43
- padding-top: $value;
44
- }
51
+ .mw-pt-#{$key} {
52
+ padding-top: $value;
53
+ }
45
54
 
46
- .mw-pb-#{$key} {
47
- padding-bottom: $value;
48
- }
55
+ .mw-pb-#{$key} {
56
+ padding-bottom: $value;
57
+ }
49
58
 
50
- .mw-pl-#{$key} {
51
- padding-left: $value;
52
- }
59
+ .mw-pl-#{$key} {
60
+ padding-left: $value;
61
+ }
53
62
 
54
- .mw-pr-#{$key} {
55
- padding-right: $value;
56
- }
63
+ .mw-pr-#{$key} {
64
+ padding-right: $value;
65
+ }
57
66
 
58
- .mw-px-#{$key} {
59
- padding-left: $value;
60
- padding-right: $value;
61
- }
67
+ .mw-px-#{$key} {
68
+ padding-left: $value;
69
+ padding-right: $value;
70
+ }
62
71
 
63
- .mw-py-#{$key} {
64
- padding-top: $value;
65
- padding-bottom: $value;
72
+ .mw-py-#{$key} {
73
+ padding-top: $value;
74
+ padding-bottom: $value;
75
+ }
66
76
  }
67
77
  }
68
78
 
69
- // Gap utilities (positive values only — negative gap is invalid CSS)
79
+ // Gap utilities
70
80
  @each $key, $value in $spacing {
71
81
  @if $value >= 0 {
72
82
  .mw-gap-#{$key} {
73
83
  gap: $value;
74
-
75
- @include media-down('sm') {
76
- gap: calc($value * 0.75);
77
- }
78
84
  }
79
85
  }
80
86
  }