forked from emre/www_projectmycelium_com
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:
@@ -1,64 +1,125 @@
|
||||
import { Container } from '@/components/Container'
|
||||
import { Eyebrow, SectionHeader, P } from '@/components/Texts'
|
||||
"use client";
|
||||
|
||||
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 = [
|
||||
{
|
||||
title: 'Secure Access to Self-Hosted Services',
|
||||
isIntro: true,
|
||||
eyebrow: "USE CASES",
|
||||
title: "Private Connectivity for People, Services, and Infrastructures",
|
||||
description:
|
||||
'Access your services, VMs, and clusters remotely without VPNs, public IPs, or port forwarding.',
|
||||
ideal:
|
||||
'Ideal for: homelabs, personal clouds, long-running self-hosted stacks',
|
||||
"Mycelium Network provides a secure, autonomous communication layer that works across homes, clouds, edge workloads, and global deployments.",
|
||||
},
|
||||
{
|
||||
title: 'Service-to-Service Networking Across Environments',
|
||||
title: "Secure Access to Self-Hosted Services",
|
||||
description:
|
||||
'Connect applications running across home labs, cloud regions, edge nodes, and data centers all on one address space.',
|
||||
ideal:
|
||||
'Ideal for: dev teams, distributed apps, container + K3s workloads',
|
||||
"Access your services, VMs, and clusters remotely without VPNs, public IPs, or port forwarding.",
|
||||
ideal: "Ideal for: homelabs, personal clouds, long-running self-hosted stacks",
|
||||
icon: LockClosedIcon,
|
||||
},
|
||||
{
|
||||
title: 'Resilient Connectivity Across Regions & Outages',
|
||||
title: "Service-to-Service Networking Across Environments",
|
||||
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',
|
||||
"Connect applications running across home labs, cloud regions, edge nodes, and data centers all on one address space.",
|
||||
ideal: "Ideal for: dev teams, distributed apps, container + K3s workloads",
|
||||
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() {
|
||||
return (
|
||||
<section className="bg-white py-24 sm:py-32">
|
||||
<Container>
|
||||
<div className="mx-auto max-w-3xl text-center">
|
||||
<Eyebrow>USE CASES</Eyebrow>
|
||||
<SectionHeader as="h2" className="mt-6 text-gray-900">
|
||||
Private Connectivity for People, Services, and Infrastructures
|
||||
</SectionHeader>
|
||||
<P className="mt-6 text-gray-600">
|
||||
Mycelium Network provides a secure, autonomous communication layer
|
||||
that works across homes, clouds, edge workloads, and global deployments.
|
||||
</P>
|
||||
</div>
|
||||
const sliderRef = useRef<HTMLUListElement>(null);
|
||||
|
||||
<div className="mx-auto mt-16 max-w-5xl grid gap-8 lg:grid-cols-3">
|
||||
{networkUseCases.map((useCase) => (
|
||||
<div
|
||||
key={useCase.title}
|
||||
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"
|
||||
const scrollLeft = () =>
|
||||
sliderRef.current?.scrollBy({ left: -400, behavior: "smooth" });
|
||||
|
||||
const scrollRight = () =>
|
||||
sliderRef.current?.scrollBy({ left: 400, behavior: "smooth" });
|
||||
|
||||
return (
|
||||
<section className="bg-white w-full max-w-8xl mx-auto">
|
||||
<div className="max-w-7xl mx-auto py-6 border border-t-0 border-b-0 border-slate-200" />
|
||||
<div className="w-full border-t border-l border-r border-slate-200" />
|
||||
|
||||
<div className="relative mx-auto max-w-7xl border border-t-0 border-b-0 border-slate-200 bg-white overflow-hidden">
|
||||
<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"
|
||||
}`}
|
||||
>
|
||||
<h3 className="text-xl font-semibold text-gray-900">
|
||||
{useCase.title}
|
||||
</h3>
|
||||
<p className="mt-4 text-sm leading-relaxed text-gray-600">
|
||||
{useCase.description}
|
||||
</p>
|
||||
<p className="mt-4 text-xs font-medium text-cyan-700">
|
||||
{useCase.ideal}
|
||||
</p>
|
||||
</div>
|
||||
{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>
|
||||
<P className="mt-4 text-gray-600 text-sm lg:text-base">
|
||||
{item.description}
|
||||
</P>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-x-4 mt-2">
|
||||
<button
|
||||
onClick={scrollLeft}
|
||||
className="h-8 w-8 flex items-center justify-center border border-slate-300 rounded-md hover:border-cyan-500 transition-colors"
|
||||
>
|
||||
<IoArrowBackOutline className="text-gray-600" size={16} />
|
||||
</button>
|
||||
<button
|
||||
onClick={scrollRight}
|
||||
className="h-8 w-8 flex items-center justify-center border border-slate-300 rounded-md hover:border-cyan-500 transition-colors"
|
||||
>
|
||||
<IoArrowForwardOutline className="text-gray-600" size={16} />
|
||||
</button>
|
||||
</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>
|
||||
))}
|
||||
</div>
|
||||
</Container>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -187,12 +187,14 @@ function DeviceChartIcon(props: React.ComponentPropsWithoutRef<'svg'>) {
|
||||
|
||||
export function SecondaryFeatures() {
|
||||
return (
|
||||
<section
|
||||
id="comingsoon"
|
||||
aria-label="Features for building a portfolio"
|
||||
className="py-20 sm:py-32"
|
||||
>
|
||||
<Container>
|
||||
<section className="w-full max-w-8xl mx-auto bg-transparent">
|
||||
|
||||
{/* ✅ Top horizontal line with spacing */}
|
||||
<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 className="py-12 border border-t-0 border-b-0 border-gray-100">
|
||||
<div className="mx-auto max-w-4xl sm:text-center">
|
||||
<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">
|
||||
@@ -204,7 +206,7 @@ export function SecondaryFeatures() {
|
||||
</div>
|
||||
<ul
|
||||
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) => (
|
||||
<li
|
||||
@@ -220,6 +222,9 @@ export function SecondaryFeatures() {
|
||||
))}
|
||||
</ul>
|
||||
</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>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user