Skip to content

Commit

Permalink
feat(nx-dev): Refactor customer icon grid and testimonial carousel fo…
Browse files Browse the repository at this point in the history
…r improved layout and accessibility

Added Colours for selected testimonial
  • Loading branch information
ndcunningham committed Jan 8, 2025
1 parent e895b9c commit de68ed4
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 45 deletions.
29 changes: 1 addition & 28 deletions nx-dev/ui-customers/src/lib/customer-icon-grid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,44 +11,17 @@ interface CustomerIconGridProps {
icons: CustomerIcon[];
}

// Helper function to determine border class based on the index and total number of icons
function getBorderClass(index: number, totalIcons: number, columns = 4) {
const isFirstRow = index < columns;
const isLastRow = index >= totalIcons - columns;

// First Row
if (isFirstRow) {
if (index % columns === 0) return 'border'; // 1st item
if ((index + 1) % columns === 0) return 'border-y border-r'; // 4th item
return index % 2 === 0 ? 'border' : 'border-y'; // 2nd and 3rd items
}

// Last Row
if (isLastRow) {
if (index % columns === 0) return 'border-x border-b'; // 1st item
if ((index + 1) % columns === 0) return 'border-b border-r'; // 4th item
return index % 2 === 0 ? 'border-x border-b' : 'border-b'; // 2nd and 3rd items
}

// Middle Rows
if (index % columns === 0) return 'border-x border-b'; // 1st item
if ((index + 1) % columns === 0) return 'border-r border-b'; // 4th item
return index % 2 === 0 ? 'border-x border-b' : 'border-b'; // 2nd and 3rd items
}

