feat: convert network use cases to horizontal carousel with icons

- Replaced static grid layout with scrollable carousel supporting touch/mouse navigation
- Added hero icons to each use case card for visual hierarchy
- Introduced intro card with navigation controls and updated styling for better mobile experience
This commit is contained in:
2025-11-07 23:34:27 +01:00
parent 100cae988c
commit ee6b5458de
2 changed files with 119 additions and 53 deletions

View File

@@ -1,64 +1,125 @@
import { Container } from '@/components/Container' "use client";
import { Eyebrow, SectionHeader, P } from '@/components/Texts'
import { useRef } from "react";
import { Eyebrow, SectionHeader, P, CT, CP } from "@/components/Texts";
import { IoArrowBackOutline, IoArrowForwardOutline } from "react-icons/io5";
import {
LockClosedIcon,
ArrowPathIcon,
GlobeAltIcon,
} from "@heroicons/react/24/solid";
const networkUseCases = [ const networkUseCases = [
{ {
title: 'Secure Access to Self-Hosted Services', isIntro: true,
eyebrow: "USE CASES",
title: "Private Connectivity for People, Services, and Infrastructures",
description: description:
'Access your services, VMs, and clusters remotely without VPNs, public IPs, or port forwarding.', "Mycelium Network provides a secure, autonomous communication layer that works across homes, clouds, edge workloads, and global deployments.",
ideal:
'Ideal for: homelabs, personal clouds, long-running self-hosted stacks',
}, },
{ {
title: 'Service-to-Service Networking Across Environments', title: "Secure Access to Self-Hosted Services",
description: description:
'Connect applications running across home labs, cloud regions, edge nodes, and data centers all on one address space.', "Access your services, VMs, and clusters remotely without VPNs, public IPs, or port forwarding.",
ideal: ideal: "Ideal for: homelabs, personal clouds, long-running self-hosted stacks",
'Ideal for: dev teams, distributed apps, container + K3s workloads', icon: LockClosedIcon,
}, },
{ {
title: 'Resilient Connectivity Across Regions & Outages', title: "Service-to-Service Networking Across Environments",
description: description:
'Connect systems across countries, datacenters, edge locations, and remote deployments — with routing that automatically heals around ISP failures, censorship, and regional outages.', "Connect applications running across home labs, cloud regions, edge nodes, and data centers all on one address space.",
ideal: ideal: "Ideal for: dev teams, distributed apps, container + K3s workloads",
'Ideal for: research networks, cross-border orgs, distributed compute, off-grid / rural deployments', icon: GlobeAltIcon,
}, },
] {
title: "Resilient Connectivity Across Regions & Outages",
description:
"Connect systems across countries, datacenters, edge locations, and remote deployments — with routing that automatically heals around ISP failures, censorship, and regional outages.",
ideal:
"Ideal for: research networks, cross-border orgs, distributed compute, off-grid / rural deployments",
icon: ArrowPathIcon,
},
];
export function NetworkUsecases() { export function NetworkUsecases() {
const sliderRef = useRef<HTMLUListElement>(null);
const scrollLeft = () =>
sliderRef.current?.scrollBy({ left: -400, behavior: "smooth" });
const scrollRight = () =>
sliderRef.current?.scrollBy({ left: 400, behavior: "smooth" });
return ( return (
<section className="bg-white py-24 sm:py-32"> <section className="bg-white w-full max-w-8xl mx-auto">
<Container> <div className="max-w-7xl mx-auto py-6 border border-t-0 border-b-0 border-slate-200" />
<div className="mx-auto max-w-3xl text-center"> <div className="w-full border-t border-l border-r border-slate-200" />
<Eyebrow>USE CASES</Eyebrow>
<SectionHeader as="h2" className="mt-6 text-gray-900"> <div className="relative mx-auto max-w-7xl border border-t-0 border-b-0 border-slate-200 bg-white overflow-hidden">
Private Connectivity for People, Services, and Infrastructures <ul
ref={sliderRef}
className="flex overflow-x-auto snap-x snap-mandatory scroll-smooth no-scrollbar"
>
{networkUseCases.map((item, idx) => (
<li
key={idx}
className={`snap-start shrink-0 w-[85%] sm:w-[50%] lg:w-[33%] border border-slate-200 p-10 relative ${
item.isIntro ? "bg-gray-50/80" : "bg-white"
}`}
>
{item.isIntro ? (
<div className="flex flex-col justify-between h-full">
<div>
<Eyebrow>{item.eyebrow}</Eyebrow>
<SectionHeader
as="h3"
className="mt-4 text-gray-900 text-xl lg:text-2xl"
>
{item.title}
</SectionHeader> </SectionHeader>
<P className="mt-6 text-gray-600"> <P className="mt-4 text-gray-600 text-sm lg:text-base">
Mycelium Network provides a secure, autonomous communication layer {item.description}
that works across homes, clouds, edge workloads, and global deployments.
</P> </P>
</div> </div>
<div className="mx-auto mt-16 max-w-5xl grid gap-8 lg:grid-cols-3"> <div className="flex items-center gap-x-4 mt-2">
{networkUseCases.map((useCase) => ( <button
<div onClick={scrollLeft}
key={useCase.title} className="h-8 w-8 flex items-center justify-center border border-slate-300 rounded-md hover:border-cyan-500 transition-colors"
className="rounded-3xl border border-slate-200 bg-white p-8 shadow-sm transition hover:-translate-y-1 hover:border-cyan-300 hover:shadow-lg"
> >
<h3 className="text-xl font-semibold text-gray-900"> <IoArrowBackOutline className="text-gray-600" size={16} />
{useCase.title} </button>
</h3> <button
<p className="mt-4 text-sm leading-relaxed text-gray-600"> onClick={scrollRight}
{useCase.description} className="h-8 w-8 flex items-center justify-center border border-slate-300 rounded-md hover:border-cyan-500 transition-colors"
</p> >
<p className="mt-4 text-xs font-medium text-cyan-700"> <IoArrowForwardOutline className="text-gray-600" size={16} />
{useCase.ideal} </button>
</p>
</div> </div>
</div>
) : (
<>
{/* ✅ Icon above title */}
<div className="h-10 w-10 flex items-center justify-center rounded-xl bg-gray-100 mb-4">
<item.icon className="h-6 w-6 text-cyan-600" />
</div>
<CT className="text-lg font-semibold text-gray-900">
{item.title}
</CT>
<CP className="mt-2 text-gray-600 leading-snug">
{item.description}
</CP>
<CP className="mt-3 text-xs font-medium text-cyan-700">
{item.ideal}
</CP>
</>
)}
</li>
))} ))}
</ul>
</div> </div>
</Container>
</section> </section>
) );
} }

