diff --git a/src/app/globals.scss b/src/app/globals.scss
index 66b5371..60e2a0c 100644
--- a/src/app/globals.scss
+++ b/src/app/globals.scss
@@ -21,7 +21,7 @@
--secondary-muted: 242 76% 90%;
--secondary-foreground: 222.2 47.4% 11.2%;
- --muted: 225 100% 98%;
+ --muted: 207 28% 92%;
--dimmed: 246 10% 37%;
--muted-foreground: 215.4 16.3% 46.9%;
diff --git a/src/app/services/_utils/marquee-wrapper.tsx b/src/app/services/_utils/marquee-wrapper.tsx
new file mode 100644
index 0000000..ad0c90e
--- /dev/null
+++ b/src/app/services/_utils/marquee-wrapper.tsx
@@ -0,0 +1,90 @@
+"use client";
+
+import React, { useEffect, useState } from "react";
+import Marquee from "@/components/ui/marquee";
+
+interface MarqueeWrapperProps {
+ children: React.ReactNode;
+ duration?: string;
+ className?: string;
+ itemWidth?: number;
+ gapWidth?: number;
+}
+
+const CARD_WIDTH = 300; // Default width of each card in pixels
+const GAP_WIDTH = 16; // Default gap between cards in pixels
+const BASE_SPEED = 80; // Much faster base speed
+const SLOW_SPEED = 60; // Faster speed for more items
+const MIN_DURATION = 15; // Shorter minimum duration
+const MAX_DURATION = 30; // Shorter maximum duration
+
+const MarqueeWrapper = ({
+ children,
+ duration: propDuration,
+ className = "",
+ itemWidth = CARD_WIDTH,
+ gapWidth = GAP_WIDTH
+}: MarqueeWrapperProps) => {
+ // State to track if the marquee is hovered
+ const [isHovered, setIsHovered] = useState(false);
+ // State to check if the component is rendered on the client
+ const [isClient, setIsClient] = useState(false);
+
+ // Effect to set isClient to true after the component mounts
+ useEffect(() => {
+ setIsClient(true);
+ }, []);
+
+ // Convert children to an array for easier manipulation
+ const childrenArray = React.Children.toArray(children);
+ const itemCount = childrenArray.length;
+
+ // Create duplicates for infinite scrolling
+ const infiniteChildren = [
+ ...childrenArray.slice(0, 2), // Add first two items for seamless effect
+ ...childrenArray,
+ ...childrenArray.slice(0, 2) // Add first two items again at the end
+ ];
+
+ // Function to calculate the duration of the marquee animation
+ const calculateDuration = () => {
+ if (!isClient) return "0s"; // Return 0s if not on client
+
+ // Calculate total width of all items
+ const totalWidth = infiniteChildren.length * (itemWidth + gapWidth);
+ // Determine speed factor based on item count
+ const speedFactor = itemCount > 5 ? SLOW_SPEED : BASE_SPEED;
+ // Calculate base duration based on total width and speed
+ const baseDuration = totalWidth / speedFactor;
+ // Ensure duration is within defined min and max limits
+ const duration = Math.min(MAX_DURATION, Math.max(MIN_DURATION, baseDuration));
+
+ return `${duration}s`; // Return calculated duration
+ };
+
+ // Use propDuration if provided, otherwise calculate it
+ const calculatedDuration = propDuration || calculateDuration();
+
+ // Style object for the marquee animation
+ const style = {
+ '--duration': calculatedDuration,
+ animationPlayState: isHovered ? 'paused' : 'running' // Pause on hover
+ } as React.CSSProperties;
+
+ return (
+
+
setIsHovered(true)} // Set hover state on mouse enter
+ onMouseLeave={() => setIsHovered(false)} // Reset hover state on mouse leave
+ >
+
+ {infiniteChildren}
+
+
+
+ );
+};
+
+export default MarqueeWrapper;
diff --git a/src/app/services/_utils/testimonials.tsx b/src/app/services/_utils/testimonials.tsx
index ce5bf47..60f250c 100644
--- a/src/app/services/_utils/testimonials.tsx
+++ b/src/app/services/_utils/testimonials.tsx
@@ -1,25 +1,14 @@
"use client";
-import { Swiper, SwiperSlide } from "swiper/react";
-import "swiper/css";
-import "swiper/css/grid";
-import "swiper/css/pagination";
-
-import {
- Autoplay,
- Keyboard,
- Mousewheel,
- Navigation,
- Pagination,
-} from "swiper/modules";
import ServicesCTA from "@/components/molecule/services-cta";
import Tagline from "@/components/molecule/tagline";
import { Sparkle } from "lucide-react";
import ANIM__FadeInOutOnScroll from "@/components/anims/fadein.anim";
import Image from "next/image";
import { usePathname } from "next/navigation";
-import { useState } from "react";
+import { useState, useEffect } from "react";
import clsx from "clsx";
+import MarqueeWrapper from "./marquee-wrapper";
const Testimonials = ({ data }: { data: any }) => {
const pathname = usePathname();
@@ -35,6 +24,15 @@ const Testimonials = ({ data }: { data: any }) => {
customwebdevelopment: "customwebdev",
uiux: "uiux",
};
+
+ const [isClient, setIsClient] = useState(false);
+
+ useEffect(() => {
+ setIsClient(true);
+ }, []);
+
+ const filteredData = data?.filter((item: any) => item.text?.length < 100) || [];
+
return (
@@ -48,89 +46,49 @@ const Testimonials = ({ data }: { data: any }) => {
-
-
-
-
-
- {data?.length
- ? data?.filter((item: any) => item.text?.length < 100)?.map((item: any) => {
- return (
-
-
-
- );
- })
- : null}
-
-
-
-
- Get Started Right Away>,
- link: `/joining?type=${path[materedPath]}`,
- },
- secondary: { text: <>Get A Free Consultation>, link: "/" },
- }}
- />
-
+
+
+ {isClient && (
+
+ {filteredData?.slice(0, 10)?.map((item: any) => (
+
+ ))}
+
+ )}
+
+
+
+
+ Get Started Right Away>,
+ link: `/joining?type=${path[materedPath]}`,
+ },
+ secondary: { text: <>Get A Free Consultation>, link: "/" },
+ }}
+ />
);
};
-export default Testimonials;
-
-export const TestimonialCard = ({
- details,
-}: {
- details: {
- _id: number;
- name: string;
- rating?: number;
- category: string;
- text: string;
- avatar: string;
- company: string;
- country: string;
- image?: string;
- date?: string;
- };
+export const TestimonialCard = ({
+ details,
+ className = ""
+}: {
+ details: any;
+ className?: string;
}) => {
const {
_id,
@@ -148,58 +106,59 @@ export const TestimonialCard = ({
return Array.from({ length: n }, (_, i) => i + 1);
};
const [moreText, setMoreText] = useState(false);
-
+
const letterCount = 100;
return (
-
+
{createArray(rating || 1).map((item: number) => {
- return ;
+ return (
+
+ );
})}
-
- {`"${
- text?.length > letterCount
- ? moreText
- ? text
- : text?.slice(0, letterCount) + "..."
- : text
- }"`}
- {text?.length > letterCount ? (
+
+ {`"${text}"`}
+ {text?.length > letterCount && (
setMoreText(!moreText)}
- className="text-secondary text-xs"
+ className="text-secondary text-xs ml-1"
>
{moreText ? "See less" : "See more"}
- ) : null}
+ )}
-
{name}
-
{country}
+
{name}
+
{country}
-
+ {image && (
+
+ )}
);
};
+
+export default Testimonials;
diff --git a/src/components/assets/brandlogo.tsx b/src/components/assets/brandlogo.tsx
index 4dfbd6f..a29aae8 100644
--- a/src/components/assets/brandlogo.tsx
+++ b/src/components/assets/brandlogo.tsx
@@ -1,4 +1,4 @@
-const BrandLogo = () => {
+const BrandLogo = ({dark = false}: {dark?: boolean}) => {
return (