mirror of
https://github.com/outbackdingo/firezone.git
synced 2026-01-27 18:18:55 +00:00
feat(website): Create customer testimonials section, add scrollbar styling (#6252)
Co-authored-by: Jamil Bou Kheir <jamilbk@users.noreply.github.com>
This commit is contained in:
BIN
website/public/images/portrait-cian-byrne.jpg
Normal file
BIN
website/public/images/portrait-cian-byrne.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 60 KiB |
BIN
website/public/images/portrait-james-winegar.png
Normal file
BIN
website/public/images/portrait-james-winegar.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 251 KiB |
BIN
website/public/images/portrait-mark-sim.jpg
Normal file
BIN
website/public/images/portrait-mark-sim.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
BIN
website/public/images/portrait-robert-buisman.png
Normal file
BIN
website/public/images/portrait-robert-buisman.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
@@ -27,4 +27,49 @@
|
||||
*:is(code) {
|
||||
@apply overflow-x-auto rounded text-sm;
|
||||
}
|
||||
|
||||
/* For WebKit browsers (Chrome, Safari) */
|
||||
.dark-scroll::-webkit-scrollbar {
|
||||
width: 12px; /* Adjust the width of the scrollbar */
|
||||
}
|
||||
|
||||
.dark-scroll::-webkit-scrollbar-button {
|
||||
height: 0;
|
||||
width: 0;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dark-scroll::-webkit-scrollbar-thumb {
|
||||
background-color: #666; /* White color at 20% opacity */
|
||||
border-radius: 10px; /* Optional: Rounds the corners of the scrollbar thumb */
|
||||
}
|
||||
|
||||
.dark-scroll::-webkit-scrollbar-track {
|
||||
background-color: #222; /* White color at 40% opacity */
|
||||
border-radius: 10px; /* Optional: Rounds the corners of the scrollbar track */
|
||||
}
|
||||
|
||||
/* For Firefox */
|
||||
* .dark-scroll {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #666 #222;
|
||||
}
|
||||
|
||||
.dark-scroll::-ms-scrollbar {
|
||||
width: 12px; /* Adjust the width of the scrollbar */
|
||||
}
|
||||
|
||||
.dark-scroll::-ms-scrollbar-thumb {
|
||||
background-color: rgba(255, 255, 255, 0.2); /* White color at 20% opacity */
|
||||
border-radius: 10px; /* Optional: Rounds the corners of the scrollbar thumb */
|
||||
}
|
||||
|
||||
.dark-scroll::-ms-scrollbar-track {
|
||||
background-color: rgba(255, 255, 255, 0.4); /* White color at 40% opacity */
|
||||
border-radius: 10px; /* Optional: Rounds the corners of the scrollbar track */
|
||||
}
|
||||
|
||||
.dark-scroll::-ms-scrollbar-button {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import ComplianceDiagram from "@/components/Animations/ComplianceDiagram";
|
||||
import SimpleArchitecture from "@/components/Animations/SimpleArchitecture";
|
||||
import { manrope } from "@/lib/fonts";
|
||||
import "@/styles/hero.css";
|
||||
import CustomerTestimonials from "@/components/CustomerTestimonials";
|
||||
import FeatureCards from "@/components/FeatureCards";
|
||||
import SingleFeature from "@/components/SingleFeature";
|
||||
|
||||
@@ -116,6 +117,8 @@ export default function Page() {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<CustomerTestimonials />
|
||||
|
||||
{/* Feature section 2: Achieve compliance in minutes, not weeks. */}
|
||||
<section className="bg-white py-20 md:py-16">
|
||||
<div className="sm:mx-auto px-4 mb-4 md:mb-8 text-3xl md:text-4xl lg:text-5xl text-pretty text-center">
|
||||
|
||||
45
website/src/components/Carousel/index.tsx
Normal file
45
website/src/components/Carousel/index.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import type { CustomFlowbiteTheme } from "flowbite-react";
|
||||
import { Carousel as FlowbiteCarousel } from "flowbite-react";
|
||||
|
||||
const theme: CustomFlowbiteTheme["carousel"] = {
|
||||
root: {
|
||||
base: "relative h-full w-full",
|
||||
leftControl:
|
||||
"absolute left-0 bottom-4 flex items-end justify-center px-4 focus:outline-none",
|
||||
rightControl:
|
||||
"absolute right-0 bottom-4 flex items-end justify-center px-4 focus:outline-none",
|
||||
},
|
||||
indicators: {
|
||||
active: {
|
||||
off: "bg-white/50 hover:bg-white dark:bg-gray-800/50 dark:hover:bg-gray-800",
|
||||
on: "bg-white dark:bg-gray-800",
|
||||
},
|
||||
base: "h-3 w-3 rounded-full",
|
||||
wrapper: "absolute bottom-7 left-1/2 flex -translate-x-1/2 space-x-3",
|
||||
},
|
||||
item: {
|
||||
base: "absolute left-1/2 block w-full -translate-x-1/2",
|
||||
wrapper: {
|
||||
off: "w-full flex-shrink-0 transform cursor-default snap-center",
|
||||
on: "w-full flex-shrink-0 transform cursor-grab snap-center",
|
||||
},
|
||||
},
|
||||
control: {
|
||||
base: "inline-flex h-8 w-8 items-center justify-center rounded-full bg-white/30 group-hover:bg-white/50 group-focus:outline-none group-focus:ring-4 group-focus:ring-white dark:bg-gray-800/30 dark:group-hover:bg-gray-800/60 dark:group-focus:ring-gray-800/70 sm:h-10 sm:w-10",
|
||||
icon: "h-5 w-5 text-white dark:text-gray-800 sm:h-6 sm:w-6",
|
||||
},
|
||||
scrollContainer: {
|
||||
base: "flex h-full snap-mandatory overflow-y-hidden overflow-x-scroll scroll-smooth rounded-lg",
|
||||
snap: "snap-x",
|
||||
},
|
||||
};
|
||||
|
||||
export default function Carousel({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="h-96 rounded-2xl text-neutral-50 bg-[#1B1B1D]">
|
||||
<FlowbiteCarousel slideInterval={10000} pauseOnHover theme={theme}>
|
||||
{children}
|
||||
</FlowbiteCarousel>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
109
website/src/components/CustomerTestimonials/index.tsx
Normal file
109
website/src/components/CustomerTestimonials/index.tsx
Normal file
@@ -0,0 +1,109 @@
|
||||
import Carousel from "@/components/Carousel";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { HiArrowLeft, HiArrowRight } from "react-icons/hi2";
|
||||
import { FaHeart } from "react-icons/fa";
|
||||
import { manrope } from "@/lib/fonts";
|
||||
|
||||
const customerData = [
|
||||
{
|
||||
desc: `When producing live broadcasts for Fortune 500 companies security is of
|
||||
the utmost importance. We therefore selected Firezone for its robust
|
||||
WireGuard-based architecture. The flexible policy system and simple &
|
||||
clean user experience make Firezone the best fitting product for us in
|
||||
the market after trying several other solutions like Tailscale, OpenVPN,
|
||||
and Nebula.`,
|
||||
authorName: "Robert Buisman",
|
||||
authorImage: "/images/portrait-robert-buisman.png",
|
||||
authorTitle: "CEO, NOMOBO",
|
||||
},
|
||||
{
|
||||
desc: `Firezone's easy-to-setup, sleek, and simple interface makes management
|
||||
effortless. It perfectly met our zero-trust security needs without the
|
||||
complexity found in other products we tested.`,
|
||||
authorName: "Mark Sim",
|
||||
authorImage: "/images/portrait-mark-sim.jpg",
|
||||
authorTitle: "Technical Account Manager, Beakon",
|
||||
},
|
||||
{
|
||||
desc: `After comparing Tailscale, we ultimately chose Firezone to secure access
|
||||
to our data warehouses. Firezone's ease of configuration and robust
|
||||
policy-based access system made it the clear choice for our needs.`,
|
||||
authorName: "James Winegar",
|
||||
authorImage: "/images/portrait-james-winegar.png",
|
||||
authorTitle: "CEO, Corrdyn",
|
||||
},
|
||||
{
|
||||
desc: `At Strong Compute, we have been using Firezone for over 3 years and it
|
||||
is still the most stable and best VPN solution we tested for remote access.`,
|
||||
authorName: "Cian Byrne",
|
||||
authorImage: "/images/portrait-cian-byrne.jpg",
|
||||
authorTitle: "Founding Engineer, Strong Compute",
|
||||
},
|
||||
];
|
||||
|
||||
interface TestimonialBoxProps {
|
||||
desc: string;
|
||||
authorImage: string;
|
||||
authorName: string;
|
||||
authorTitle: string;
|
||||
}
|
||||
|
||||
const TestimonialBox = ({
|
||||
desc,
|
||||
authorImage,
|
||||
authorName,
|
||||
authorTitle,
|
||||
}: TestimonialBoxProps) => {
|
||||
return (
|
||||
<div className="h-full px-8 md:px-16 py-8 md:py-12">
|
||||
<div className="mb-4 md:mb-8">
|
||||
<p className="text-md md:text-lg tracking-wide font-light mb-2 md:mb-6 break-keep italic">
|
||||
"{desc}"
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex gap-4 items-center">
|
||||
<Image
|
||||
src={authorImage}
|
||||
alt="author portrait"
|
||||
width={128}
|
||||
height={128}
|
||||
className="w-10 h-10 sm:h-12 sm:h-12 md:h-16 md:w-16 rounded-full"
|
||||
/>
|
||||
<div>
|
||||
<p className="text-md md:text-lg">{authorName}</p>
|
||||
<p className="text-sm md:text-md font-light">{authorTitle}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default function CustomerTestimonials() {
|
||||
return (
|
||||
<section className="bg-neutral-950 py-24 px-8 md:px-0">
|
||||
<div className="max-w-screen-md mx-auto">
|
||||
<h3
|
||||
className={`text-white text-3xl leading-5 md:text-4xl lg:text-5xl tracking-tight font-medium inline-block text-left mb-4 ${manrope.className}`}
|
||||
>
|
||||
Customers{" "}
|
||||
<FaHeart className="text-red-500 w-12 h-12 mx-1 inline-block" /> us
|
||||
</h3>
|
||||
<p className="text-neutral-500 text-sm md:text-md font-light mb-8">
|
||||
(and we love them back)
|
||||
</p>
|
||||
<Carousel>
|
||||
{customerData.map((item, index) => (
|
||||
<TestimonialBox
|
||||
key={index}
|
||||
authorTitle={item.authorTitle}
|
||||
desc={item.desc}
|
||||
authorImage={item.authorImage}
|
||||
authorName={item.authorName}
|
||||
/>
|
||||
))}
|
||||
</Carousel>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user