@madj2k/fe-frontend-kit 2.0.30 → 2.0.32

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
@@ -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
@@ -12,3 +12,4 @@
12
12
  @forward 'tools/scrolling/index';
13
13
  @forward 'tools/simple-fade-slider/index';
14
14
  @forward 'tools/toggled-overlay/index';
15
+ @forward 'tools/element-in-viewport/index';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@madj2k/fe-frontend-kit",
3
- "version": "2.0.30",
3
+ "version": "2.0.32",
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
@@ -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
@@ -90,11 +90,13 @@
90
90
  $wrap-class: '.fullwidth',
91
91
  $default-padding: 16px,
92
92
  $padding-class: $page-wrap-class,
93
- $version: 1,
94
93
  ) {
95
94
 
96
95
  /** addition class which simply contains the normal padding */
97
96
  #{$padding-class}-padding {
97
+ @include page-padding-only($default-padding);
98
+ }
99
+ #{$padding-class}-padding-full {
98
100
  @include page-padding-only($default-padding, 'full');
99
101
  }
100
102
 
@@ -103,12 +105,18 @@
103
105
  @include media-breakpoint-up($breakpoint) {
104
106
  /** addition class which simply contains the normal padding */
105
107
  #{$padding-class}-padding {
108
+ @include page-padding-only($padding-x);
109
+ }
110
+ #{$padding-class}-padding-full {
106
111
  @include page-padding-only($padding-x, 'full');
107
112
  }
108
113
 
109
114
  body {
110
115
  /** addition class which simply contains the normal padding */
111
116
  #{$padding-class}-#{$breakpoint}-padding {
117
+ @include page-padding-only($padding-x);
118
+ }
119
+ #{$padding-class}-#{$breakpoint}-padding-full {
112
120
  @include page-padding-only($padding-x, 'full');
113
121
  }
114
122
  }
@@ -206,11 +214,8 @@
206
214
  /** ensure dominance with body-prefix */
207
215
  body #{$page-wrap-class} {
208
216
 
209
- // seems to be a bug in older version. But we keep it for compatibility
210
- @if ($version < 2) {
211
- padding-left: $padding-x - math.div($grid-gutter-width, 2);
212
- padding-right: $padding-x - math.div($grid-gutter-width, 2);
213
- }
217
+ padding-left: $padding-x - math.div($grid-gutter-width, 2);
218
+ padding-right: $padding-x - math.div($grid-gutter-width, 2);
214
219
 
215
220
  /** addition class which simply contains the normal padding */
216
221
  #{$padding-class}-#{$breakpoint}-padding {
@@ -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,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
+ }
@@ -0,0 +1,2 @@
1
+ import Madj2kElementInViewport from './element-in-viewport-2.0';
2
+ export { Madj2kElementInViewport };
@@ -0,0 +1 @@
1
+ @forward './element-in-viewport-2.0';