@brandocms/jupiter 3.55.0 → 4.0.0-beta.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.
Files changed (76) hide show
  1. package/README.md +509 -54
  2. package/package.json +30 -18
  3. package/src/index.js +15 -10
  4. package/src/modules/Application/index.js +236 -158
  5. package/src/modules/Breakpoints/index.js +116 -36
  6. package/src/modules/Cookies/index.js +95 -64
  7. package/src/modules/CoverOverlay/index.js +21 -14
  8. package/src/modules/Dataloader/index.js +71 -24
  9. package/src/modules/Dataloader/url-sync.js +238 -0
  10. package/src/modules/Dom/index.js +24 -0
  11. package/src/modules/DoubleHeader/index.js +571 -0
  12. package/src/modules/Dropdown/index.js +108 -73
  13. package/src/modules/EqualHeightElements/index.js +8 -8
  14. package/src/modules/EqualHeightImages/index.js +15 -7
  15. package/src/modules/FixedHeader/index.js +116 -30
  16. package/src/modules/FooterReveal/index.js +5 -5
  17. package/src/modules/HeroSlider/index.js +231 -106
  18. package/src/modules/HeroVideo/index.js +72 -44
  19. package/src/modules/Lazyload/index.js +128 -80
  20. package/src/modules/Lightbox/index.js +101 -80
  21. package/src/modules/Links/index.js +77 -51
  22. package/src/modules/Looper/index.js +1737 -0
  23. package/src/modules/Marquee/index.js +106 -37
  24. package/src/modules/MobileMenu/index.js +105 -130
  25. package/src/modules/Moonwalk/index.js +479 -153
  26. package/src/modules/Parallax/index.js +280 -57
  27. package/src/modules/Popover/index.js +187 -17
  28. package/src/modules/Popup/index.js +172 -53
  29. package/src/modules/ScrollSpy/index.js +21 -0
  30. package/src/modules/StackedBoxes/index.js +8 -6
  31. package/src/modules/StickyHeader/index.js +394 -164
  32. package/src/modules/Toggler/index.js +207 -11
  33. package/src/modules/Typography/index.js +33 -20
  34. package/src/utils/motion-helpers.js +330 -0
  35. package/types/README.md +159 -0
  36. package/types/events/index.d.ts +20 -0
  37. package/types/index.d.ts +6 -0
  38. package/types/modules/Application/index.d.ts +168 -0
  39. package/types/modules/Breakpoints/index.d.ts +40 -0
  40. package/types/modules/Cookies/index.d.ts +81 -0
  41. package/types/modules/CoverOverlay/index.d.ts +6 -0
  42. package/types/modules/Dataloader/index.d.ts +38 -0
  43. package/types/modules/Dataloader/url-sync.d.ts +36 -0
  44. package/types/modules/Dom/index.d.ts +47 -0
  45. package/types/modules/DoubleHeader/index.d.ts +63 -0
  46. package/types/modules/Dropdown/index.d.ts +15 -0
  47. package/types/modules/EqualHeightElements/index.d.ts +8 -0
  48. package/types/modules/EqualHeightImages/index.d.ts +11 -0
  49. package/types/modules/FeatureTests/index.d.ts +27 -0
  50. package/types/modules/FixedHeader/index.d.ts +219 -0
  51. package/types/modules/Fontloader/index.d.ts +5 -0
  52. package/types/modules/FooterReveal/index.d.ts +5 -0
  53. package/types/modules/HeroSlider/index.d.ts +28 -0
  54. package/types/modules/HeroVideo/index.d.ts +83 -0
  55. package/types/modules/Lazyload/index.d.ts +80 -0
  56. package/types/modules/Lightbox/index.d.ts +123 -0
  57. package/types/modules/Links/index.d.ts +55 -0
  58. package/types/modules/Looper/index.d.ts +127 -0
  59. package/types/modules/Marquee/index.d.ts +23 -0
  60. package/types/modules/MobileMenu/index.d.ts +63 -0
  61. package/types/modules/Moonwalk/index.d.ts +322 -0
  62. package/types/modules/Parallax/index.d.ts +71 -0
  63. package/types/modules/Popover/index.d.ts +29 -0
  64. package/types/modules/Popup/index.d.ts +76 -0
  65. package/types/modules/ScrollSpy/index.d.ts +29 -0
  66. package/types/modules/StackedBoxes/index.d.ts +9 -0
  67. package/types/modules/StickyHeader/index.d.ts +220 -0
  68. package/types/modules/Toggler/index.d.ts +48 -0
  69. package/types/modules/Typography/index.d.ts +77 -0
  70. package/types/utils/dispatchElementEvent.d.ts +1 -0
  71. package/types/utils/imageIsLoaded.d.ts +1 -0
  72. package/types/utils/imagesAreLoaded.d.ts +1 -0
  73. package/types/utils/loadScript.d.ts +2 -0
  74. package/types/utils/prefersReducedMotion.d.ts +4 -0
  75. package/types/utils/rafCallback.d.ts +2 -0
  76. package/types/utils/zoom.d.ts +4 -0