View File

@@ -187,12 +187,14 @@ function DeviceChartIcon(props: React.ComponentPropsWithoutRef<'svg'>) {
export function SecondaryFeatures() { export function SecondaryFeatures() {
return ( return (
<section <section className="w-full max-w-8xl mx-auto bg-transparent">
id="comingsoon"
aria-label="Features for building a portfolio" {/* ✅ Top horizontal line with spacing */}
className="py-20 sm:py-32" <div className="max-w-7xl bg-transparent mx-auto py-6 border border-t-0 border-b-0 border-gray-100"></div>
> <div className="w-full border-t border-l border-r border-gray-100" />
<Container>
<Container className="py-12 border border-t-0 border-b-0 border-gray-100">
<div className="mx-auto max-w-4xl sm:text-center"> <div className="mx-auto max-w-4xl sm:text-center">
<h2 className="text-base/7 font-semibold text-cyan-500">IN ACTIVE EVOLUTION</h2> <h2 className="text-base/7 font-semibold text-cyan-500">IN ACTIVE EVOLUTION</h2>
<p className="text-3xl lg:text-4xl font-medium tracking-tight text-gray-900"> <p className="text-3xl lg:text-4xl font-medium tracking-tight text-gray-900">
@@ -204,7 +206,7 @@ export function SecondaryFeatures() {
</div> </div>
<ul <ul
role="list" role="list"
className="mx-auto mt-16 grid max-w-2xl grid-cols-1 gap-6 text-sm sm:mt-20 sm:grid-cols-2 md:gap-y-10 lg:max-w-none lg:grid-cols-3" className="mx-auto mt-12 grid max-w-2xl grid-cols-1 gap-6 text-sm sm:grid-cols-2 md:gap-y-10 lg:max-w-none lg:grid-cols-3"
> >
{features.map((feature) => ( {features.map((feature) => (
<li <li
@@ -220,6 +222,9 @@ export function SecondaryFeatures() {
))} ))}
</ul> </ul>
</Container> </Container>
{/* ✅ Bottom horizontal line with spacing */}
<div className="w-full border-b border-gray-100" />
<div className="max-w-7xl bg-transparent mx-auto py-6 border border-t-0 border-b-0 border-gray-100"></div>
</section> </section>
) )
} }