@madj2k/fe-frontend-kit 2.0.24 → 2.0.26

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/index.js CHANGED
@@ -4,6 +4,7 @@ export { Madj2kBetterResizeEvent } from './tools/better-resize-event';
4
4
  export { Madj2kOwlThumbnail } from './tools/owl-thumbnail';
5
5
  export { Madj2kResizeEnd } from './tools/resize-end';
6
6
  export { Madj2kScrolling } from './tools/scrolling';
7
+ export { Madj2kSimpleFadeSlider } from './tools/simple-fade-slider';
7
8
  export { Madj2kToggledOverlay } from './tools/toggled-overlay';
8
9
 
9
10
  // Menus
package/index.scss CHANGED
@@ -10,4 +10,5 @@
10
10
  @forward 'tools/owl-thumbnail/index';
11
11
  @forward 'tools/resize-end/index';
12
12
  @forward 'tools/scrolling/index';
13
+ @forward 'tools/simple-fade-slider/index';
13
14
  @forward 'tools/toggled-overlay/index';
@@ -49,6 +49,7 @@ class Madj2kFlyoutMenu {
49
49
  menuCloseClass: "js-flyout-close",
50
50
  menuContainerClass: "js-flyout-container",
51
51
  menuInnerClass: "js-flyout-inner",
52
+ heightCalculationClass: 'calculate',
52
53
  heightMode: 'full',
53
54
  paddingBehavior: 0,
54
55
  paddingViewPortMinWidth: 0,
@@ -230,7 +231,9 @@ class Madj2kFlyoutMenu {
230
231
  * Opens the flyout menu
231
232
  */
232
233
  open() {
233
- const {$menu, $element, animationOpenStatusClass, openStatusClass} = this.settings;
234
+ const {$menu, $element, animationOpenStatusClass, openStatusClass, openStatusBodyClass, animationBodyClassPrefix} = this.settings;
235
+ const $body = document.body;
236
+
234
237
  if (!$menu.classList.contains(openStatusClass) && !$menu.classList.contains(animationOpenStatusClass)) {
235
238
  document.dispatchEvent(new CustomEvent('madj2k-slidemenu-close'));
236
239
  document.dispatchEvent(new CustomEvent('madj2k-pulldownmenu-close'));
@@ -244,7 +247,8 @@ class Madj2kFlyoutMenu {
244
247
  $menu.classList.add(openStatusClass, animationOpenStatusClass);
245
248
  $element.classList.add(openStatusClass, animationOpenStatusClass);
246
249
  $element.setAttribute('aria-expanded', true);
247
- document.body.classList.add(`${this.settings.animationBodyClassPrefix}-${animationOpenStatusClass}`);
250
+ $body.classList.add(openStatusBodyClass);
251
+ $body.classList.add(`${animationBodyClassPrefix}-${animationOpenStatusClass}`);
248
252
 
249
253
  this.settings.$menuContainer.style.transition = `top ${this.settings.animationDuration}ms`;
250
254
  this.settings.$menuContainer.style.top = '0';
@@ -252,7 +256,7 @@ class Madj2kFlyoutMenu {
252
256
  setTimeout(() => {
253
257
  $menu.classList.remove(animationOpenStatusClass);
254
258
  $element.classList.remove(animationOpenStatusClass);
255
- document.body.classList.remove(`${this.settings.animationBodyClassPrefix}-${animationOpenStatusClass}`);
259
+ $body.classList.remove(`${animationBodyClassPrefix}-${animationOpenStatusClass}`);
256
260
  document.dispatchEvent(new CustomEvent('madj2k-flyoutmenu-opened'));
257
261
  }, this.settings.animationDuration);
258
262
  }
@@ -284,17 +288,20 @@ class Madj2kFlyoutMenu {
284
288
  * Closes the flyout menu
285
289
  */
286
290
  close() {
287
- const {$menu, $element, animationCloseStatusClass, openStatusClass} = this.settings;
291
+ const {$menu, $element, animationCloseStatusClass, openStatusClass, openStatusBodyClass, animationBodyClassPrefix} = this.settings;
292
+ const $body = document.body;
293
+
288
294
  if ($menu.classList.contains(openStatusClass) && !$menu.classList.contains(animationCloseStatusClass)) {
289
295
  document.dispatchEvent(new CustomEvent('madj2k-flyoutmenu-closing'));
290
296
 
297
+ this.toggleNoScroll();
298
+
291
299
  $menu.classList.add(animationCloseStatusClass);
292
300
  $element.classList.add(animationCloseStatusClass);
293
- document.body.classList.add(`${this.settings.animationBodyClassPrefix}-${animationCloseStatusClass}`);
294
301
  $element.classList.remove(openStatusClass);
295
302
  $element.setAttribute('aria-expanded', false);
296
-
297
- this.toggleNoScroll();
303
+ $body.classList.add(`${animationBodyClassPrefix}-${animationCloseStatusClass}`);
304
+ $body.classList.remove(openStatusBodyClass);
298
305
 
299
306
  this.settings.$menuContainer.style.transition = `top ${this.settings.animationDuration}ms`;
300
307
  this.settings.$menuContainer.style.top = '-100%';
@@ -302,7 +309,7 @@ class Madj2kFlyoutMenu {
302
309
  setTimeout(() => {
303
310
  $menu.classList.remove(openStatusClass, animationCloseStatusClass);
304
311
  $element.classList.remove(animationCloseStatusClass);
305
- document.body.classList.remove(`${this.settings.animationBodyClassPrefix}-${animationCloseStatusClass}`);
312
+ $body.classList.remove(`${animationBodyClassPrefix}-${animationCloseStatusClass}`);
306
313
  document.dispatchEvent(new CustomEvent('madj2k-flyoutmenu-closed'));
307
314
  }, this.settings.animationDuration);
308
315
  }
@@ -334,29 +341,41 @@ class Madj2kFlyoutMenu {
334
341
  console.warn('Option "fullHeight" is deprecated. Please use "heightMode" instead.');
335
342
  }
336
343
 
337
- let height = this.settings.$menuInner.offsetHeight || this.settings.$menu.offsetHeight;
344
+ // remove max-height for correct calculation
345
+ this.settings.$menu.classList.add(this.settings.heightCalculationClass);
346
+
347
+ const innerHeight = this.settings.$menuInner.offsetHeight || this.settings.$menu.offsetHeight;
338
348
  const refObj = this.settings.$positionReference || this.$element;
339
349
  const refPos = refObj.getBoundingClientRect();
340
- const flyoutTop = refPos.top + refObj.offsetHeight;
350
+ const refHeight = refObj.offsetHeight;
351
+ const flyoutTop = refPos.top + refHeight;
352
+ let newHeight = '';
341
353
 
342
354
  // heightMode "full" with deprecated fullHeight-setting as fallback
343
355
  if (this.settings.heightMode === 'full' || this.settings.fullHeight === true) {
356
+
357
+ // we use the viewport height because it may be the case that accordions are used here
344
358
  const viewPortHeight = window.innerHeight;
345
- if (height < viewPortHeight) {
346
- this.settings.$menu.style.height = `calc(100vh + ${flyoutTop}px)`;
359
+ if (innerHeight < (viewPortHeight - flyoutTop)) {
360
+ newHeight = `calc(100vh - ${flyoutTop}px)`;
347
361
  } else {
348
- this.settings.$menu.style.height = `${height}px`;
362
+ newHeight = `${innerHeight}px`;
349
363
  }
350
364
 
351
365
  } else if (this.settings.heightMode === 'maxContent') {
352
- this.settings.$menu.style.height = `max-content`;
366
+ newHeight = `max-content`;
353
367
  console.warn('heightMode: maxContent is not working on Apple Safari. Please use heightMode: full instead.');
354
368
 
355
369
  } else {
356
- this.settings.$menu.style.height = `${height}px`;
370
+ newHeight = `${innerHeight}px`;
357
371
  }
372
+
373
+ // set max-height again so that longer flyouts do not lead to scrolling on smaller ones
374
+ this.settings.$menu.classList.remove(this.settings.heightCalculationClass);
375
+ this.settings.$menu.style.height = newHeight;
358
376
  }
359
377
 
378
+
360
379
  /**
361
380
  * Positions the menu based on the reference element
362
381
  */
@@ -406,28 +425,33 @@ class Madj2kFlyoutMenu {
406
425
  * Toggles scroll behavior of the page
407
426
  */
408
427
  toggleNoScroll() {
409
- const body = document.body;
410
- const helper = body.querySelector('.no-scroll-helper');
411
- const inner = body.querySelector('.no-scroll-helper-inner');
412
- let noScrollClass = this.settings.openStatusBodyClass;
413
428
 
414
- if (document.documentElement.scrollHeight > window.innerHeight) {
415
- noScrollClass += ' ' + this.settings.openStatusBodyClassOverflow;
416
- }
429
+ // heightMode "full" with deprecated fullHeight-setting as fallback
430
+ if (this.settings.heightMode === 'full' || this.settings.fullHeight === true) {
431
+ const body = document.body;
432
+ const helper = body.querySelector('.no-scroll-helper');
433
+ const inner = body.querySelector('.no-scroll-helper-inner');
434
+ let noScrollClass ='';
435
+
436
+ if (document.documentElement.scrollHeight > window.innerHeight) {
437
+ noScrollClass = this.settings.openStatusBodyClassOverflow;
438
+ }
439
+
440
+ if (!body.classList.contains(this.settings.openStatusBodyClass)) {
441
+ const scrollTop = -document.documentElement.scrollTop;
442
+ helper.setAttribute('data-scroll-top', scrollTop);
443
+ helper.style.cssText = 'position:relative;overflow:hidden;height:100vh;width:100%';
444
+ inner.style.cssText = `position:absolute;top:${scrollTop}px;height:100%;width:100%`;
445
+ body.classList.add(noScrollClass);
446
+ window.scrollTo({top: 0, behavior: 'instant'});
447
+ } else {
448
+ const scrollTop = parseInt(helper.getAttribute('data-scroll-top') || '0') * -1;
449
+ helper.removeAttribute('style');
450
+ inner.removeAttribute('style');
451
+ body.classList.remove(this.settings.openStatusBodyClassOverflow);
452
+ window.scrollTo({top: scrollTop, behavior: 'instant'});
453
+ }
417
454
 
418
- if (!body.classList.contains(this.settings.openStatusBodyClass)) {
419
- const scrollTop = -document.documentElement.scrollTop;
420
- helper.setAttribute('data-scroll-top', scrollTop);
421
- helper.style.cssText = 'position:relative;overflow:hidden;height:100vh;width:100%';
422
- inner.style.cssText = `position:absolute;top:${scrollTop}px;height:100%;width:100%`;
423
- body.classList.add(...noScrollClass.split(' '));
424
- window.scrollTo({top: 0, behavior: 'instant'});
425
- } else {
426
- const scrollTop = parseInt(helper.getAttribute('data-scroll-top') || '0') * -1;
427
- helper.removeAttribute('style');
428
- inner.removeAttribute('style');
429
- body.classList.remove(this.settings.openStatusBodyClass, this.settings.openStatusBodyClassOverflow);
430
- window.scrollTo({top: scrollTop, behavior: 'instant'});
431
455
  }
432
456
  }
433
457
  }
@@ -1,3 +1,7 @@
1
+ html, body {
2
+ height: 100.1%; // enforce scrollbar
3
+ }
4
+
1
5
  .flyout {
2
6
  position: absolute;
3
7
  left: 0;
@@ -5,12 +9,18 @@
5
9
 
6
10
  width: 100%;
7
11
  z-index: -1;
8
- overflow:hidden;
12
+ overflow: hidden;
9
13
  visibility: hidden;
14
+ max-height: 0;
15
+
16
+ &.calculate {
17
+ max-height: none;
18
+ }
10
19
 
11
20
  &.open {
12
21
  z-index: 890;
13
22
  visibility: visible;
23
+ max-height: none;
14
24
 
15
25
  .flyout-container {
16
26
  pointer-events: auto;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@madj2k/fe-frontend-kit",
3
- "version": "2.0.24",
3
+ "version": "2.0.26",
4
4
  "description": "Shared frontend utilities, menus and mixins for projects",
5
5
  "main": "index.js",
6
6
  "style": "index.scss",
package/readme.md CHANGED
@@ -26,6 +26,179 @@ Or import individual mixins:
26
26
  ### Menu components (JS and SCSS):
27
27
  Each menu component can be used separately.
28
28
 
29
+ ### JS components (JS and SCSS):
30
+ Each JS component can be used separately.
31
+
32
+
33
+ # Flyout-Navigation
34
+ ## Usage
35
+ Integrate the CSS- and JS-file into your project. Make sure jQuery is included.
36
+ Then init the menu with
37
+ ```
38
+ import { Madj2kFlyoutMenu } from '@madj2k/fe-frontend-kit/menus/flyout-menu';
39
+ document.querySelectorAll('.js-flyout-toggle').forEach((el) => {
40
+ new Madj2kFlyoutMenu(el, { animationDuration: 800 });
41
+ });
42
+ ```
43
+ Optional for automatically closing of the flyout menu resizing the browser window:
44
+ ```
45
+ import { Madj2kBetterResizeEvent } from '@madj2k/fe-frontend-kit/tools/better-resize-event';
46
+ document.addEventListener('madj2k-better-resize-event', () => {
47
+ document.dispatchEvent(new CustomEvent('madj2k-flyoutmenu-close'));
48
+ });
49
+ ```
50
+
51
+ CSS:
52
+ ```
53
+ @use '@madj2k/fe-frontend-kit/menus/flyout-menu' as *;
54
+ ```
55
+
56
+ ## Basic HTML
57
+ ```
58
+ <div class="siteheader" id="siteheader">
59
+
60
+ [...]
61
+
62
+ <nav>
63
+ <button class="js-flyout-toggle"
64
+ aria-label="Open menu"
65
+ aria-controls="nav-mobile"
66
+ aria-haspopup="true"
67
+ aria-expanded="false">
68
+ <span class="icon-bars"></span>
69
+ </button>
70
+ <div class="flyout js-flyout"
71
+ id="nav-mobile"
72
+ data-position-ref="siteheader">
73
+ <div class="flyout-container js-flyout-container">
74
+ <div class="nav-mobile-inner js-flyout-inner">
75
+ CONTENT HERE
76
+ </div>
77
+ </div>
78
+ </div>
79
+ </nav>
80
+ </div>
81
+ ```
82
+ IMPORTANT: If the siteheader is positioned with ```position:fixed```, you have to switch that to ```position:absolute``` when the menu is opened.
83
+ Otherwise in the opened menu the scrolling won't work.
84
+ ```
85
+ .flyout-open {
86
+ .siteheader {
87
+ position:absolute;
88
+ }
89
+ }
90
+ ```
91
+ ## Special: blur/gray effect for background
92
+ * In order to achieve a blur/gray-effect for the background we add the following DIV to the main-content section:
93
+ ```
94
+ <div class="background-blur"></div>
95
+ ```
96
+ Then we deactivate the fullHeight of the flyout menu and make the blurry background clickable
97
+ ```
98
+ import { Madj2kFlyoutdownMenu } from '@madj2k/fe-frontend-kit/menus/flyout-menu';
99
+ document.querySelectorAll('.js-flyout-toggle').forEach((el) => {
100
+ new Madj2kFlyoutMenu(el, { fullHeight: false });
101
+ });
102
+ document.querySelector('.js-background-blur').addEventListener('click', function() {
103
+ document.dispatchEvent(new CustomEvent('madj2k-flyoutmenu-close'));
104
+ });
105
+ ```
106
+ * And we need this additional CSS:
107
+ ```
108
+ /**
109
+ * background-blur for opened flyout
110
+ */
111
+ .background-blur {
112
+ position:fixed;
113
+ top:0;
114
+ left:0;
115
+ width: 100%;
116
+ height: 100%;
117
+ backdrop-filter: blur(1px) grayscale(100%);
118
+ background-color: rgba(255, 255, 255, 0.7);
119
+ transition: opacity 0.3s ease-in-out;
120
+ opacity:0;
121
+ z-index:-1;
122
+ }
123
+
124
+ .flyout-open,
125
+ .flyout-closing {
126
+ .background-blur {
127
+ z-index:90;
128
+ }
129
+ }
130
+
131
+ .flyout-open{
132
+ .background-blur {
133
+ opacity:1;
134
+ }
135
+ }
136
+
137
+
138
+ ```
139
+
140
+ # Pulldown-Navigation
141
+ ## Usage
142
+ Integrate the CSS- and JS-file into your project. Make sure jQuery is included.
143
+ Then init the menu with
144
+ ```
145
+ import { Madj2kPulldownMenu } from '@madj2k/fe-frontend-kit/menus/pulldown-menu';
146
+ document.querySelectorAll('.js-pulldown-toggle').forEach((el) => {
147
+ new Madj2kPulldownMenu(el);
148
+ });
149
+ ```
150
+ Optional for automatically closing of the flyout menu resizing the browser window:
151
+ ```
152
+ import { Madj2kBetterResizeEvent } from '@madj2k/fe-frontend-kit/tools/better-resize-event';
153
+ document.addEventListener('madj2k-better-resize-event', () => {
154
+ document.dispatchEvent(new CustomEvent('madj2k-pulldownmenu-close'));
155
+ });
156
+ ```
157
+
158
+ CSS:
159
+ ```
160
+ @use '@madj2k/fe-frontend-kit/menus/pulldown-menu' as *;
161
+ ```
162
+
163
+ ## Basic HTML
164
+ ```
165
+ <div class="siteheader">
166
+
167
+ [...]
168
+
169
+ <nav class="pulldown-wrap js-pulldown-wrap">
170
+ <button class="pulldown-toggle js-pulldown-toggle"
171
+ aria-label="Open Menu"
172
+ aria-controls="nav-language"
173
+ aria-haspopup="true"
174
+ aria-expanded="false">
175
+ <span>Menu-Item Level 1</span>
176
+ </button>
177
+
178
+ <div class="pulldown js-pulldown" id="nav-language">
179
+ <div class="pulldown-inner">
180
+ <ul>
181
+ <!-- used to have the right padding-top of the pulldown -->
182
+ <li class="pulldown-hide">
183
+ <a href="#" tabIndex="-1"><span>Menu-Item Level 1</span></a>
184
+ </li>
185
+ <li>
186
+ <a href="#"><span>Menu-Item I Level 2</span></a>
187
+ </li>
188
+ <li>
189
+ <a href="#"><span>Menu-Item II Level 2</span></a>
190
+ </li>
191
+ <li>
192
+ ...
193
+ </li>
194
+ </ul>
195
+ </div>
196
+ </div>
197
+ </nav>
198
+ </div>
199
+ ```
200
+
201
+
29
202
  # JS: Banner
30
203
  A lightweight class to show a full-page overlay (banner, popup, hint or cookie layer),
31
204
  with opening and closing animation and optional cookie persistence.
@@ -218,177 +391,36 @@ HTML:
218
391
  </div>
219
392
  ```
220
393
 
221
- # Flyout-Navigation
222
- ## Usage
223
- Integrate the CSS- and JS-file into your project. Make sure jQuery is included.
224
- Then init the menu with
225
- ```
226
- import { Madj2kFlyoutMenu } from '@madj2k/fe-frontend-kit/menus/flyout-menu';
227
- document.querySelectorAll('.js-flyout-toggle').forEach((el) => {
228
- new Madj2kFlyoutMenu(el, { animationDuration: 800 });
229
- });
230
- ```
231
- Optional for automatically closing of the flyout menu resizing the browser window:
232
- ```
233
- import { Madj2kBetterResizeEvent } from '@madj2k/fe-frontend-kit/tools/better-resize-event';
234
- document.addEventListener('madj2k-better-resize-event', () => {
235
- document.dispatchEvent(new CustomEvent('madj2k-flyoutmenu-close'));
236
- });
237
- ```
238
-
239
- CSS:
240
- ```
241
- @use '@madj2k/fe-frontend-kit/menus/flyout-menu' as *;
242
- ```
243
-
244
- ## Basic HTML
245
- ```
246
- <div class="siteheader" id="siteheader">
247
-
248
- [...]
249
-
250
- <nav>
251
- <button class="js-flyout-toggle"
252
- aria-label="Open menu"
253
- aria-controls="nav-mobile"
254
- aria-haspopup="true"
255
- aria-expanded="false">
256
- <span class="icon-bars"></span>
257
- </button>
258
- <div class="flyout js-flyout"
259
- id="nav-mobile"
260
- data-position-ref="siteheader">
261
- <div class="flyout-container js-flyout-container">
262
- <div class="nav-mobile-inner js-flyout-inner">
263
- CONTENT HERE
264
- </div>
265
- </div>
266
- </div>
267
- </nav>
268
- </div>
269
- ```
270
- IMPORTANT: If the siteheader is positioned with ```position:fixed```, you have to switch that to ```position:absolute``` when the menu is opened.
271
- Otherwise in the opened menu the scrolling won't work.
272
- ```
273
- .flyout-open {
274
- .siteheader {
275
- position:absolute;
276
- }
277
- }
278
- ```
279
- ## Special: blur/gray effect for background
280
- * In order to achieve a blur/gray-effect for the background we add the following DIV to the main-content section:
281
- ```
282
- <div class="background-blur"></div>
283
- ```
284
- Then we deactivate the fullHeight of the flyout menu and make the blurry background clickable
285
- ```
286
- import { Madj2kFlyoutdownMenu } from '@madj2k/fe-frontend-kit/menus/flyout-menu';
287
- document.querySelectorAll('.js-flyout-toggle').forEach((el) => {
288
- new Madj2kFlyoutMenu(el, { fullHeight: false });
289
- });
290
- document.querySelector('.js-background-blur').addEventListener('click', function() {
291
- document.dispatchEvent(new CustomEvent('madj2k-flyoutmenu-close'));
292
- });
293
- ```
294
- * And we need this additional CSS:
295
- ```
296
- /**
297
- * Prevent jumping because of scrollbar
298
- */
299
- html,body {
300
- min-height: 100.1%;
301
- }
302
-
303
- /**
304
- * background-blur for opened flyout
305
- */
306
- .background-blur {
307
- position:fixed;
308
- top:0;
309
- left:0;
310
- width: 100%;
311
- height: 100%;
312
- backdrop-filter: blur(1px) grayscale(100%);
313
- background-color: rgba(255, 255, 255, 0.7);
314
- transition: opacity 0.3s ease-in-out;
315
- opacity:0;
316
- z-index:-1;
317
- }
318
-
319
- .flyout-open,
320
- .flyout-closing {
321
- .background-blur {
322
- z-index:90;
323
- }
324
- }
325
-
326
- .flyout-open{
327
- .background-blur {
328
- opacity:1;
329
- }
330
- }
331
-
394
+ # JS: Simple Fade Slider
395
+ A lightweight fade slider using opacity and z-index.
396
+ - Purely DOM based (no keyframes required)
397
+ - Automatic looping through an arbitrary number of slides
398
+ - Fully accessible with aria-hidden management
399
+ - Configurable via options
400
+ - Designed for CMS contexts with dynamic content
332
401
 
402
+ Init with available options:
333
403
  ```
334
-
335
- # Pulldown-Navigation
336
- ## Usage
337
- Integrate the CSS- and JS-file into your project. Make sure jQuery is included.
338
- Then init the menu with
339
- ```
340
- import { Madj2kPulldownMenu } from '@madj2k/fe-frontend-kit/menus/pulldown-menu';
341
- document.querySelectorAll('.js-pulldown-toggle').forEach((el) => {
342
- new Madj2kPulldownMenu(el);
343
- });
344
- ```
345
- Optional for automatically closing of the flyout menu resizing the browser window:
346
- ```
347
- import { Madj2kBetterResizeEvent } from '@madj2k/fe-frontend-kit/tools/better-resize-event';
348
- document.addEventListener('madj2k-better-resize-event', () => {
349
- document.dispatchEvent(new CustomEvent('madj2k-pulldownmenu-close'));
404
+ import { Madj2kSimpleFadeSlider } from '@madj2k/fe-frontend-kit/tools/simple-fade-slider';
405
+ const fadeSlider = new Madj2kSimpleFadeSlider('.js-fade-slider', {
406
+ duration: 8,
407
+ classSlide: 'fade-slider-item',
408
+ classVisible: 'is-visible',
409
+ debug: true
350
410
  });
351
411
  ```
352
412
 
353
- CSS:
354
- ```
355
- @use '@madj2k/fe-frontend-kit/menus/pulldown-menu' as *;
356
- ```
357
-
358
- ## Basic HTML
413
+ HTML:
359
414
  ```
360
- <div class="siteheader">
361
-
362
- [...]
363
-
364
- <nav class="pulldown-wrap js-pulldown-wrap">
365
- <button class="pulldown-toggle js-pulldown-toggle"
366
- aria-label="Open Menu"
367
- aria-controls="nav-language"
368
- aria-haspopup="true"
369
- aria-expanded="false">
370
- <span>Menu-Item Level 1</span>
371
- </button>
372
-
373
- <div class="pulldown js-pulldown" id="nav-language">
374
- <div class="pulldown-inner">
375
- <ul>
376
- <!-- used to have the right padding-top of the pulldown -->
377
- <li class="pulldown-hide">
378
- <a href="#" tabIndex="-1"><span>Menu-Item Level 1</span></a>
379
- </li>
380
- <li>
381
- <a href="#"><span>Menu-Item I Level 2</span></a>
382
- </li>
383
- <li>
384
- <a href="#"><span>Menu-Item II Level 2</span></a>
385
- </li>
386
- <li>
387
- ...
388
- </li>
389
- </ul>
390
- </div>
391
- </div>
392
- </nav>
393
- </div>
415
+ <section class="fade-slider js-fade-slider" aria-label="Image gallery">
416
+ <div class="fade-slider-item">
417
+ <img src="img1.jpg" alt="Description 1">
418
+ </div>
419
+ <div class="fade-slider-item">
420
+ <img src="img2.jpg" alt="Description 2">
421
+ </div>
422
+ <div class="fade-slider-item">
423
+ <img src="img3.jpg" alt="Description 3">
424
+ </div>
425
+ </section>
394
426
  ```
@@ -268,11 +268,24 @@
268
268
  /// @param {Color} $icon-color - Icon color (e.g., fill or text color). Defaults to `$color-primary`.
269
269
  /// @param {Map} $icon-mappings - Map of icon toggle states (e.g., hamburger → close). Defaults to a predefined map.
270
270
  ///
271
- /// @example html
271
+ /// @example html with icon font
272
+ /// <button class="nav-toggle" aria-expanded="false">
273
+ /// <i class="nav-toggle-icon icon-hamburger icon"></i>
274
+ /// </button>
275
+ ///
276
+ /// @example html with icon sprite
272
277
  /// <button class="nav-toggle" aria-expanded="false">
273
- /// <i class="nav-toggle-icon icon-hamburger"></i>
278
+ /// <i class="nav-toggle-icon">
279
+ /// <svg class="nav-toggle-icon-opened" aria-hidden="true" focusable="false">
280
+ /// <use href="sprite.svg#search"></use>
281
+ /// </svg>
282
+ /// <svg class="nav-toggle-icon-closed" aria-hidden="true" focusable="false">
283
+ /// <use href="sprite.svg#close"></use>
284
+ /// </svg>
285
+ /// </i>
274
286
  /// </button>
275
287
  ///
288
+ ///
276
289
  /// @example scss
277
290
  /// .nav-toggle {
278
291
  /// @include nav-toggle(
@@ -305,6 +318,7 @@
305
318
  display: flex;
306
319
  align-items: center;
307
320
 
321
+ // variant with icons as font
308
322
  &[aria-expanded="true"] {
309
323
  @each $icon-class, $content in $icon-mappings {
310
324
  .#{$icon-class} {
@@ -314,11 +328,30 @@
314
328
  }
315
329
  }
316
330
  }
331
+
332
+ // variant with icons in a sprite
333
+ .nav-toggle-icon-opened {
334
+ display: none;
335
+ }
336
+
337
+ &[aria-expanded="true"] {
338
+ .nav-toggle-icon-opened {
339
+ display: inline-block;
340
+ }
341
+ .nav-toggle-icon-closed {
342
+ display: none;
343
+ }
344
+ }
317
345
  }
318
346
 
319
- &-icon {
347
+ &-icon:not(:has(svg)),
348
+ &-icon svg {
320
349
  font-size: rem-calc($icon-size);
321
350
  width: rem-calc($icon-size);
322
351
  color: $icon-color;
323
352
  }
353
+
354
+ &-icon svg {
355
+ height: 100%;
356
+ }
324
357
  }
@@ -0,0 +1,2 @@
1
+ import Madj2kSimpleFadeSlider from './simple-fade-slider-2.0';
2
+ export { Madj2kSimpleFadeSlider };
@@ -0,0 +1 @@
1
+ @forward './simple-fade-slider-2.0';
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Madj2kSimpleFadeSlider
3
+ *
4
+ * A lightweight fade slider using opacity and z-index.
5
+ * - Purely DOM based (no keyframes required)
6
+ * - Automatic looping through an arbitrary number of slides
7
+ * - Fully accessible with aria-hidden management
8
+ * - Configurable via options
9
+ * - Designed for CMS contexts with dynamic content
10
+ *
11
+ * @author Steffen Kroggel <developer@steffenkroggel.de>
12
+ * @copyright 2025 Steffen Kroggel
13
+ * @version 1.0.0
14
+ * @license GNU General Public License v3.0
15
+ * @see https://www.gnu.org/licenses/gpl-3.0.en.html
16
+ *
17
+ * @example
18
+ * // Initialize with defaults
19
+ * const slider = new Madj2kSimpleFadeSlider('.js-fade-sliderr');
20
+ *
21
+ * @example
22
+ * // Initialize with custom config
23
+ * const slider = new Madj2kSimpleFadeSlider('.js-fade-sliderr', {
24
+ * duration: 8,
25
+ * classSlide: 'fade-slider-item',
26
+ * classVisible: 'is-visible',
27
+ * debug: true
28
+ * });
29
+ *
30
+ * @example
31
+ * // Example HTML
32
+ * <section class="fade-slider js-fade-sliderr" aria-label="Image gallery">
33
+ * <div class="fade-slider-item"><img src="img1.jpg" alt="Description 1"></div>
34
+ * <div class="fade-slider-item"><img src="img2.jpg" alt="Description 2"></div>
35
+ * <div class="fade-slider-item"><img src="img3.jpg" alt="Description 3"></div>
36
+ * </section>
37
+ *
38
+ * @example
39
+ * // Example SCSS
40
+ * .fade-slider {
41
+ * position: relative;
42
+ * height: 300px;
43
+ * overflow: hidden;
44
+ * }
45
+ *
46
+ * .fade-slide, .fade-slider-item {
47
+ * position: absolute;
48
+ * inset: 0;
49
+ * opacity: 0;
50
+ * z-index: 1;
51
+ * transition: opacity 1s ease-in-out;
52
+ * }
53
+ *
54
+ * .fade-slide.is-visible, .fade-slider-item.is-visible {
55
+ * opacity: 1;
56
+ * z-index: 2;
57
+ * }
58
+ */
59
+
60
+ class Madj2kSimpleFadeSlider {
61
+ config = {
62
+ duration: 12,
63
+ classSlide: 'fade-slide',
64
+ classVisible: 'is-visible',
65
+ debug: false
66
+ };
67
+
68
+ /**
69
+ * @param {string} selector - CSS selector for the slider container
70
+ * @param {Object} config - configuration options
71
+ * @param {number} [config.duration=12] - time in seconds between slide changes
72
+ * @param {string} [config.classSlide='fade-slide'] - class for each slide
73
+ * @param {string} [config.classVisible='is-visible'] - class for visible slide
74
+ * @param {boolean} [config.debug=false] - enable debug logging
75
+ */
76
+ constructor(selector, config = {}) {
77
+ this.container = document.querySelector(selector);
78
+ if (!this.container) return;
79
+
80
+ this.config = {
81
+ ...this.config,
82
+ ...config
83
+ };
84
+
85
+ this.slides = Array.from(this.container.querySelectorAll(`.${this.config.classSlide}`));
86
+ this.total = this.slides.length;
87
+ this.current = 0;
88
+
89
+ this._log('Initialized with config:', this.config);
90
+
91
+ this.setup();
92
+ this.start();
93
+ }
94
+
95
+ /**
96
+ * Initializes all slides
97
+ * @private
98
+ */
99
+ setup() {
100
+ this.slides.forEach((slide, index) => {
101
+ slide.classList.add(this.config.classSlide);
102
+ slide.setAttribute('aria-hidden', index === 0 ? 'false' : 'true');
103
+ });
104
+ if (this.slides[0]) {
105
+ this.slides[0].classList.add(this.config.classVisible);
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Starts the automatic fade loop
111
+ * @private
112
+ */
113
+ start() {
114
+ setInterval(() => this.showNext(), this.config.duration * 1000);
115
+ }
116
+
117
+ /**
118
+ * Shows the next slide
119
+ * @private
120
+ */
121
+ showNext() {
122
+ const currentSlide = this.slides[this.current];
123
+ if (currentSlide) {
124
+ currentSlide.classList.remove(this.config.classVisible);
125
+ currentSlide.setAttribute('aria-hidden', 'true');
126
+ }
127
+
128
+ this.current = (this.current + 1) % this.total;
129
+
130
+ const nextSlide = this.slides[this.current];
131
+ if (nextSlide) {
132
+ nextSlide.classList.add(this.config.classVisible);
133
+ nextSlide.setAttribute('aria-hidden', 'false');
134
+ }
135
+
136
+ this._log('Switched to slide', this.current);
137
+ }
138
+
139
+ /**
140
+ * Debug logging helper
141
+ * @private
142
+ */
143
+ _log(...args) {
144
+ if (this.config.debug) {
145
+ console.log('[Madj2kSimpleFadeSlider]', ...args);
146
+ }
147
+ }
148
+ }
@@ -0,0 +1,18 @@
1
+ .fade-slider {
2
+ position: relative;
3
+ height: 100vh;
4
+ overflow: hidden;
5
+
6
+ &-item {
7
+ position: absolute;
8
+ inset: 0;
9
+ opacity: 0;
10
+ z-index: 1;
11
+ transition: opacity 1s ease-in-out;
12
+
13
+ &.is-visible {
14
+ opacity: 1;
15
+ z-index: 2;
16
+ }
17
+ }
18
+ }