package/README.md CHANGED
@@ -237,7 +237,6 @@ include images from this section. Otherwise, all lightboxed images will be inclu
237
237
 
238
238
  - `trigger` - `false` - selector representing an element you want to use as a trigger to open lightbox
239
239
  - `captions` - `false` - whether to show captions or not in the overlay
240
- - `swipe` - `true` – if swipe is true, native zoom won't work, so allow choosing
241
240
  - `elements` - `object` - switch out default elements in the overlay
242
241
  - `arrowRight` - `function` - returns an element
243
242
  - `arrowLeft` - `function` - returns an element
@@ -277,6 +276,33 @@ HTML
277
276
  </div>
278
277
  ```
279
278
 
279
+ #### Group Functionality
280
+
281
+ You can group togglers together to create an accordion-like behavior where only one toggler in the group can be open at a time. When you open one toggler, all others in the same group will close automatically.
282
+
283
+ ```html
284
+ <div data-toggle data-toggle-group="phases">
285
+ <button data-toggle-trigger="phase1">Phase 1 <span class="arrow icon">&darr;</span></button>
286
+ <div class="panel" data-toggle-content="phase1">
287
+ Phase 1 content
288
+ </div>
289
+ </div>
290
+
291
+ <div data-toggle data-toggle-group="phases">
292
+ <button data-toggle-trigger="phase2">Phase 2 <span class="arrow icon">&darr;</span></button>
293
+ <div class="panel" data-toggle-content="phase2">
294
+ Phase 2 content
295
+ </div>
296
+ </div>
297
+
298
+ <div data-toggle data-toggle-group="phases">
299
+ <button data-toggle-trigger="phase3">Phase 3 <span class="arrow icon">&darr;</span></button>
300
+ <div class="panel" data-toggle-content="phase3">
301
+ Phase 3 content
302
+ </div>
303
+ </div>
304
+ ```
305
+
280
306
  CSS
281
307
  ```css
282
308
  [data-toggle-trigger] {
@@ -290,6 +316,14 @@ CSS
290
316
  }
291
317
  }
292
318
 