const CustomerIconGrid: FC<CustomerIconGridProps> = ({ icons }) => {
return (
<div className="grid grid-cols-2 justify-between md:grid-cols-4">
{icons.map((customerIcon, index) => {
const borderClass = getBorderClass(index, icons.length);

return (
<a
key={`customer-icon-${index}`}
href={customerIcon.url}
target="_blank"
rel="noopener noreferrer"
className={`flex items-center justify-center border-slate-200/20 ${borderClass} p-12 transition hover:bg-slate-100/20 hover:text-slate-950 dark:border-slate-800/20 dark:hover:border-slate-600/20 dark:hover:bg-slate-600/10 dark:hover:text-white`}
className={`flex items-center justify-center border-slate-200/20 p-12 shadow-[0_0px_1px_0_rgba(226,232,240,0.2)] transition hover:bg-slate-100/20 hover:text-slate-950 dark:border-slate-800/20 dark:hover:border-slate-600/20 dark:hover:bg-slate-600/10 dark:hover:text-white`}
>
<customerIcon.icon
aria-hidden="true"
Expand Down
47 changes: 31 additions & 16 deletions nx-dev/ui-customers/src/lib/customer-testimonial-carousel.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
'use client';
import { Fragment, useState, useEffect } from 'react';
import { Fragment, useState, useEffect, FC, SVGProps } from 'react';
import { Dialog, Transition } from '@headlessui/react';
import {
ChevronLeftIcon,
Expand Down Expand Up @@ -33,9 +33,10 @@ interface Testimonial {
videoId: string;
thumbnail: string;
logo: {
icon: any;
icon: FC<SVGProps<SVGSVGElement>>;
height: string;
width: string;
color?: string;
};
}

Expand All @@ -45,8 +46,9 @@ const testimonials: Testimonial[] = [
subtitle:
'How Hetzner Cloud solved the Performance Paradox: Faster builds with more features',
metrics: [
{ value: 'Faster CI', label: 'More projects, less time' },
{ value: 'Seconds', label: 'From 20min CI to instant builds' },
{ value: 'Faster tests', label: 'From 20 min -> seconds.' },
{ value: 'Faster builds', label: 'Down from 30 minutes.' },
{ value: 'Speed & scale', label: 'Faster CI with even more features.' },
],
company: 'Hetzner',
videoId: '2BLqiNnBPuU',
Expand All @@ -55,6 +57,7 @@ const testimonials: Testimonial[] = [
icon: HetznerCloudIcon,
height: 'h-10',
width: 'w-10',
color: 'text-[#D50C2D]',
},
},
{
Expand Down Expand Up @@ -83,6 +86,7 @@ const testimonials: Testimonial[] = [
icon: CasewareIcon,
height: 'h-12',
width: 'w-12',
color: 'text-[#F56354]',
},
},
{
Expand Down Expand Up @@ -113,6 +117,7 @@ const testimonials: Testimonial[] = [
icon: SiriusxmIcon,
height: 'h-32',
width: 'w-32',
color: 'text-[#0000EB]',
},
},
{
Expand Down Expand Up @@ -141,6 +146,7 @@ const testimonials: Testimonial[] = [
icon: PayfitIcon,
height: 'h-16',
width: 'w-16',
color: 'text-[#0F6FDE]',
},
},
{
Expand Down Expand Up @@ -170,6 +176,7 @@ const testimonials: Testimonial[] = [
icon: UkgIcon,
height: 'h-20',
width: 'w-20',
color: 'text-[#005151]',
},
},
{
Expand Down Expand Up @@ -197,6 +204,7 @@ const testimonials: Testimonial[] = [
icon: VmwareIcon,
height: 'h-28',
width: 'w-28',
color: 'text-[#607078]',
},
},
];
Expand All @@ -212,10 +220,7 @@ export function CustomerTestimonialCarousel(): JSX.Element {

if (!isOpen) {
timer = setInterval(() => {
setCurrentIndex((prevIndex) => {
const nextIndex = (prevIndex + 1) % testimonials.length;
return nextIndex;
});
setCurrentIndex((prevIndex) => (prevIndex + 1) % testimonials.length);
}, slideLogoTimeOut);
}

Expand Down Expand Up @@ -249,18 +254,22 @@ export function CustomerTestimonialCarousel(): JSX.Element {
{/* Prev Button Mobile only */}
<button
disabled={currentIndex === 0}
title={`See ${testimonials[currentIndex - 1]?.company} again!`}
title={`See ${testimonials[currentIndex - 1]?.company}`}
className="flex h-12 w-12 items-center justify-center rounded-full p-2 transition hover:text-slate-950 disabled:pointer-events-none disabled:opacity-0 md:hidden dark:hover:text-white"
onClick={() => {
setCurrentIndex(
(currentIndex - 1 + testimonials.length) % testimonials.length
);
}}
>
<ChevronLeftIcon className="h-8 w-8" />
<span className="sr-only">Previous</span>
<ChevronLeftIcon
className="h-8 w-8 flex-shrink-0"
aria-hidden="true"
/>
</button>
<div
className="group relative h-full max-h-[450px] w-full cursor-pointer self-stretch overflow-hidden rounded-lg xl:shadow-2xl"
className="group relative h-56 w-full cursor-pointer self-stretch overflow-hidden rounded-lg sm:h-80 md:h-[450px] xl:shadow-2xl"
onClick={() => setIsOpen(true)}
>
{/* Thumbnail */}
Expand All @@ -279,7 +288,7 @@ export function CustomerTestimonialCarousel(): JSX.Element {
{currentTestimonial.subtitle}
</h3>
<button className="mt-2 inline-flex items-center gap-1 rounded-full bg-white/10 px-4 py-2 text-sm font-medium text-white backdrop-blur-sm transition hover:bg-white/20 md:mt-4 md:gap-2">
<PlayIcon className="size-3 md:size-5" />
<PlayIcon className="size-3 md:size-5" aria-hidden="true" />
Watch video
</button>
</div>
Expand All @@ -288,14 +297,18 @@ export function CustomerTestimonialCarousel(): JSX.Element {
<button
className="flex h-12 w-12 items-center justify-center rounded-full p-2 transition hover:text-slate-950 disabled:pointer-events-none disabled:opacity-0 md:hidden dark:hover:text-white"
disabled={currentIndex === testimonials.length - 1}
title={`Next ${testimonials[currentIndex + 1]?.company}!`}
title={`Next ${testimonials[currentIndex + 1]?.company}`}
onClick={() => {
setCurrentIndex(
(currentIndex + 1 + testimonials.length) % testimonials.length
);
}}
>
<ChevronRightIcon className="h-8 w-8" />
<span className="sr-only">Next</span>
<ChevronRightIcon
className="h-8 w-8 flex-shrink-0"
aria-hidden="true"
/>
</button>
</div>

Expand Down Expand Up @@ -330,14 +343,16 @@ export function CustomerTestimonialCarousel(): JSX.Element {
>
<logo.icon
key={`firstSet-icon-${i}`}
className={`${logo.height} ${logo.width} transition-transform duration-300`}
className={`${logo.height} ${logo.width} ${
i === currentIndex && logo.color
} transition-transform duration-300`}
/>

{/* Progress Bar */}
{i === currentIndex && !isOpen && (
<div className="absolute left-0 top-0 h-[2px] w-full overflow-hidden bg-gray-300/80 transition-all">
<div
className="animate-progress h-full w-full bg-sky-600/80"
className="animate-progress h-full w-full bg-blue-600/80 dark:bg-sky-600/80"
style={{ animationDuration: `${slideLogoTimeOut}ms` }}
/>
</div>
Expand Down
4 changes: 4 additions & 0 deletions nx-dev/ui-icons/src/lib/customers/caseware.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { FC, SVGProps } from 'react';

/**
* Use `#F56354` for a colored version.
*/

export const CasewareIcon: FC<SVGProps<SVGSVGElement>> = (props) => (
<svg
xmlns="http://www.w3.org/2000/svg"
Expand Down
4 changes: 3 additions & 1 deletion nx-dev/ui-icons/src/lib/customers/ukg.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { FC, SVGProps } from 'react';

/**
* Use `#005151` for a colored version.
*/
export const UkgIcon: FC<SVGProps<SVGSVGElement>> = (props) => (
<svg
xmlns="http://www.w3.org/2000/svg"
Expand Down

0 comments on commit de68ed4

Please sign in to comment.