ultimate-jekyll-manager 0.0.138 → 0.0.141

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 CHANGED
@@ -59,6 +59,14 @@ npm start -- --debug=true
59
59
  ```bash
60
60
  npm start -- --ujPluginDevMode=true
61
61
  ```
62
+ * `--profile` - Enables Jekyll build profiling to see how long each phase takes.
63
+ ```bash
64
+ npm start -- --profile
65
+ ```
66
+ * `--all-posts` - Disables the development post limit (15 posts) and builds with all posts. Useful when you need to test with full blog content.
67
+ ```bash
68
+ npm start -- --all-posts
69
+ ```
62
70
 
63
71
  ### Other ENV variables
64
72
  ```bash
@@ -164,7 +164,10 @@ $avatar-sizes: (
164
164
  md: 2.5rem,
165
165
  lg: 3.5rem,
166
166
  xl: 5rem,
167
- 2xl: 7.5rem
167
+ 2xl: 7.5rem,
168
+ 3xl: 10rem,
169
+ 4xl: 12.5rem,
170
+ 5xl: 15rem
168
171
  ) !default;
169
172
 
170
173
  // Forward Bootstrap with our configuration
@@ -14,8 +14,8 @@ window.bootstrap = bootstrap;
14
14
 
15
15
  // Import navbar scroll functionality
16
16
  import setupNavbarScroll from './js/navbar-scroll.js';
17
- // Import logo scroll functionality
18
- import setupLogoScroll from './js/logo-scroll.js';
17
+ // Import infinite scroll functionality (used by logo scroll, testimonials, etc.)
18
+ import { setupInfiniteScroll } from './js/infinite-scroll.js';
19
19
  // Import tooltip initialization
20
20
  import initializeTooltips from './js/initialize-tooltips.js';
21
21
 
@@ -23,7 +23,7 @@ import initializeTooltips from './js/initialize-tooltips.js';
23
23
  domReady().then(() => {
24
24
  // Classy Theme Initializations
25
25
  setupNavbarScroll();
26
- setupLogoScroll();
26
+ setupInfiniteScroll();
27
27
 
28
28
  // Generic Bootstrap initializations
29
29
  initializeTooltips();
@@ -29,6 +29,6 @@
29
29
  @import 'css/components/carousel';
30
30
  @import 'css/components/forms';
31
31
  @import 'css/components/links';
32
- @import 'css/components/logo-scroll';
32
+ @import 'css/components/infinite-scroll';
33
33
  @import 'css/components/spinners';
34
34
  @import 'css/components/text';
@@ -0,0 +1,140 @@
1
+ // Infinite Scroll Animation Component
2
+ // A reusable horizontal infinite scrolling container
3
+ // Used by: logo scroll, testimonial scroll, etc.
4
+
5
+ // ============================================
6
+ // Container & Track
7
+ // ============================================
8
+
9
+ .infinite-scroll-wrapper {
10
+ position: relative;
11
+ width: 100vw;
12
+ margin-left: calc(-50vw + 50%);
13
+ overflow: hidden;
14
+
15
+ // Fade edges modifier
16
+ &.infinite-scroll-fade-edges {
17
+ &::before,
18
+ &::after {
19
+ content: '';
20
+ position: absolute;
21
+ top: 0;
22
+ bottom: 0;
23
+ width: 100px;
24
+ z-index: 10;
25
+ pointer-events: none;
26
+ }
27
+
28
+ &::before {
29
+ left: 0;
30
+ background: linear-gradient(to right, var(--bs-body-bg), transparent);
31
+ }
32
+
33
+ &::after {
34
+ right: 0;
35
+ background: linear-gradient(to left, var(--bs-body-bg), transparent);
36
+ }
37
+ }
38
+ }
39
+
40
+ .infinite-scroll-track {
41
+ display: flex;
42
+ width: fit-content;
43
+ animation: infinite-scroll var(--infinite-scroll-duration, 30s) linear infinite;
44
+
45
+ &:hover {
46
+ animation-play-state: paused;
47
+ }
48
+
49
+ // Gap modifiers
50
+ &.gap-sm {
51
+ gap: 1rem;
52
+ }
53
+
54
+ &.gap-md {
55
+ gap: 1.5rem;
56
+ }
57
+
58
+ &.gap-lg {
59
+ gap: 2rem;
60
+ }
61
+ }
62
+
63
+ .infinite-scroll-item {
64
+ flex-shrink: 0;
65
+ }
66
+
67
+ // ============================================
68
+ // Animation
69
+ // ============================================
70
+
71
+ @keyframes infinite-scroll {
72
+ 0% {
73
+ transform: translateX(0);
74
+ }
75
+ 100% {
76
+ transform: translateX(var(--infinite-scroll-distance, -50%));
77
+ }
78
+ }
79
+
80
+ // ============================================
81
+ // Specific Item Types
82
+ // ============================================
83
+
84
+ // Logo items (used by trusted-by/partner sections)
85
+ .infinite-scroll-item--logo {
86
+ padding: 0 3rem;
87
+ display: flex;
88
+ align-items: center;
89
+ height: 60px;
90
+
91
+ img, svg {
92
+ height: 40px;
93
+ width: auto;
94
+ max-width: 150px;
95
+ transition: all 0.3s ease;
96
+ }
97
+ }
98
+
99
+ // Card items (used by testimonials, etc.)
100
+ .infinite-scroll-item--card {
101
+ width: 320px;
102
+ padding: 0.5rem 0;
103
+
104
+ .card {
105
+ height: 100%;
106
+ border-radius: 0.75rem;
107
+ transition: transform 0.2s ease, box-shadow 0.2s ease;
108
+
109
+ &:hover {
110
+ transform: translateY(-2px);
111
+ box-shadow: 0 0.5rem 1.5rem rgba(0, 0, 0, 0.1) !important;
112
+ }
113
+ }
114
+ }
115
+
116
+ // ============================================
117
+ // Responsive Design
118
+ // ============================================
119
+
120
+ @media (max-width: 768px) {
121
+ .infinite-scroll-wrapper.infinite-scroll-fade-edges {
122
+ &::before,
123
+ &::after {
124
+ width: 50px;
125
+ }
126
+ }
127
+
128
+ .infinite-scroll-item--logo {
129
+ padding: 0 2rem;
130
+ height: 50px;
131
+
132
+ img, svg {
133
+ height: 30px;
134
+ }
135
+ }
136
+
137
+ .infinite-scroll-item--card {
138
+ width: 280px;
139
+ }
140
+ }
@@ -0,0 +1,139 @@
1
+ // Infinite Scroll Animation Component
2
+ // A reusable horizontal infinite scrolling animation
3
+ // Used by: logo scroll, testimonial scroll, etc.
4
+
5
+ // Default scroll speed (pixels per second)
6
+ const DEFAULT_SCROLL_SPEED = 40;
7
+
8
+ /**
9
+ * Initialize infinite scroll for all matching elements
10
+ * @param {Object} options - Configuration options
11
+ * @param {string} options.selector - CSS selector for scroll tracks (default: '.infinite-scroll-track')
12
+ * @param {number} options.speed - Scroll speed in pixels per second (default: 40)
13
+ */
14
+ export function setupInfiniteScroll(options = {}) {
15
+ const {
16
+ selector = '.infinite-scroll-track',
17
+ speed = DEFAULT_SCROLL_SPEED,
18
+ } = options;
19
+
20
+ const scrollTracks = document.querySelectorAll(selector);
21
+
22
+ scrollTracks.forEach(track => {
23
+ initializeTrack(track, speed);
24
+ });
25
+
26
+ // Recalculate on window resize
27
+ setupResizeHandler(scrollTracks, speed);
28
+ }
29
+
30
+ /**
31
+ * Initialize a single scroll track
32
+ * @param {HTMLElement} track - The scroll track element
33
+ * @param {number} speed - Scroll speed in pixels per second
34
+ */
35
+ function initializeTrack(track, speed) {
36
+ if (!track) {
37
+ return;
38
+ }
39
+
40
+ // Check if already initialized
41
+ if (track.dataset.infiniteScrollInitialized === 'true') {
42
+ return;
43
+ }
44
+
45
+ // Get original items
46
+ const originalItems = Array.from(track.children);
47
+ if (originalItems.length === 0) {
48
+ return;
49
+ }
50
+
51
+ // Store original count for resize handling
52
+ track.dataset.originalItemCount = originalItems.length;
53
+ track.dataset.infiniteScrollInitialized = 'true';
54
+
55
+ // Calculate total width of original items
56
+ let totalWidth = 0;
57
+ const computedStyle = getComputedStyle(track);
58
+ const gap = parseFloat(computedStyle.gap) || 0;
59
+
60
+ originalItems.forEach((item, index) => {
61
+ totalWidth += item.getBoundingClientRect().width;
62
+ if (index < originalItems.length - 1) {
63
+ totalWidth += gap;
64
+ }
65
+ });
66
+
67
+ // Calculate how many sets we need to fill the screen plus extra for smooth scrolling
68
+ const viewportWidth = window.innerWidth;
69
+ const setsNeeded = Math.ceil((viewportWidth * 2.5) / totalWidth);
70
+
71
+ // Clone item sets
72
+ for (let i = 0; i < setsNeeded; i++) {
73
+ originalItems.forEach(item => {
74
+ const clone = item.cloneNode(true);
75
+ track.appendChild(clone);
76
+ });
77
+ }
78
+
79
+ // Calculate animation duration based on total width
80
+ const allItems = track.children;
81
+ let animationWidth = 0;
82
+
83
+ // Calculate width of half the items (for the seamless loop)
84
+ const halfCount = Math.floor(allItems.length / 2);
85
+ for (let i = 0; i < halfCount; i++) {
86
+ animationWidth += allItems[i].getBoundingClientRect().width + gap;
87
+ }
88
+
89
+ // Set CSS variables for animation
90
+ const duration = animationWidth / speed;
91
+ track.style.setProperty('--infinite-scroll-duration', `${duration}s`);
92
+ track.style.setProperty('--infinite-scroll-distance', `-${animationWidth}px`);
93
+
94
+ // Restart animation when it completes to ensure seamless loop
95
+ track.addEventListener('animationiteration', () => {
96
+ // Reset the animation to prevent accumulation of drift
97
+ track.style.animation = 'none';
98
+ track.offsetHeight; // Trigger reflow
99
+ track.style.animation = `infinite-scroll var(--infinite-scroll-duration, ${duration}s) linear infinite`;
100
+ });
101
+ }
102
+
103
+ /**
104
+ * Setup resize handler for recalculating scroll animations
105
+ * @param {NodeList} scrollTracks - All scroll track elements
106
+ * @param {number} speed - Scroll speed in pixels per second
107
+ */
108
+ function setupResizeHandler(scrollTracks, speed) {
109
+ let resizeTimeout;
110
+
111
+ window.addEventListener('resize', () => {
112
+ clearTimeout(resizeTimeout);
113
+ resizeTimeout = setTimeout(() => {
114
+ scrollTracks.forEach(track => {
115
+ // Get original item count
116
+ const originalCount = parseInt(track.dataset.originalItemCount, 10) || 0;
117
+ if (originalCount === 0) {
118
+ return;
119
+ }
120
+
121
+ // Remove cloned items
122
+ while (track.children.length > originalCount) {
123
+ track.removeChild(track.lastChild);
124
+ }
125
+
126
+ // Reset initialization flag
127
+ track.dataset.infiniteScrollInitialized = 'false';
128
+
129
+ // Re-initialize
130
+ initializeTrack(track, speed);
131
+ });
132
+ }, 250);
133
+ });
134
+ }
135
+
136
+ // Default export for backward compatibility
137
+ export default function() {
138
+ setupInfiniteScroll();
139
+ }
@@ -0,0 +1,84 @@
1
+ {% comment %}
2
+ Testimonial Scroll Component
3
+ A scrolling masonry-style testimonial display inspired by modern SaaS landing pages.
4
+ Uses the generic infinite-scroll component for animation.
5
+
6
+ Usage:
7
+ {% include themes/classy/frontend/components/testimonial-scroll.html
8
+ testimonials=page.resolved.testimonials %}
9
+
10
+ Required data structure:
11
+ testimonials:
12
+ superheadline:
13
+ icon: "megaphone"
14
+ text: "Testimonials"
15
+ headline: "What people are saying"
16
+ headline_accent: "about us"
17
+ subheadline: "Hear from real customers"
18
+ items:
19
+ - quote: "..."
20
+ author: "Name"
21
+ role: "Title"
22
+ company: "Company"
23
+ initial: "N"
24
+ handle: "@username" (optional)
25
+ {% endcomment %}
26
+
27
+ <!-- Testimonials Section -->
28
+ <section class="testimonial-scroll-section overflow-hidden">
29
+ <div class="container">
30
+ <div class="text-center mb-5" data-lazy="@class animation-slide-up">
31
+ {% iftruthy include.testimonials.superheadline.text %}
32
+ <span class="badge bg-body-tertiary border-gradient-rainbow border-1 text-body p-2 mb-1 fw-semibold small">
33
+ {% iftruthy include.testimonials.superheadline.icon %}
34
+ {% uj_icon include.testimonials.superheadline.icon, "me-1" %}
35
+ {% endiftruthy %}
36
+ {{ include.testimonials.superheadline.text }}
37
+ </span>
38
+ {% endiftruthy %}
39
+ <h2 class="h2 mb-2">
40
+ {{ include.testimonials.headline }}
41
+ {% iftruthy include.testimonials.headline_accent %}
42
+ <span class="text-accent">{{ include.testimonials.headline_accent }}</span>
43
+ {% endiftruthy %}
44
+ </h2>
45
+ {% iftruthy include.testimonials.subheadline %}
46
+ <p class="fs-5 text-muted">{{ include.testimonials.subheadline }}</p>
47
+ {% endiftruthy %}
48
+ </div>
49
+ </div>
50
+
51
+ {% if include.testimonials.items %}
52
+ <div class="infinite-scroll-wrapper infinite-scroll-fade-edges" data-lazy="@class animation-fade-in">
53
+ <div class="infinite-scroll-track gap-md" data-scroll-speed="30">
54
+ {% for testimonial in include.testimonials.items %}
55
+ <div class="infinite-scroll-item infinite-scroll-item--card">
56
+ <div class="card border-0 shadow-sm">
57
+ <div class="card-body p-4">
58
+ <!-- Header with avatar and info -->
59
+ <div class="d-flex align-items-center mb-3">
60
+ <div class="avatar avatar-md rounded-circle bg-primary text-light d-flex align-items-center justify-content-center flex-shrink-0">
61
+ <span class="fw-bold">{{ testimonial.initial }}</span>
62
+ </div>
63
+ <div class="ms-3">
64
+ <div class="fw-semibold text-body">{{ testimonial.author }}</div>
65
+ <div class="small text-muted">
66
+ {% iftruthy testimonial.handle %}
67
+ {{ testimonial.handle }}
68
+ {% endiftruthy %}
69
+ {% iffalsy testimonial.handle %}
70
+ {{ testimonial.role }}{% iftruthy testimonial.company %} at {{ testimonial.company }}{% endiftruthy %}
71
+ {% endiffalsy %}
72
+ </div>
73
+ </div>
74
+ </div>
75
+ <!-- Quote -->
76
+ <p class="mb-0 text-body">{{ testimonial.quote }}</p>
77
+ </div>
78
+ </div>
79
+ </div>
80
+ {% endfor %}
81
+ </div>
82
+ </div>
83
+ {% endif %}
84
+ </section>
@@ -50,7 +50,7 @@ hero:
50
50
 
51
51
  # Trusted By Section
52
52
  trusted_by:
53
- headline: "Trusted by the Best"
53
+ headline: "Trusted by the best"
54
54
  logos:
55
55
  # - name: "airbnb"
56
56
  - name: "amazon"
@@ -348,10 +348,10 @@ cta:
348
348
  <h3 class="fs-4">{{ page.resolved.trusted_by.headline }}</h3>
349
349
  </div>
350
350
 
351
- <div class="logo-scroll-wrapper" data-lazy="@class animation-slide-up">
352
- <div class="logo-scroll-track">
351
+ <div class="infinite-scroll-wrapper" data-lazy="@class animation-slide-up">
352
+ <div class="infinite-scroll-track">
353
353
  {% for logo in page.resolved.trusted_by.logos %}
354
- <div class="logo-item filter-adaptive hover-opacity">
354
+ <div class="infinite-scroll-item infinite-scroll-item--logo filter-adaptive hover-opacity">
355
355
  {% uj_logo logo.name, "combomarks", "original" %}
356
356
  </div>
357
357
  {% endfor %}
@@ -544,73 +544,7 @@ cta:
544
544
  </div>
545
545
  </section>
546
546
 
547
- <!-- Testimonials Section -->
548
- <section>
549
- <div class="container">
550
- <div class="text-center mb-5" data-lazy="@class animation-slide-up">
551
- {% iftruthy page.resolved.testimonials.superheadline.text %}
552
- <span class="badge bg-body-tertiary border-gradient-rainbow border-1 text-body p-2 mb-1 fw-semibold small">
553
- {% iftruthy page.resolved.testimonials.superheadline.icon %}
554
- {% uj_icon page.resolved.testimonials.superheadline.icon, "me-1" %}
555
- {% endiftruthy %}
556
- {{ page.resolved.testimonials.superheadline.text }}
557
- </span>
558
- {% endiftruthy %}
559
- <h2 class="h2 mb-2">
560
- {{ page.resolved.testimonials.headline }}
561
- {% iftruthy page.resolved.testimonials.headline_accent %}
562
- <span class="text-accent">{{ page.resolved.testimonials.headline_accent }}</span>
563
- {% endiftruthy %}
564
- </h2>
565
- {% iftruthy page.resolved.testimonials.subheadline %}
566
- <p class="fs-5 text-muted">{{ page.resolved.testimonials.subheadline }}</p>
567
- {% endiftruthy %}
568
- </div>
569
-
570
- {% if page.resolved.testimonials.items %}
571
- <div id="homeTestimonialCarousel" class="carousel slide" data-bs-ride="carousel" data-lazy="@class animation-slide-up">
572
- <div class="carousel-indicators">
573
- {% for testimonial in page.resolved.testimonials.items %}
574
- <button type="button" data-bs-target="#homeTestimonialCarousel" data-bs-slide-to="{{ forloop.index0 }}" {% if forloop.first %}class="active" aria-current="true"{% endif %} aria-label="Slide {{ forloop.index }}"></button>
575
- {% endfor %}
576
- </div>
577
-
578
- <div class="carousel-inner">
579
- {% for testimonial in page.resolved.testimonials.items %}
580
- <div class="carousel-item {% if forloop.first %}active{% endif %}">
581
- <div class="row justify-content-center">
582
- <div class="col-md-8">
583
- <div class="card border-0 shadow-sm my-1 rounded-3">
584
- <div class="card-body p-5 text-center">
585
- <div class="d-flex justify-content-center mb-4">
586
- <div class="avatar avatar-xl rounded-circle bg-primary text-light d-flex align-items-center justify-content-center">
587
- <span class="fs-2 fw-bold">{{ testimonial.initial }}</span>
588
- </div>
589
- </div>
590
- <blockquote class="mb-4">
591
- <p class="fs-4 fw-bold">"{{ testimonial.quote }}"</p>
592
- </blockquote>
593
- <p class="text-muted fs-5 mb-0">{{ testimonial.author }}, {{ testimonial.role }} at {{ testimonial.company }}</p>
594
- </div>
595
- </div>
596
- </div>
597
- </div>
598
- </div>
599
- {% endfor %}
600
- </div>
601
-
602
- <button class="carousel-control-prev" type="button" data-bs-target="#homeTestimonialCarousel" data-bs-slide="prev">
603
- <span class="carousel-control-prev-icon rounded-circle p-3" aria-hidden="true"></span>
604
- <span class="visually-hidden">Previous</span>
605
- </button>
606
- <button class="carousel-control-next" type="button" data-bs-target="#homeTestimonialCarousel" data-bs-slide="next">
607
- <span class="carousel-control-next-icon rounded-circle p-3" aria-hidden="true"></span>
608
- <span class="visually-hidden">Next</span>
609
- </button>
610
- </div>
611
- {% endif %}
612
- </div>
613
- </section>
547
+ {% include themes/classy/frontend/components/testimonial-scroll.html testimonials=page.resolved.testimonials %}
614
548
 
615
549
  <!-- Stats Section -->
616
550
  <section>
@@ -739,73 +739,7 @@ faqs:
739
739
  </div>
740
740
  </section>
741
741
 
742
- <!-- Testimonials Section -->
743
- <section>
744
- <div class="container">
745
- <div class="text-center mb-5" data-lazy="@class animation-slide-up">
746
- {% iftruthy page.resolved.testimonials.superheadline.text %}
747
- <span class="badge bg-body-tertiary border-gradient-rainbow border-1 text-body p-2 mb-1 fw-semibold small">
748
- {% iftruthy page.resolved.testimonials.superheadline.icon %}
749
- {% uj_icon page.resolved.testimonials.superheadline.icon, "me-1" %}
750
- {% endiftruthy %}
751
- {{ page.resolved.testimonials.superheadline.text }}
752
- </span>
753
- {% endiftruthy %}
754
- <h2 class="h2 mb-2">
755
- {{ page.resolved.testimonials.headline }}
756
- {% iftruthy page.resolved.testimonials.headline_accent %}
757
- <span class="text-accent">{{ page.resolved.testimonials.headline_accent }}</span>
758
- {% endiftruthy %}
759
- </h2>
760
- {% iftruthy page.resolved.testimonials.subheadline %}
761
- <p class="fs-5 text-muted">{{ page.resolved.testimonials.subheadline }}</p>
762
- {% endiftruthy %}
763
- </div>
764
-
765
- {% if page.resolved.testimonials.items %}
766
- <div id="testimonialCarousel" class="carousel slide" data-bs-ride="carousel" data-lazy="@class animation-slide-up">
767
- <div class="carousel-indicators">
768
- {% for testimonial in page.resolved.testimonials.items %}
769
- <button type="button" data-bs-target="#testimonialCarousel" data-bs-slide-to="{{ forloop.index0 }}" {% if forloop.first %}class="active" aria-current="true"{% endif %} aria-label="Slide {{ forloop.index }}"></button>
770
- {% endfor %}
771
- </div>
772
-
773
- <div class="carousel-inner">
774
- {% for testimonial in page.resolved.testimonials.items %}
775
- <div class="carousel-item {% if forloop.first %}active{% endif %}">
776
- <div class="row justify-content-center">
777
- <div class="col-md-8">
778
- <div class="card border-0 shadow-sm my-1">
779
- <div class="card-body p-5 text-center">
780
- <div class="d-flex justify-content-center mb-4">
781
- <div class="avatar avatar-xl rounded-circle bg-primary text-light d-flex align-items-center justify-content-center">
782
- <span class="fs-2 fw-bold">{{ testimonial.initial }}</span>
783
- </div>
784
- </div>
785
- <blockquote class="mb-4">
786
- <p class="fs-4 fw-bold">"{{ testimonial.quote }}"</p>
787
- </blockquote>
788
- <p class="text-muted fs-5 mb-0">{{ testimonial.author }}, {{ testimonial.role }} at {{ testimonial.company }}</p>
789
- </div>
790
- </div>
791
- </div>
792
- </div>
793
- </div>
794
- {% endfor %}
795
- </div>
796
-
797
- <button class="carousel-control-prev" type="button" data-bs-target="#testimonialCarousel" data-bs-slide="prev">
798
- <span class="carousel-control-prev-icon rounded-circle p-3" aria-hidden="true"></span>
799
- <span class="visually-hidden">Previous</span>
800
- </button>
801
- <button class="carousel-control-next" type="button" data-bs-target="#testimonialCarousel" data-bs-slide="next">
802
- <span class="carousel-control-next-icon rounded-circle p-3" aria-hidden="true"></span>
803
- <span class="visually-hidden">Next</span>
804
- </button>
805
- </div>
806
- {% endif %}
807
- </div>
808
- </section>
742
+ {% include themes/classy/frontend/components/testimonial-scroll.html testimonials=page.resolved.testimonials %}
809
743
 
810
744
  <!-- FAQ Section -->
811
745
  <section>
@@ -24,7 +24,7 @@ company_values:
24
24
  <div class="row justify-content-center text-center">
25
25
  <div class="col-lg-10">
26
26
  <h1 class="h1 mb-3">
27
- Meet Our <span class="text-gradient-rainbow">Amazing Team</span>
27
+ Meet our <span class="text-gradient-rainbow">amazing team</span>
28
28
  </h1>
29
29
  <p class="fs-5 text-muted mb-0">
30
30
  We're a diverse group of passionate individuals working together to build the future of {{ site.brand.name }}. Get to know the people behind the magic.
@@ -46,7 +46,7 @@ company_values:
46
46
  {% for member in site.team %}
47
47
  <div class="col-lg-4 col-md-6">
48
48
  <div class="card h-100 border-0 shadow-sm team-member-card">
49
- <div class="card-body p-4 text-center">
49
+ <div class="card-body p-4 text-center d-flex flex-column">
50
50
  <div class="position-relative mb-3 d-inline-block">
51
51
  <a href="{{ site.url }}{{ member.url }}" class="text-decoration-none d-block">
52
52
  <div class="avatar avatar-2xl">
@@ -61,19 +61,25 @@ company_values:
61
61
  </a>
62
62
  </h4>
63
63
  <p class="text-primary fw-semibold mb-3">{{ member.member.position }}</p>
64
- <p class="text-muted small mb-3">{{ member.member.description }}</p>
64
+ <p class="text-muted small mb-0 flex-grow-1">{{ member.member.description }}</p>
65
65
 
66
66
  {% iftruthy member.member.links %}
67
- <div class="d-flex justify-content-center gap-2">
67
+ <div class="d-flex justify-content-center gap-2 mt-3">
68
68
  {% for link in member.member.links %}
69
69
  {% assign link_icon = link.id %}
70
70
  {% if link.id == "website" %}{% assign link_icon = "globe" %}{% endif %}
71
- <a href="{{ link.url }}" class="btn btn-outline-primary btn-sm avatar avatar-md rounded-circle d-flex align-items-center justify-content-center" target="_blank" rel="nofollow noopener" title="{{ link.title }}">
71
+ <a href="{{ link.url }}" class="btn btn-outline-adaptive btn-sm avatar avatar-md rounded-circle d-flex align-items-center justify-content-center" target="_blank" rel="nofollow noopener" title="{{ link.title }}">
72
72
  {% uj_icon link_icon, "" %}
73
73
  </a>
74
74
  {% endfor %}
75
75
  </div>
76
76
  {% endiftruthy %}
77
+
78
+ <div class="mt-3">
79
+ <a href="{{ site.url }}{{ member.url }}" class="btn btn-outline-primary btn-sm">
80
+ View Profile
81
+ </a>
82
+ </div>
77
83
  </div>
78
84
  </div>
79
85
  </div>
@@ -12,7 +12,7 @@ layout: themes/[ site.theme.id ]/frontend/core/base
12
12
  <div class="col-lg-10">
13
13
  <div class="text-center">
14
14
  <!-- Member Image -->
15
- <div class="mb-4 avatar avatar-2xl">
15
+ <div class="mb-4 avatar avatar-5xl">
16
16
  {%- uj_member page.member.id, "image-tag", class="rounded-circle object-fit-cover shadow-lg team-member-image" -%}
17
17
  </div>
18
18
 
@@ -5,13 +5,17 @@
5
5
  text: '{{ site.brand.name }}',
6
6
  },
7
7
  links: [
8
+ {
9
+ label: 'Pricing',
10
+ href: '/pricing'
11
+ },
8
12
  {
9
13
  label: 'About',
10
14
  href: '/about'
11
15
  },
12
16
  {
13
- label: 'Pricing',
14
- href: '/pricing'
17
+ label: 'Contact',
18
+ href: '/contact'
15
19
  },
16
20
  {
17
21
  label: 'Explore',
@@ -24,13 +28,13 @@
24
28
  {
25
29
  label: 'Team',
26
30
  href: '/team'
27
- }
31
+ },
32
+ {
33
+ label: 'Careers',
34
+ href: '/careers'
35
+ },
28
36
  ]
29
37
  },
30
- {
31
- label: 'Contact',
32
- href: '/contact'
33
- }
34
38
  ],
35
39
  actions: [
36
40
  {
@@ -18,6 +18,9 @@ const config = Manager.getConfig('project');
18
18
  const rootPathPackage = Manager.getRootPath('main');
19
19
  const rootPathProject = Manager.getRootPath('project');
20
20
 
21
+ // Constants
22
+ const LOUD = process.env.UJ_LOUD_LOGS === 'true';
23
+
21
24
  // Load ultimate-jekyll-manager.json config
22
25
  const ujConfigPath = path.join(rootPathPackage, 'dist/defaults/config/ultimate-jekyll-manager.json');
23
26
  const ujConfig = jetpack.exists(ujConfigPath) ? JSON5.parse(jetpack.read(ujConfigPath)) : {};
@@ -465,7 +468,11 @@ function customTransform() {
465
468
 
466
469
  // Skip if instructed
467
470
  if (options.skip || (!options.overwrite && exists && !options.merge && !options.mergeLines)) {
468
- logger.log(`Skipping file: ${relativePath}`);
471
+ // Log if loud is enabled
472
+ if (LOUD) {
473
+ logger.log(`Skipping file: ${relativePath}`);
474
+ }
475
+
469
476
  return callback();
470
477
  }
471
478
 
@@ -13,6 +13,9 @@ const config = Manager.getConfig('project');
13
13
  const rootPathPackage = Manager.getRootPath('main');
14
14
  const rootPathProject = Manager.getRootPath('project');
15
15
 
16
+ // Constants
17
+ const LOUD = process.env.UJ_LOUD_LOGS === 'true';
18
+
16
19
  // Glob
17
20
  const input = [
18
21
  // Files to include
@@ -75,7 +78,9 @@ function customTransform() {
75
78
  const relativePath = path.relative(file.base, file.path).replace(/\\/g, '/');
76
79
 
77
80
  // Log
78
- logger.log(`Processing file: ${relativePath}`);
81
+ if (LOUD) {
82
+ logger.log(`Processing file: ${relativePath}`);
83
+ }
79
84
 
80
85
  // Change path if it starts with 'pages/'
81
86
  // if (relativePath.startsWith('pages/')) {
@@ -99,7 +99,9 @@ async function jekyll(complete) {
99
99
  Manager.isBuildMode() ? '' : `./node_modules/${package.name}/dist/config/_config_development.yml`,
100
100
  ].join(','),
101
101
  '--incremental',
102
- Manager.isBuildMode() ? ' --profile' : '',
102
+ (Manager.isBuildMode() || argv.profile) ? ' --profile' : '',
103
+ // Limit posts in development for faster builds (use --all-posts to disable)
104
+ (!Manager.isBuildMode() && !argv['all-posts']) ? '--limit_posts 15' : '',
103
105
  // '--disable-disk-cache',
104
106
  ]
105
107
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ultimate-jekyll-manager",
3
- "version": "0.0.138",
3
+ "version": "0.0.141",
4
4
  "description": "Ultimate Jekyll dependency manager",
5
5
  "main": "dist/index.js",
6
6
  "exports": {
@@ -97,7 +97,7 @@
97
97
  "sass": "^1.94.2",
98
98
  "spellchecker": "^3.7.1",
99
99
  "through2": "^4.0.2",
100
- "web-manager": "^4.0.31",
100
+ "web-manager": "^4.0.32",
101
101
  "webpack": "^5.103.0",
102
102
  "wonderful-fetch": "^1.3.4",
103
103
  "wonderful-version": "^1.3.2",
@@ -1,57 +0,0 @@
1
- // Logo Scroll Animation Component
2
- // Infinite scrolling logo carousel for partner/client sections
3
-
4
- // ============================================
5
- // Container & Track
6
- // ============================================
7
-
8
- .logo-scroll-wrapper {
9
- position: relative;
10
- width: 100vw;
11
- margin-left: calc(-50vw + 50%);
12
- overflow: hidden;
13
- }
14
-
15
- .logo-scroll-track {
16
- display: flex;
17
- width: fit-content;
18
- animation: scroll-logos var(--logo-scroll-duration, 30s) linear infinite;
19
- }
20
-
21
- .logo-item {
22
- padding: 0 3rem;
23
- display: flex;
24
- align-items: center;
25
- height: 60px;
26
- flex-shrink: 0;
27
-
28
- img, svg {
29
- height: 40px;
30
- width: auto;
31
- max-width: 150px;
32
- transition: all 0.3s ease;
33
- }
34
- }
35
-
36
- @keyframes scroll-logos {
37
- 0% {
38
- transform: translateX(0);
39
- }
40
- 100% {
41
- transform: translateX(var(--logo-scroll-distance, -50%));
42
- }
43
- }
44
-
45
- // ============================================
46
- // Responsive Design
47
- // ============================================
48
- @media (max-width: 768px) {
49
- .logo-item {
50
- padding: 0 2rem;
51
- height: 50px;
52
-
53
- img, svg {
54
- height: 30px;
55
- }
56
- }
57
- }
@@ -1,83 +0,0 @@
1
- // Setup speed
2
- const SCROLL_SPEED_PX_PER_SECOND = 40; // Pixels per second
3
-
4
- // Logo scroll animation setup
5
- export default function setupLogoScroll() {
6
- const scrollTracks = document.querySelectorAll('.logo-scroll-track');
7
-
8
- scrollTracks.forEach(track => {
9
- if (!track) {
10
- return;
11
- }
12
-
13
- // Get original logos
14
- const originalLogos = Array.from(track.children);
15
- if (originalLogos.length === 0) {
16
- return;
17
- }
18
-
19
- // Calculate total width of original logos
20
- let totalWidth = 0;
21
- originalLogos.forEach(logo => {
22
- totalWidth += logo.getBoundingClientRect().width;
23
- });
24
-
25
- // Calculate how many sets we need to fill the screen plus extra for smooth scrolling
26
- const viewportWidth = window.innerWidth;
27
- const setsNeeded = Math.ceil((viewportWidth * 2.5) / totalWidth);
28
-
29
- // Clone logo sets
30
- for (let i = 0; i < setsNeeded; i++) {
31
- originalLogos.forEach(logo => {
32
- const clone = logo.cloneNode(true);
33
- track.appendChild(clone);
34
- });
35
- }
36
-
37
- // Calculate animation duration based on total width
38
- // Slower speed for better visibility
39
- const allLogos = track.children;
40
- let animationWidth = 0;
41
-
42
- // Calculate width of half the logos (for the 50% translation)
43
- const halfCount = Math.floor(allLogos.length / 2);
44
- for (let i = 0; i < halfCount; i++) {
45
- animationWidth += allLogos[i].getBoundingClientRect().width;
46
- }
47
-
48
- // Set CSS variables for animation
49
- const duration = animationWidth / SCROLL_SPEED_PX_PER_SECOND;
50
- track.style.setProperty('--logo-scroll-duration', `${duration}s`);
51
- track.style.setProperty('--logo-scroll-distance', `-${animationWidth}px`);
52
-
53
- // Restart animation when it completes to ensure seamless loop
54
- track.addEventListener('animationiteration', () => {
55
- // Reset the animation to prevent accumulation of drift
56
- track.style.animation = 'none';
57
- track.offsetHeight; // Trigger reflow
58
- track.style.animation = `scroll-logos var(--logo-scroll-duration, ${duration}s) linear infinite`;
59
- });
60
- });
61
-
62
- // Recalculate on window resize
63
- let resizeTimeout;
64
- window.addEventListener('resize', () => {
65
- clearTimeout(resizeTimeout);
66
- resizeTimeout = setTimeout(() => {
67
- // Reset and recalculate
68
- scrollTracks.forEach(track => {
69
- // Remove cloned logos
70
- const logos = Array.from(track.children);
71
- const originalCount = logos.length / (Math.ceil(logos.length / 8)); // Estimate original count
72
-
73
- // Keep only estimated original logos
74
- while (track.children.length > 8) { // Assuming max 8 original logos
75
- track.removeChild(track.lastChild);
76
- }
77
- });
78
-
79
- // Re-run setup
80
- setupLogoScroll();
81
- }, 250);
82
- });
83
- }