319
+ /* Style active trigger elements */
320
+ [data-toggle-trigger][data-toggle-trigger-active] {
321
+ background-color: #e8f4ff;
322
+ border-color: #4a90e2;
323
+ color: #0056b3;
324
+ font-weight: bold;
325
+ }
326
+
293
327
  [data-toggle-content] {
294
328
  height: 0;
295
329
  overflow: hidden;
@@ -313,6 +347,136 @@ togglers.forEach(toggleEl => {
313
347
  #### Options
314
348
 
315
349
 
350
+ ## Dataloader
351
+
352
+ A component for dynamically loading and filtering content via AJAX with optional URL synchronization.
353
+
354
+ ### Basic Usage
355
+
356
+ HTML:
357
+ ```html
358
+ <div data-loader="/api/articles" data-loader-id="articles">
359
+ <div class="filters">
360
+ <a href="#" data-loader-param="all" data-loader-param-selected>All</a>
361
+ <a href="#" data-loader-param="tech" data-loader-param-key="category">Technology</a>
362
+ <a href="#" data-loader-param="design" data-loader-param-key="category">Design</a>
363
+ </div>
364
+
365
+ <div data-loader-canvas>
366
+ <!-- Content will be loaded here -->
367
+ </div>
368
+
369
+ <button data-loader-more>Load More</button>
370
+ </div>
371
+ ```
372
+
373
+ JavaScript:
374
+ ```js
375
+ import { Dataloader } from '@brandocms/jupiter'
376
+
377
+ const dataloader = new Dataloader(app, $loaderEl, {
378
+ onFetch: (dataloader) => {
379
+ // Initialize components after content loads
380
+ const mw = new Moonwalk(app, {}, dataloader.$canvasEl)
381
+ new Lazyload(app, {}, dataloader.$canvasEl)
382
+ mw.ready()
383
+ }
384
+ })
385
+ ```
386
+
387
+ ### URL Synchronization
388
+
389
+ Enable bidirectional sync between dataloader parameters and browser URL:
390
+
391
+ ```js
392
+ // Configure URL patterns for different dataloaders
393
+ const urlConfigs = {
394
+ events: {
395
+ templates: {
396
+ en: '/events/:location/:type',
397
+ no: '/arrangementer/:location/:type'
398
+ },
399
+ updateOnInit: false // Don't update URL on initialization
400
+ },
401
+ news: {
402
+ templates: {
403
+ en: '/news/:category/:year',
404
+ no: '/nyheter/:category/:year'
405
+ }
406
+ }
407
+ }
408
+
409
+ // Initialize all dataloaders with URL sync
410
+ app.dataloaders = []
411
+ Dom.all('[data-loader]').forEach($dl => {
412
+ app.dataloaders.push(
413
+ new Dataloader(app, $dl, {
414
+ urlSync: urlConfigs,
415
+ onFetch: (dataloader) => {
416
+ // Your onFetch logic
417
+ }
418
+ })
419
+ )
420
+ })
421
+ ```
422
+
423
+ ### Split Layout
424
+
425
+ Components can be placed outside the main dataloader element:
426
+
427
+ ```html
428
+ <!-- Sidebar -->
429
+ <div data-loader="/api/articles" data-loader-id="articles">
430
+ <a href="#" data-loader-param="tech" data-loader-param-key="category">Tech</a>
431
+ </div>
432
+
433
+ <!-- Main content area -->
434
+ <div data-loader-canvas-for="articles">
435
+ <!-- Content loads here -->
436
+ </div>
437
+
438
+ <!-- Footer -->
439
+ <button data-loader-more-for="articles">Load More</button>
440
+ <input data-loader-filter-for="articles" placeholder="Search...">
441
+ ```
442
+
443
+ ### Options
444
+
445
+ - `page` - `number` - Initial page number (default: 0)
446
+ - `loaderParam` - `object` - Initial parameters
447
+ - `filter` - `string` - Initial filter value
448
+ - `onFetch` - `function` - Called after content is fetched
449
+ - `urlSync` - `object` - URL synchronization configuration:
450
+ - `templates` - Language-specific URL templates with `:param` placeholders
451
+ - `updateOnInit` - `boolean` - Update URL on initialization (default: true)
452
+ - `languageInPath` - `boolean` - Include language in URL path
453
+ - `hideDefaultLanguage` - `boolean` - Hide default language from URL (default: true)
454
+ - `defaultLanguage` - `string` - Default language code (default: 'en')
455
+ - `buildUrl` - `function` - Custom URL building function
456
+ - `parseUrl` - `function` - Custom URL parsing function
457
+
458
+ ### Attributes
459
+
460
+ - `data-loader="/api/endpoint"` - Main container with API endpoint
461
+ - `data-loader-id="unique-id"` - Unique identifier for the dataloader
462
+ - `data-loader-canvas` - Container where content will be loaded
463
+ - `data-loader-canvas-for="id"` - Canvas for specific dataloader (split layout)
464
+ - `data-loader-param="value"` - Parameter value to send to API
465
+ - `data-loader-param-key="key"` - Parameter key (default: 'defaultParam')
466
+ - `data-loader-param-multi` - Allow multiple selections for this parameter
467
+ - `data-loader-param-selected` - Marks parameter as selected
468
+ - `data-loader-more` - Load more button
469
+ - `data-loader-more-for="id"` - Load more for specific dataloader (split layout)
470
+ - `data-loader-filter` - Filter input field
471
+ - `data-loader-filter-for="id"` - Filter for specific dataloader (split layout)
472
+ - `data-loader-loading` - Added during loading
473
+ - `data-loader-starved` - Added to load more button when no more content
474
+
475
+ ### API Response Headers
476
+
477
+ - `jpt-dataloader: starved` - Set this header when there's no more content to load
478
+
479
+
316
480
  ## Links
317
481
 
318
482
  #### Options
@@ -532,36 +696,161 @@ Paragraph one and two will then get a `data-moonwalk="slide"` attribute.
532
696
 
533
697
  ## Popup
534
698
 
699
+ The Popup module allows you to create modal dialogs that appear when triggered by a button click.
700
+
535
701
  ### Options
536
702
 
537
- - `tweenIn: (el, popup) => {}`
538
- - Function that gets called to tween popup + background in.
539
- - `el` is the popup element itself, while `popup` is the `Popup()` class.
703
+ - `selector` - default `[data-popup]`
704
+ - CSS selector to find popup elements
705
+
706
+ - `responsive: (app) => boolean` - default `() => true`
707
+ - Function to determine if popup should be shown on current breakpoint
708
+
709
+ - `onOpen: (trigger, target, popup) => {}`
710
+ - Function called when popup opens
711
+ - `trigger` is the element that triggered the popup
712
+ - `target` is the popup element
713
+ - `popup` is the Popup instance
714
+
715
+ - `onClose: (popup) => {}`
716
+ - Function called when popup closes
717
+ - `popup` is the Popup instance
718
+
719
+ - `tweenIn: (trigger, target, popup) => {}`
720
+ - Function for animating the popup opening
721
+ - `trigger` is the element that triggered the popup
722
+ - `target` is the popup element
723
+ - `popup` is the Popup instance
540
724
  - Backdrop can be accessed as `popup.backdrop`
541
725
 
542
726
  - `tweenOut: (popup) => {}`
543
- - Function that gets called to tween popup + background out
727
+ - Function for animating the popup closing
728
+ - `popup` is the Popup instance
729
+ - Popup element can be accessed as `popup.currentPopup`
544
730
  - Backdrop can be accessed as `popup.backdrop`
545
731
 
546
- - `onClose: (popup) => {}`
547
- - Function that gets called right before `popup.close`
732
+ ### Basic Usage
733
+
734
+ HTML:
735
+
736
+ ```html
737
+ <div id="my-popup" data-popup>
738
+ <div class="popup-header">
739
+ <h3>My Popup</h3>
740
+ <button class="close-button" data-popup-close>×</button>
741
+ </div>
742
+ <div class="popup-content">
743
+ <p>This is a popup that appears when the trigger button is clicked.</p>
744
+ </div>
745
+ </div>
746
+
747
+ <button data-popup-trigger="#my-popup">Open Popup</button>
748
+ ```
548
749
 
549
- ### Example
750
+ JavaScript:
751
+
752
+ ```js
753
+ import { Application, Popup } from '@brandocms/jupiter'
754
+
755
+ const app = new Application()
756
+ const popup = new Popup(app)
757
+ ```
550
758
 
551
- Example HTML
759
+ ### Advanced Usage with Multiple Popups
760
+
761
+ You can create multiple independent popups by using the key-based system. This ensures that each popup operates independently, with its own backdrop and close behavior.
762
+
763
+ HTML:
552
764
 
553
765
  ```html
554
- <div class="newsletter-popup" data-popup>
555
- ...
556
- <button data-popup-close>
766
+ <!-- First popup with key "newsletter" -->
767
+ <div id="newsletter-popup" data-popup data-popup-key="newsletter">
768
+ <div class="popup-header">
769
+ <h3>Newsletter Signup</h3>
770
+ <button class="close-button" data-popup-close>×</button>
771
+ </div>
772
+ <div class="popup-content">
773
+ <p>Sign up for our newsletter!</p>
774
+ </div>
557
775
  </div>
558
776
 
559
- <button data-popup-trigger=".newsletter-popup">
560
- Open popup
777
+ <!-- Second popup with key "login" -->
778
+ <div id="login-popup" data-popup data-popup-key="login">
779
+ <div class="popup-header">
780
+ <h3>Login</h3>
781
+ <button class="close-button" data-popup-close>×</button>
782
+ </div>
783
+ <div class="popup-content">
784
+ <p>Enter your credentials to log in.</p>
785
+ </div>
786
+ </div>
787
+
788
+ <!-- Triggers for each popup with corresponding keys -->
789
+ <button data-popup-trigger="#newsletter-popup" data-popup-key="newsletter">
790
+ Subscribe to Newsletter
791
+ </button>
792
+
793
+ <button data-popup-trigger="#login-popup" data-popup-key="login">
794
+ Login
561
795
  </button>
562
796
  ```
563
797
 
564
- Example CSS (PCSS)
798
+ JavaScript:
799
+
800
+ ```js
801
+ import { Application, Popup } from '@brandocms/jupiter'
802
+
803
+ const app = new Application()
804
+
805
+ // Create separate instances for different types of popups
806
+ const newsletterPopup = new Popup(app, '[data-popup][data-popup-key="newsletter"]', {
807
+ onOpen: (trigger, target, popup) => {
808
+ console.log('Newsletter popup opened')
809
+ }
810
+ })
811
+
812
+ const loginPopup = new Popup(app, '[data-popup][data-popup-key="login"]', {
813
+ onOpen: (trigger, target, popup) => {
814
+ console.log('Login popup opened')
815
+ }
816
+ })
817
+ ```
818
+
819
+ ### Custom Animations
820
+
821
+ You can customize the animation for specific popups:
822
+
823
+ ```js
824
+ const customPopup = new Popup(app, '[data-popup][data-popup-key="custom"]', {
825
+ tweenIn: (trigger, target, popup) => {
826
+ // Set backdrop visible
827
+ gsap.set(popup.backdrop, { display: 'block' })
828
+ gsap.to(popup.backdrop, {
829
+ duration: 0.3,
830
+ opacity: 1,
831
+ onComplete: () => {
832
+ // Bounce in animation for popup
833
+ gsap.fromTo(
834
+ target,
835
+ {
836
+ scale: 0.5,
837
+ opacity: 0,
838
+ display: 'block'
839
+ },
840
+ {
841
+ duration: 0.5,
842
+ scale: 1,
843
+ opacity: 1,
844
+ ease: 'back.out(1.7)'
845
+ }
846
+ )
847
+ }
848
+ })
849
+ }
850
+ })
851
+ ```
852
+
853
+ ### CSS Styling
565
854
 
566
855
  ```scss
567
856
  [data-popup] {
@@ -576,9 +865,12 @@ Example CSS (PCSS)
576
865
  text-align: center;
577
866
  display: none;
578
867
  opacity: 0;
579
-
868
+ transform: translate(-50%, -50%);
869
+ border-radius: 6px;
870
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
871
+
580
872
  @responsive mobile {
581
- width: 80%;
873
+ width: 90%;
582
874
  }
583
875
  }
584
876
 
@@ -586,13 +878,23 @@ Example CSS (PCSS)
586
878
  z-index: 4999;
587
879
  display: none;
588
880
  opacity: 0;
589
- background-color: theme(colors.blue.100);
881
+ background-color: rgba(0, 0, 0, 0.5);
590
882
  position: fixed;
591
883
  top: 0;
592
884
  left: 0;
885
+ right: 0;
886
+ bottom: 0;
593
887
  height: 100%;
594
888
  width: 100%;
595
889
  }
890
+
891
+ .close-button {
892
+ background: none;
893
+ border: none;
894
+ font-size: 20px;
895
+ cursor: pointer;
896
+ color: #333;
897
+ }
596
898
  ```
597
899
 
598
900
 
@@ -614,7 +916,15 @@ Example CSS (PCSS)
614
916
  </div>
615
917
  ```
616
918
 
617
- ## StickyHeader
919
+ ## DoubleHeader
920
+
921
+ A dual-header module that clones the original header element. The clone stays fixed and
922
+ hides when scrolling down, revealing on scroll up. Uses IntersectionObserver to detect
923
+ when the original header is visible.
924
+
925
+ > **Note:** This module was previously named `StickyHeader`. It was renamed to `DoubleHeader`
926
+ > to better reflect its dual-header/clone architecture. The new `StickyHeader` module uses
927
+ > CSS `position: sticky` instead.
618
928
 
619
929
  * header element should not have position: fixed
620
930
 
@@ -647,6 +957,23 @@ Example CSS (PCSS)
647
957
  before tweening into view
648
958
 
649
959
 
960
+ ## StickyHeader
961
+
962
+ A header that uses `position: sticky`. Hides when scrolling down and is revealed on scrolling up.
963
+ Unlike FixedHeader, the sticky header stays in document flow - when hidden via transform,
964
+ its space is still reserved.
965
+
966
+ > **Note:** This is a new module. If you were using the old `StickyHeader` (which cloned the header),
967
+ > you should now use `DoubleHeader` instead.
968
+
969
+ * header element needs `position: sticky; top: 0;`
970
+ * No padding-top needed on content below (header stays in document flow)
971
+
972
+ ### Options
973
+
974
+ Same options as FixedHeader - see FixedHeader documentation below.
975
+
976
+
650
977
  ## FixedHeader
651
978
 
652
979
  * header element needs position: fixed;
@@ -796,81 +1123,209 @@ Hero example:
796
1123
 
797
1124
  ## Parallax
798
1125
 
1126
+ Smooth parallax scrolling effect for images and elements, inspired by SimpleParallax.js.
1127
+
799
1128
  ### Options
800
1129
 
801
1130
  - `el`
802
1131
  - default `[data-parallax]`
1132
+ - Can also be `[data-parallax-parent]` for multi-element parallax
1133
+
1134
+ - `factor`
1135
+ - default `1.3`
1136
+ - Controls the speed of the parallax effect (higher = more movement)
1137
+ - Can be overridden per-element with `data-parallax-factor` attribute
803
1138
 
804
1139
  - `fadeContent`
1140
+ - default `true`
1141
+ - If true, fades out content as it leaves the viewport
1142
+ - For multi-element parallax, can be set per-element with `data-parallax-fade` attribute
1143
+
1144
+ - `scale`
1145
+ - default `1.2`
1146
+ - Scale factor to apply to parallax background images to prevent gaps during movement
1147
+
1148
+ - `orientation`
1149
+ - default `'up'`
1150
+ - Direction of parallax movement: `'up'`, `'down'`, `'left'`, or `'right'`
1151
+ - Can be overridden per-element with `data-parallax-orientation` attribute
1152
+
1153
+ - `overflow`
805
1154
  - default `false`
806
- - If true, fades out `[data-parallax-content]` as we move towards bottom of parallaxed element
1155
+ - Whether to allow element overflow to be visible
807
1156
 
1157
+ ### Single-Element Parallax
808
1158
 
809
- Example:
1159
+ For traditional parallax effects with a background and content:
810
1160
 
811
1161
  ```html
812
1162
  <style>
813
1163
  [data-parallax] {
814
1164
  position: relative;
815
- min-height: 100vh;
1165
+ min-height: 70vh;
816
1166
  overflow: hidden;
817
1167
  }
818
1168
 
819
1169
  [data-parallax-figure] {
820
1170
  position: absolute;
821
- top: 0;
1171
+ top: -20%;
822
1172
  left: 0;
823
- height: 100%;
824
- width: 100%;
825
- max-height: 100%;
826
- }
827
-
828
- [data-parallax-figure] picture {
829
- height: 100%;
830
1173
  width: 100%;
831
- }
832
-
833
- [data-parallax-figure] picture img {
834
- min-height: 100%;
835
- min-width: 100%;
836
- max-height: 100%;
837
- object-fit: cover;
1174
+ height: 140%;
1175
+ background-size: cover;
1176
+ background-position: center;
1177
+ will-change: transform;
838
1178
  }
839
1179
 
840
1180
  [data-parallax-content] {
841
1181
  position: absolute;
842
1182
  top: 0;
843
1183
  left: 0;
844
- height: 100%;
845
1184
  width: 100%;
1185
+ height: 100%;
846
1186
  display: flex;
1187
+ flex-direction: column;
847
1188
  justify-content: center;
848
1189
  align-items: center;
1190
+ color: white;
1191
+ text-align: center;
1192
+ background-color: rgba(0, 0, 0, 0.3);
1193
+ z-index: 1;
1194
+ will-change: transform, opacity;
849
1195
  }
1196
+ </style>
850
1197
 
851
- [data-parallax-content] div {
852
- color: #ffffff;
853
- font-size: 4rem;
1198
+ <section data-parallax>
1199
+ <div
1200
+ data-parallax-figure
1201
+ style="background-image: url('/path/to/image.jpg');"
1202
+ ></div>
1203
+ <div data-parallax-content>
1204
+ <h2>Parallax Title</h2>
1205
+ <p>Parallax content with automatic fade effect</p>
1206
+ </div>
1207
+ </section>
1208
+ ```
1209
+
1210
+ ### Multi-Element Parallax
1211
+
1212
+ For creating parallax effects with multiple elements, each with their own movement speed and fade settings:
1213
+
1214
+ ```html
1215
+ <style>
1216
+ [data-parallax-parent] {
1217
+ position: relative;
1218
+ height: 80vh;
1219
+ overflow: hidden;
1220
+ }
1221
+
1222
+ .parallax-element {
1223
+ position: absolute;
1224
+ width: 200px;
1225
+ height: 200px;
1226
+ display: flex;
1227
+ align-items: center;
1228
+ justify-content: center;
1229
+ color: white;
1230
+ font-weight: bold;
1231
+ text-align: center;
1232
+ border-radius: 10px;
1233
+ will-change: transform, opacity;
1234
+ }
1235
+
1236
+ /* Position elements as needed */
1237
+ .element-1 {
1238
+ background-color: #e74c3c;
1239
+ left: 20%;
1240
+ top: 30%;
1241
+ }
1242
+
1243
+ .element-2 {
1244
+ background-color: #3498db;
1245
+ left: 50%;
1246
+ top: 40%;
1247
+ }
1248
+
1249
+ .element-3 {
1250
+ background-color: #2ecc71;
1251
+ left: 70%;
1252
+ top: 50%;
854
1253
  }
855
1254
  </style>
856
1255
 
1256
+ <div data-parallax-parent>
1257
+ <!-- Slow element moving down -->
1258
+ <div
1259
+ class="parallax-element element-1"
1260
+ data-parallax-factor="0.8"
1261
+ data-parallax-orientation="down"
1262
+ >
1263
+ Slow downward movement
1264
+ </div>
1265
+
1266
+ <!-- Medium element with fade effect -->
1267
+ <div
1268
+ class="parallax-element element-2"
1269
+ data-parallax-factor="1.5"
1270
+ data-parallax-fade
1271
+ >
1272
+ Medium upward movement with fade
1273
+ </div>
1274
+
1275
+ <!-- Fast element without fade moving left -->
1276
+ <div
1277
+ class="parallax-element element-3"
1278
+ data-parallax-factor="2.5"
1279
+ data-parallax-orientation="left"
1280
+ >
1281
+ Fast leftward movement
1282
+ </div>
1283
+ </div>
1284
+ ```
1285
+
1286
+ ### Initialize in JS
1287
+
1288
+ ```js
1289
+ import { Application, Parallax } from '@brandocms/jupiter'
1290
+
1291
+ const app = new Application()
1292
+
1293
+ // Traditional parallax with background image and content
1294
+ const singleParallax = new Parallax(app, {
1295
+ // Default options
1296
+ factor: 1.3,
1297
+ fadeContent: true,
1298
+ scale: 1.2
1299
+ })
1300
+
1301
+ // Multi-element parallax
1302
+ const multiParallax = new Parallax(app, {
1303
+ el: '[data-parallax-parent]',
1304
+ orientation: 'up' // Default movement direction
1305
+ })
1306
+
1307
+ // Cleanup when needed
1308
+ function cleanup() {
1309
+ singleParallax.destroy()
1310
+ multiParallax.destroy()
1311
+ }
1312
+ ```
1313
+
1314
+ ### Using with picture elements
1315
+
1316
+ For using parallax with responsive images:
1317
+
1318
+ ```html
857
1319
  <section data-parallax>
858
1320
  <div data-parallax-figure>
859
- <%= picture_tag(
860
- work.cover,
861
- placeholder: false,
862
- key: :original,
863
- lazyload: true,
864
- srcset: {Kunstnerforbundet.Artists.Work, :cover},
865
- prefix: media_url(),
866
- img_class: "img-fluid",
867
- alt: "#{work.title} (#{work.year}) - #{work.size} - #{work.technique}")
868
- %>
1321
+ <picture>
1322
+ <source media="(min-width: 1200px)" srcset="/images/large.jpg">
1323
+ <source media="(min-width: 768px)" srcset="/images/medium.jpg">
1324
+ <img src="/images/small.jpg" alt="Parallax image">
1325
+ </picture>
869
1326
  </div>
870
1327
  <div data-parallax-content>
871
- <div>
872
- Testing some parallax :)
873
- </div>
1328
+ <h2>Responsive Parallax</h2>
874
1329
  </div>
875
1330
  </section>
876
1331
  ```