@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.
- package/README.md +509 -54
- package/package.json +30 -18
- package/src/index.js +15 -10
- package/src/modules/Application/index.js +236 -158
- package/src/modules/Breakpoints/index.js +116 -36
- package/src/modules/Cookies/index.js +95 -64
- package/src/modules/CoverOverlay/index.js +21 -14
- package/src/modules/Dataloader/index.js +71 -24
- package/src/modules/Dataloader/url-sync.js +238 -0
- package/src/modules/Dom/index.js +24 -0
- package/src/modules/DoubleHeader/index.js +571 -0
- package/src/modules/Dropdown/index.js +108 -73
- package/src/modules/EqualHeightElements/index.js +8 -8
- package/src/modules/EqualHeightImages/index.js +15 -7
- package/src/modules/FixedHeader/index.js +116 -30
- package/src/modules/FooterReveal/index.js +5 -5
- package/src/modules/HeroSlider/index.js +231 -106
- package/src/modules/HeroVideo/index.js +72 -44
- package/src/modules/Lazyload/index.js +128 -80
- package/src/modules/Lightbox/index.js +101 -80
- package/src/modules/Links/index.js +77 -51
- package/src/modules/Looper/index.js +1737 -0
- package/src/modules/Marquee/index.js +106 -37
- package/src/modules/MobileMenu/index.js +105 -130
- package/src/modules/Moonwalk/index.js +479 -153
- package/src/modules/Parallax/index.js +280 -57
- package/src/modules/Popover/index.js +187 -17
- package/src/modules/Popup/index.js +172 -53
- package/src/modules/ScrollSpy/index.js +21 -0
- package/src/modules/StackedBoxes/index.js +8 -6
- package/src/modules/StickyHeader/index.js +394 -164
- package/src/modules/Toggler/index.js +207 -11
- package/src/modules/Typography/index.js +33 -20
- package/src/utils/motion-helpers.js +330 -0
- package/types/README.md +159 -0
- package/types/events/index.d.ts +20 -0
- package/types/index.d.ts +6 -0
- package/types/modules/Application/index.d.ts +168 -0
- package/types/modules/Breakpoints/index.d.ts +40 -0
- package/types/modules/Cookies/index.d.ts +81 -0
- package/types/modules/CoverOverlay/index.d.ts +6 -0
- package/types/modules/Dataloader/index.d.ts +38 -0
- package/types/modules/Dataloader/url-sync.d.ts +36 -0
- package/types/modules/Dom/index.d.ts +47 -0
- package/types/modules/DoubleHeader/index.d.ts +63 -0
- package/types/modules/Dropdown/index.d.ts +15 -0
- package/types/modules/EqualHeightElements/index.d.ts +8 -0
- package/types/modules/EqualHeightImages/index.d.ts +11 -0
- package/types/modules/FeatureTests/index.d.ts +27 -0
- package/types/modules/FixedHeader/index.d.ts +219 -0
- package/types/modules/Fontloader/index.d.ts +5 -0
- package/types/modules/FooterReveal/index.d.ts +5 -0
- package/types/modules/HeroSlider/index.d.ts +28 -0
- package/types/modules/HeroVideo/index.d.ts +83 -0
- package/types/modules/Lazyload/index.d.ts +80 -0
- package/types/modules/Lightbox/index.d.ts +123 -0
- package/types/modules/Links/index.d.ts +55 -0
- package/types/modules/Looper/index.d.ts +127 -0
- package/types/modules/Marquee/index.d.ts +23 -0
- package/types/modules/MobileMenu/index.d.ts +63 -0
- package/types/modules/Moonwalk/index.d.ts +322 -0
- package/types/modules/Parallax/index.d.ts +71 -0
- package/types/modules/Popover/index.d.ts +29 -0
- package/types/modules/Popup/index.d.ts +76 -0
- package/types/modules/ScrollSpy/index.d.ts +29 -0
- package/types/modules/StackedBoxes/index.d.ts +9 -0
- package/types/modules/StickyHeader/index.d.ts +220 -0
- package/types/modules/Toggler/index.d.ts +48 -0
- package/types/modules/Typography/index.d.ts +77 -0
- package/types/utils/dispatchElementEvent.d.ts +1 -0
- package/types/utils/imageIsLoaded.d.ts +1 -0
- package/types/utils/imagesAreLoaded.d.ts +1 -0
- package/types/utils/loadScript.d.ts +2 -0
- package/types/utils/prefersReducedMotion.d.ts +4 -0
- package/types/utils/rafCallback.d.ts +2 -0
- 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">↓</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">↓</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">↓</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
|
-
- `
|
|
538
|
-
-
|
|
539
|
-
|
|
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
|
|
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
|
-
|
|
547
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
555
|
-
|
|
556
|
-
<
|
|
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
|
-
|
|
560
|
-
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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
|
-
##
|
|
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
|
-
-
|
|
1155
|
+
- Whether to allow element overflow to be visible
|
|
807
1156
|
|
|
1157
|
+
### Single-Element Parallax
|
|
808
1158
|
|
|
809
|
-
|
|
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:
|
|
1165
|
+
min-height: 70vh;
|
|
816
1166
|
overflow: hidden;
|
|
817
1167
|
}
|
|
818
1168
|
|
|
819
1169
|
[data-parallax-figure] {
|
|
820
1170
|
position: absolute;
|
|
821
|
-
top:
|
|
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
|
-
|
|
834
|
-
|
|
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
|
-
|
|
852
|
-
|
|
853
|
-
|
|
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
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
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
|
-
<
|
|
872
|
-
Testing some parallax :)
|
|
873
|
-
</div>
|
|
1328
|
+
<h2>Responsive Parallax</h2>
|
|
874
1329
|
</div>
|
|
875
1330
|
</section>
|
|
876
1331
|
```
|