@madj2k/fe-frontend-kit 2.0.31 → 2.0.33
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 +1 -0
- package/index.scss +1 -0
- package/package.json +1 -1
- package/readme.md +46 -0
- package/sass/bootstrap-5.3/10_mixins/_icons.scss +4 -0
- package/sass/bootstrap-5.3/10_mixins/_nav.scss +13 -43
- package/sass/bootstrap-5.3/10_mixins/_section.scss +18 -0
- package/sass/bootstrap-5.3/10_mixins/_toggle.scss +141 -0
- package/sass/bootstrap-5.3/10_mixins/index.scss +1 -0
- package/tools/element-in-viewport/element-in-viewport-2.0.js +117 -0
- package/tools/element-in-viewport/element-in-viewport-2.0.scss +0 -0
- package/tools/element-in-viewport/index.js +2 -0
- package/tools/element-in-viewport/index.scss +1 -0
package/index.js
CHANGED
|
@@ -6,6 +6,7 @@ export { Madj2kResizeEnd } from './tools/resize-end';
|
|
|
6
6
|
export { Madj2kScrolling } from './tools/scrolling';
|
|
7
7
|
export { Madj2kSimpleFadeSlider } from './tools/simple-fade-slider';
|
|
8
8
|
export { Madj2kToggledOverlay } from './tools/toggled-overlay';
|
|
9
|
+
export { Madj2kElementInViewport } from './tools/element-in-viewport';
|
|
9
10
|
|
|
10
11
|
// Menus
|
|
11
12
|
export { Madj2kFlyoutMenu } from './menus/flyout-menu';
|
package/index.scss
CHANGED
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -366,6 +366,52 @@ Usage with Appear-On-Scroll (HTML):
|
|
|
366
366
|
</div>
|
|
367
367
|
```
|
|
368
368
|
|
|
369
|
+
# JS: Element In Viewport
|
|
370
|
+
A lightweight helper class that adds a configurable class to any DOM element once it becomes visible in the viewport.
|
|
371
|
+
Perfect for triggering CSS-based animations (e.g., quote reveals, fade-ins, transitions) when an element enters view.
|
|
372
|
+
* Works with IntersectionObserver API
|
|
373
|
+
* Purely DOM-based (no keyframes required)
|
|
374
|
+
* Fully configurable (threshold, delay, class)
|
|
375
|
+
* Ideal for CMS-driven content (dynamic DOM)
|
|
376
|
+
* Designed for performance and flexibility
|
|
377
|
+
|
|
378
|
+
Init:
|
|
379
|
+
```
|
|
380
|
+
document.querySelectorAll('.js-inview').forEach((el) => {
|
|
381
|
+
new Madj2kElementInViewport(el, {
|
|
382
|
+
visibleClass: 'is-in-viewport',
|
|
383
|
+
threshold: 0.5,
|
|
384
|
+
debug: false
|
|
385
|
+
});
|
|
386
|
+
});
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
HTML-Example
|
|
390
|
+
```
|
|
391
|
+
<section class="my-element js-inview">
|
|
392
|
+
<div class="my-element-content">Lorem ipsum dolor sit amet.</div>
|
|
393
|
+
</section>
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
SCSS-Example
|
|
397
|
+
```
|
|
398
|
+
.my-element {
|
|
399
|
+
.my-element-content {
|
|
400
|
+
opacity: 0;
|
|
401
|
+
transform: translateY(20%);
|
|
402
|
+
transition: opacity 0.6s ease, transform 0.6s ease;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
&.is-in-viewport {
|
|
406
|
+
.my-element-content {
|
|
407
|
+
opacity: 1;
|
|
408
|
+
transform: translateY(0);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
|
|
369
415
|
# JS: Toggled Overlay
|
|
370
416
|
This class toggles the visibility of any target element referenced by the `aria-controls`
|
|
371
417
|
attribute of a trigger element (button, link, etc.). It manages ARIA attributes for accessibility
|
|
@@ -91,8 +91,12 @@
|
|
|
91
91
|
///
|
|
92
92
|
/// @license
|
|
93
93
|
/// GNU General Public License v3.0 https://www.gnu.org/licenses/gpl-3.0.en.html
|
|
94
|
+
/// @deprecated
|
|
94
95
|
///
|
|
95
96
|
@mixin toggle-icon($selector: '.toggle') {
|
|
97
|
+
|
|
98
|
+
@warn "toggle-icon() is deprecated. Use toggle() instead.";
|
|
99
|
+
|
|
96
100
|
#{$selector}:has(.icon:first-child) {
|
|
97
101
|
line-height: 1; // icon-only toggle buttons
|
|
98
102
|
}
|
|
@@ -250,6 +250,12 @@
|
|
|
250
250
|
align-items: center;
|
|
251
251
|
gap: rem-calc($gap);
|
|
252
252
|
height: 100%;
|
|
253
|
+
|
|
254
|
+
@warn "nav-toggle-group() is deprecated. Use toggle-group() instead.";
|
|
255
|
+
|
|
256
|
+
@include toggle-group(
|
|
257
|
+
$gap: $gap,
|
|
258
|
+
);
|
|
253
259
|
}
|
|
254
260
|
|
|
255
261
|
|
|
@@ -309,49 +315,13 @@
|
|
|
309
315
|
"icon-plus": unquote('"\\e908"')
|
|
310
316
|
)
|
|
311
317
|
) {
|
|
312
|
-
background-color: transparent;
|
|
313
|
-
border: 0;
|
|
314
|
-
margin: 0;
|
|
315
|
-
padding: 0;
|
|
316
|
-
|
|
317
|
-
&:has(.nav-toggle-icon) {
|
|
318
|
-
display: flex;
|
|
319
|
-
align-items: center;
|
|
320
|
-
|
|
321
|
-
// variant with icons as font
|
|
322
|
-
&[aria-expanded="true"] {
|
|
323
|
-
@each $icon-class, $content in $icon-mappings {
|
|
324
|
-
.#{$icon-class} {
|
|
325
|
-
&:before {
|
|
326
|
-
content: $content;
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
}
|
|
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
|
-
}
|
|
345
|
-
}
|
|
346
318
|
|
|
347
|
-
|
|
348
|
-
&-icon svg {
|
|
349
|
-
font-size: rem-calc($icon-size);
|
|
350
|
-
width: rem-calc($icon-size);
|
|
351
|
-
color: $icon-color;
|
|
352
|
-
}
|
|
319
|
+
@warn "nav-toggle() is deprecated. Use toggle() instead.";
|
|
353
320
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
321
|
+
@include toggle(
|
|
322
|
+
$icon-size: $icon-size,
|
|
323
|
+
$icon-color: $icon-color,
|
|
324
|
+
$icon-mappings: $icon-mappings,
|
|
325
|
+
$icon-selector: '.nav-toggle-icon'
|
|
326
|
+
);
|
|
357
327
|
}
|
|
@@ -43,6 +43,15 @@
|
|
|
43
43
|
/// [...]
|
|
44
44
|
/// </div>
|
|
45
45
|
///
|
|
46
|
+
/// @example html
|
|
47
|
+
/// <div class="csp-section">
|
|
48
|
+
/// [...]
|
|
49
|
+
/// </div>
|
|
50
|
+
/// <!-- With vertical padding instead of margin -->
|
|
51
|
+
/// <div class="csp-section csp-section-padding">
|
|
52
|
+
/// [...]
|
|
53
|
+
/// </div>
|
|
54
|
+
///
|
|
46
55
|
/// @example scss
|
|
47
56
|
/// .layout-default {
|
|
48
57
|
/// @include section-spacing();
|
|
@@ -56,6 +65,7 @@
|
|
|
56
65
|
$block-class: 'csp-block',
|
|
57
66
|
$not-last-class: 'csp-not-last',
|
|
58
67
|
$utility-append-class: 'sp',
|
|
68
|
+
$padding-append-class: 'padding',
|
|
59
69
|
$csp-blocks: (
|
|
60
70
|
'text',
|
|
61
71
|
'text-image',
|
|
@@ -101,9 +111,17 @@
|
|
|
101
111
|
}
|
|
102
112
|
|
|
103
113
|
.#{$section-class}.#{$not-last-class}:not(:has(+ .#{$section-class})),
|
|
114
|
+
.#{$section-class}.#{$section-class}-#{$padding-append-class},
|
|
115
|
+
.#{$section-class}:has(+ .#{$section-class}-#{$padding-append-class}),
|
|
104
116
|
.#{$section-class}-#{$utility-append-class}.#{$not-last-class}:not(:has(+ .#{$section-class})) {
|
|
105
117
|
margin-bottom: 0;
|
|
106
118
|
}
|
|
119
|
+
|
|
120
|
+
.#{$section-class}-#{$padding-append-class},
|
|
121
|
+
.#{$section-class}-#{$padding-append-class}-#{$utility-append-class} {
|
|
122
|
+
padding-top: rem-calc($spacer-section);
|
|
123
|
+
padding-bottom: rem-calc($spacer-section);
|
|
124
|
+
}
|
|
107
125
|
}
|
|
108
126
|
|
|
109
127
|
// block-spacing
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
|
|
2
|
+
/* ==================================================
|
|
3
|
+
* Toggle
|
|
4
|
+
* ==================================================*/
|
|
5
|
+
/// Creates a button group container for toggles.
|
|
6
|
+
///
|
|
7
|
+
/// Used to align toggle buttons and icons horizontally with spacing.
|
|
8
|
+
/// Useful in responsive navigation headers.
|
|
9
|
+
///
|
|
10
|
+
/// @group Toggle
|
|
11
|
+
///
|
|
12
|
+
/// @param {Length} $gap - The spacing between toggle elements. Defaults to `$spacer`.
|
|
13
|
+
///
|
|
14
|
+
/// @example html
|
|
15
|
+
/// <div class="toggle-group">
|
|
16
|
+
/// <button class="toggle" aria-expanded="false">
|
|
17
|
+
/// <i class="toggle-icon icon-hamburger"></i>
|
|
18
|
+
/// </button>
|
|
19
|
+
/// </div>
|
|
20
|
+
///
|
|
21
|
+
/// @example scss
|
|
22
|
+
/// .toggle-group {
|
|
23
|
+
/// @include toggle-group($gap: 1rem);
|
|
24
|
+
/// }
|
|
25
|
+
///
|
|
26
|
+
/// @author Steffen Kroggel <developer@steffenkroggel>
|
|
27
|
+
/// @license GNU General Public License v3.0 https://www.gnu.org/licenses/gpl-3.0.en.html
|
|
28
|
+
///
|
|
29
|
+
@mixin toggle-group(
|
|
30
|
+
$gap: 16px
|
|
31
|
+
) {
|
|
32
|
+
display: flex;
|
|
33
|
+
align-items: center;
|
|
34
|
+
gap: rem-calc($gap);
|
|
35
|
+
height: 100%;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
/// Creates a button element for toggling.
|
|
40
|
+
///
|
|
41
|
+
/// Features:
|
|
42
|
+
/// - Accessible button with `aria-expanded` state
|
|
43
|
+
/// - Icon switching based on toggle state (e.g., hamburger to close)
|
|
44
|
+
/// - Configurable icon size and colors
|
|
45
|
+
/// - No default background or borders for flexible styling
|
|
46
|
+
/// - Built-in accessibility outline
|
|
47
|
+
///
|
|
48
|
+
/// @group Toggle
|
|
49
|
+
///
|
|
50
|
+
/// @param {Length} $icon-size - Size of the icon (width/height). Defaults to `$spacer`.
|
|
51
|
+
/// @param {Color} $icon-color - Icon color (e.g., fill or text color). Defaults to `$color-primary`.
|
|
52
|
+
/// @param {Map} $icon-mappings - Map of icon toggle states (e.g., hamburger → close). Defaults to a predefined map.
|
|
53
|
+
///
|
|
54
|
+
/// @example html with icon font
|
|
55
|
+
/// <button class="toggle" aria-expanded="false">
|
|
56
|
+
/// <i class="toggle-icon icon-hamburger icon"></i>
|
|
57
|
+
/// </button>
|
|
58
|
+
///
|
|
59
|
+
/// @example html with icon sprite
|
|
60
|
+
/// <button class="toggle" aria-expanded="false">
|
|
61
|
+
/// <i class="toggle-icon">
|
|
62
|
+
/// <svg class="toggle-icon-opened" aria-hidden="true" focusable="false">
|
|
63
|
+
/// <use href="sprite.svg#search"></use>
|
|
64
|
+
/// </svg>
|
|
65
|
+
/// <svg class="toggle-icon-closed" aria-hidden="true" focusable="false">
|
|
66
|
+
/// <use href="sprite.svg#close"></use>
|
|
67
|
+
/// </svg>
|
|
68
|
+
/// </i>
|
|
69
|
+
/// </button>
|
|
70
|
+
///
|
|
71
|
+
///
|
|
72
|
+
/// @example scss
|
|
73
|
+
/// .toggle {
|
|
74
|
+
/// @include toggle(
|
|
75
|
+
/// $icon-size: 1.5rem,
|
|
76
|
+
/// $icon-color: #333,
|
|
77
|
+
/// $icon-mappings: (
|
|
78
|
+
/// "icon-hamburger": "icon-close",
|
|
79
|
+
/// "icon-plus": "icon-minus"
|
|
80
|
+
/// )
|
|
81
|
+
/// );
|
|
82
|
+
/// }
|
|
83
|
+
///
|
|
84
|
+
/// @author Steffen Kroggel <developer@steffenkroggel>
|
|
85
|
+
/// @license GNU General Public License v3.0 https://www.gnu.org/licenses/gpl-3.0.en.html
|
|
86
|
+
///
|
|
87
|
+
@mixin toggle(
|
|
88
|
+
$icon-size: 16px,
|
|
89
|
+
$icon-color: var(--bs-primary),
|
|
90
|
+
$icon-mappings: (
|
|
91
|
+
"icon-hamburger": unquote('"\\e905"'),
|
|
92
|
+
"icon-plus": unquote('"\\e908"')
|
|
93
|
+
),
|
|
94
|
+
$icon-selector: '.toggle-icon'
|
|
95
|
+
) {
|
|
96
|
+
background-color: transparent;
|
|
97
|
+
border: 0;
|
|
98
|
+
margin: 0;
|
|
99
|
+
padding: 0;
|
|
100
|
+
|
|
101
|
+
&:has(#{$icon-selector}) {
|
|
102
|
+
display: flex;
|
|
103
|
+
align-items: center;
|
|
104
|
+
|
|
105
|
+
// variant with icons as font
|
|
106
|
+
&[aria-expanded="true"] {
|
|
107
|
+
@each $icon-class, $content in $icon-mappings {
|
|
108
|
+
.#{$icon-class} {
|
|
109
|
+
&:before {
|
|
110
|
+
content: $content;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// variant with icons in a sprite
|
|
117
|
+
#{$icon-selector}-opened {
|
|
118
|
+
display: none;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
&[aria-expanded="true"] {
|
|
122
|
+
#{$icon-selector}-opened {
|
|
123
|
+
display: inline-block;
|
|
124
|
+
}
|
|
125
|
+
#{$icon-selector}-closed {
|
|
126
|
+
display: none;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
#{$icon-selector}:not(:has(svg)),
|
|
132
|
+
#{$icon-selector} svg {
|
|
133
|
+
font-size: rem-calc($icon-size);
|
|
134
|
+
width: rem-calc($icon-size);
|
|
135
|
+
color: $icon-color;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
#{$icon-selector} svg {
|
|
139
|
+
height: 100%;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Madj2kElementInViewport
|
|
3
|
+
*
|
|
4
|
+
* Adds a CSS class to any DOM element once it becomes fully (or partially) visible
|
|
5
|
+
* in the viewport using the IntersectionObserver API.
|
|
6
|
+
*
|
|
7
|
+
* - Purely CSS-triggered transitions via class
|
|
8
|
+
* - Reusable for any type of element (quotes, sections, etc.)
|
|
9
|
+
* - Fully configurable: class name, threshold, delay
|
|
10
|
+
* - Designed for CMS contexts with dynamically loaded content
|
|
11
|
+
*
|
|
12
|
+
* @author Steffen Kroggel <developer@steffenkroggel.de>
|
|
13
|
+
* @copyright 2025 Steffen Kroggel
|
|
14
|
+
* @version 1.0.0
|
|
15
|
+
* @license GNU General Public License v3.0
|
|
16
|
+
* @see https://www.gnu.org/licenses/gpl-3.0.en.html
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* // Single element with defaults
|
|
20
|
+
* const el = document.querySelector('.js-animate-in');
|
|
21
|
+
* new Madj2kElementInViewport(el);
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* // Single element with custom config
|
|
25
|
+
* new Madj2kElementInViewport(el, {
|
|
26
|
+
* visibleClass: 'is-visible',
|
|
27
|
+
* threshold: 0.75,
|
|
28
|
+
* delay: 200,
|
|
29
|
+
* debug: true
|
|
30
|
+
* });
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* // Multiple elements
|
|
34
|
+
* document.querySelectorAll('.js-element-in-viewport').forEach((el) => {
|
|
35
|
+
* new Madj2kElementInViewport(el, {
|
|
36
|
+
* visibleClass: 'is-in-viewport',
|
|
37
|
+
* threshold: 1
|
|
38
|
+
* });
|
|
39
|
+
* });
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
class Madj2kElementInViewport {
|
|
43
|
+
config = {
|
|
44
|
+
visibleClass: 'is-in-viewport',
|
|
45
|
+
threshold: 0.5,
|
|
46
|
+
delay: 0,
|
|
47
|
+
debug: false
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @param {HTMLElement} element - DOM element to observe
|
|
52
|
+
* @param {Object} config - configuration options
|
|
53
|
+
* @param {string} [config.visibleClass='in-viewport'] - class to apply when element is in view
|
|
54
|
+
* @param {number} [config.threshold=1.0] - how much of the element must be visible (0–1)
|
|
55
|
+
* @param {number} [config.delay=0] - optional delay before applying class (in ms)
|
|
56
|
+
* @param {boolean} [config.debug=false] - enable debug logs
|
|
57
|
+
*/
|
|
58
|
+
constructor(element, config = {}) {
|
|
59
|
+
if (!(element instanceof HTMLElement)) {
|
|
60
|
+
console.warn('[Madj2kElementInViewport] No valid element provided.');
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
this.element = element;
|
|
65
|
+
this.config = { ...this.config, ...config };
|
|
66
|
+
|
|
67
|
+
this._log('Initialized with config:', this.config);
|
|
68
|
+
this._observe();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Initializes the IntersectionObserver
|
|
73
|
+
* @private
|
|
74
|
+
*/
|
|
75
|
+
_observe() {
|
|
76
|
+
const observer = new IntersectionObserver(
|
|
77
|
+
([entry], observerInstance) => {
|
|
78
|
+
if (entry.isIntersecting && entry.intersectionRatio >= this.config.threshold) {
|
|
79
|
+
this._log('Element in viewport:', this.element);
|
|
80
|
+
|
|
81
|
+
if (this.config.delay > 0) {
|
|
82
|
+
setTimeout(() => this._activate(observerInstance), this.config.delay);
|
|
83
|
+
} else {
|
|
84
|
+
this._activate(observerInstance);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
threshold: this.config.threshold
|
|
90
|
+
}
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
observer.observe(this.element);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Applies the visible class and stops observing
|
|
98
|
+
* @param {IntersectionObserver} observer
|
|
99
|
+
* @private
|
|
100
|
+
*/
|
|
101
|
+
_activate(observer) {
|
|
102
|
+
this.element.classList.add(this.config.visibleClass);
|
|
103
|
+
observer.unobserve(this.element);
|
|
104
|
+
this._log(`Class "${this.config.visibleClass}" added.`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Logs debug messages
|
|
109
|
+
* @param {...any} args
|
|
110
|
+
* @private
|
|
111
|
+
*/
|
|
112
|
+
_log(...args) {
|
|
113
|
+
if (this.config.debug) {
|
|
114
|
+
console.log('[Madj2kElementInViewport]', ...args);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@forward './element-in-viewport-2.0';
|