re-design public site
This commit is contained in:
@@ -13,25 +13,23 @@ import Image from 'next/image';
|
||||
const images = [
|
||||
{
|
||||
title: 'Beautiful overview, everything is clickable to get more details',
|
||||
url: '/demo/overview-min.png',
|
||||
url: '/demo-2/1.png',
|
||||
},
|
||||
{
|
||||
title: 'Histogram, perfect for showing active users',
|
||||
url: '/demo/histogram-min.png',
|
||||
url: '/demo-2/2.png',
|
||||
},
|
||||
{ title: 'Make your overview public', url: '/demo/overview-share-min.png' },
|
||||
{ title: 'Make your overview public', url: '/demo-2/3.png' },
|
||||
{
|
||||
title: 'See real time events from your users',
|
||||
url: '/demo/events-min.png',
|
||||
url: '/demo-2/4.png',
|
||||
},
|
||||
{ title: 'The classic line chart', url: '/demo/line-min.png' },
|
||||
{ title: 'The classic line chart', url: '/demo-2/5.png' },
|
||||
{
|
||||
title: 'Bar charts to see your most popular content',
|
||||
url: '/demo/bar-min.png',
|
||||
url: '/demo-2/6.png',
|
||||
},
|
||||
{ title: 'Get nice metric cards with graphs', url: '/demo/metrics-min.png' },
|
||||
{ title: 'See where your events comes from', url: '/demo/worldmap-min.png' },
|
||||
{ title: 'The classic pie chart', url: '/demo/pie-min.png' },
|
||||
{ title: 'Get nice metric cards with graphs', url: '/demo-2/7.png' },
|
||||
];
|
||||
|
||||
export function PreviewCarousel() {
|
||||
@@ -51,13 +49,18 @@ export function PreviewCarousel() {
|
||||
key={item.url}
|
||||
className="flex-[0_0_80%] max-w-3xl pl-8"
|
||||
>
|
||||
<div className="aspect-video">
|
||||
<div className="p-3 rounded-xl overflow-hidden bg-gradient-to-b from-blue-100/50 to-white/50">
|
||||
<div
|
||||
style={{
|
||||
aspectRatio: 2982 / 1484,
|
||||
}}
|
||||
>
|
||||
<div className="p-1 rounded-xl overflow-hidden bg-gradient-to-b from-blue-100/50 to-white/50">
|
||||
<Image
|
||||
priority
|
||||
className="w-full h-full object-cover rounded-lg"
|
||||
src={item.url}
|
||||
width={1080}
|
||||
height={608}
|
||||
width={2982 * 0.5}
|
||||
height={1484 * 0.5}
|
||||
alt={item.title}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -26,7 +26,10 @@ export function Paragraph({ children, className }: Props) {
|
||||
export function Heading1({ children, className }: Props) {
|
||||
return (
|
||||
<h1
|
||||
className={cn('text-4xl md:text-5xl font-bold text-slate-800', className)}
|
||||
className={cn(
|
||||
'text-4xl md:text-5xl font-bold text-slate-800 !leading-tight',
|
||||
className
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</h1>
|
||||
|
||||
@@ -20,9 +20,9 @@ export default function Footer() {
|
||||
</div>
|
||||
|
||||
<div className="overflow-hidden rounded-xl">
|
||||
<div className="p-3 bg-white/20">
|
||||
<div className="p-2 bg-white/20">
|
||||
<Image
|
||||
src="/demo/overview-min.png"
|
||||
src="/demo-2/1.png"
|
||||
width={1080}
|
||||
height={608}
|
||||
alt="Openpanel overview page"
|
||||
@@ -31,13 +31,13 @@ export default function Footer() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="absolute bottom-0 left-0 right-0">
|
||||
<div className="-mt-8 relative z-10">
|
||||
<div className="h-px w-full bg-[radial-gradient(circle,rgba(255,255,255,0.7)_0%,rgba(255,255,255,0.7)_50%,rgba(255,255,255,0)_100%)]"></div>
|
||||
<div className="p-4 bg-blue-darker">
|
||||
<div className="container">
|
||||
<div className="flex justify-between items-center text-sm">
|
||||
<div className="flex flex-col gap-4 md:flex-row md:justify-between md:items-center text-sm">
|
||||
<Logo />
|
||||
<div className="flex gap-4">
|
||||
<div className="flex flex-col md:flex-row gap-4">
|
||||
<Link className="hover:underline" href="/terms">
|
||||
Terms and Conditions
|
||||
</Link>
|
||||
|
||||
@@ -1,76 +1,56 @@
|
||||
// background-image: radial-gradient(circle at 1px 1px, black 1px, transparent 0);
|
||||
// background-size: 40px 40px;
|
||||
|
||||
import { Logo } from '@/components/Logo';
|
||||
import {
|
||||
BarChart2Icon,
|
||||
CookieIcon,
|
||||
Globe2Icon,
|
||||
LayoutPanelTopIcon,
|
||||
LockIcon,
|
||||
ServerIcon,
|
||||
} from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
|
||||
import { Heading1, Lead, Lead2 } from './copy';
|
||||
import { JoinWaitlist } from './join-waitlist';
|
||||
import { PreviewCarousel } from './carousel';
|
||||
import { Heading1, Lead2 } from './copy';
|
||||
import { JoinWaitlistHero } from './join-waitlist-hero';
|
||||
|
||||
const features = [
|
||||
{
|
||||
title: 'Great overview',
|
||||
icon: LayoutPanelTopIcon,
|
||||
},
|
||||
{
|
||||
title: 'Beautiful charts',
|
||||
icon: BarChart2Icon,
|
||||
},
|
||||
{
|
||||
title: 'Privacy focused',
|
||||
icon: LockIcon,
|
||||
},
|
||||
{
|
||||
title: 'Open-source',
|
||||
icon: Globe2Icon,
|
||||
},
|
||||
{
|
||||
title: 'No cookies',
|
||||
icon: CookieIcon,
|
||||
},
|
||||
{
|
||||
title: 'Self-hosted',
|
||||
icon: ServerIcon,
|
||||
},
|
||||
const avatars = [
|
||||
'https://api.dicebear.com/7.x/adventurer/svg?seed=Chester&backgroundColor=b6e3f4',
|
||||
'https://api.dicebear.com/7.x/adventurer/svg?seed=Casper&backgroundColor=c0aede',
|
||||
'https://api.dicebear.com/7.x/adventurer/svg?seed=Boo&backgroundColor=ffdfbf',
|
||||
];
|
||||
|
||||
export function Hero({ waitlistCount }: { waitlistCount: number }) {
|
||||
return (
|
||||
<div className="flex flex-col items-center w-full text-center text-blue-950">
|
||||
<div className="pt-32 pb-56 p-4 flex flex-col items-center max-w-3xl bg-[radial-gradient(circle,rgba(255,255,255,0.7)_0%,rgba(255,255,255,0.7)_50%,rgba(255,255,255,0)_100%)]">
|
||||
<Heading1 className="mb-4">
|
||||
<div className="flex py-32 flex-col items-center w-full text-center bg-[#1F54FF] relative overflow-hidden">
|
||||
{/* <div className="inset-0 absolute h-full w-full bg-[radial-gradient(circle,rgba(255,255,255,0.1)_0%,rgba(255,255,255,0)_100%)]"></div> */}
|
||||
<div className="inset-0 absolute h-full w-full flex items-center justify-center">
|
||||
<div className="w-[600px] h-[600px] ring-1 ring-white/05 rounded-full shrink-0"></div>
|
||||
</div>
|
||||
<div className="inset-0 absolute h-full w-full flex items-center justify-center">
|
||||
<div className="w-[900px] h-[900px] ring-1 ring-white/10 rounded-full shrink-0"></div>
|
||||
</div>
|
||||
<div className="inset-0 absolute h-full w-full flex items-center justify-center">
|
||||
<div className="w-[1200px] h-[1200px] ring-1 ring-white/20 rounded-full shrink-0"></div>
|
||||
</div>
|
||||
<div className="relative flex flex-col items-center max-w-3xl">
|
||||
<Image
|
||||
width={64}
|
||||
height={64}
|
||||
src="/logo-white.png"
|
||||
alt="Openpanel Logo"
|
||||
className="w-16 h-16 mb-8"
|
||||
/>
|
||||
<Heading1 className="mb-4 text-white">
|
||||
An open-source
|
||||
<br />
|
||||
alternative to Mixpanel
|
||||
</Heading1>
|
||||
<p>
|
||||
Mixpanel + Plausible ={' '}
|
||||
<strong className="text-blue-600">Openpanel!</strong> A simple
|
||||
analytics tool that your wallet can afford.
|
||||
</p>
|
||||
<Lead2 className="text-white/70 font-light">
|
||||
Mixpanel + Plausible = <span className="text-white">Openpanel!</span>{' '}
|
||||
<br />A simple analytics tool that your wallet can afford.
|
||||
</Lead2>
|
||||
<div className="my-12 w-full flex flex-col items-center">
|
||||
<JoinWaitlist />
|
||||
<div className="mt-2">
|
||||
<p>{waitlistCount} people have already signed up! 🚀</p>
|
||||
<JoinWaitlistHero />
|
||||
<div className="mt-6 flex justify-center items-center">
|
||||
<p className="text-white">
|
||||
{waitlistCount} people have already signed up! 🚀
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-10 max-w-xl justify-center">
|
||||
{features.map(({ icon: Icon, title }) => (
|
||||
<div className="flex gap-2 items-center justify-center">
|
||||
<Icon className="text-blue-light " />
|
||||
{title}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<PreviewCarousel />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
128
apps/public/src/app/join-waitlist-hero.tsx
Normal file
128
apps/public/src/app/join-waitlist-hero.tsx
Normal file
@@ -0,0 +1,128 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import { cn } from '@/utils/cn';
|
||||
import Link from 'next/link';
|
||||
|
||||
interface JoinWaitlistProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function JoinWaitlistHero({ className }: JoinWaitlistProps) {
|
||||
const [value, setValue] = useState('');
|
||||
const [open, setOpen] = useState(false);
|
||||
const [success, setSuccess] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
// @ts-ignore
|
||||
window.openpanel.event('waitlist_open');
|
||||
}
|
||||
}, [open]);
|
||||
|
||||
useEffect(() => {
|
||||
if (success) {
|
||||
// @ts-ignore
|
||||
window.openpanel.event('waitlist_success', {
|
||||
email: value,
|
||||
});
|
||||
}
|
||||
}, [success]);
|
||||
|
||||
const renderSuccess = () => (
|
||||
<DialogContent className="sm:max-w-[425px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Thanks so much!</DialogTitle>
|
||||
<DialogDescription>
|
||||
You're now on the waiting list. We'll let you know when we're ready.
|
||||
Should be within a month or two 🚀
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<DialogFooter>
|
||||
<Button onClick={() => setOpen(false)}>Got it!</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
);
|
||||
|
||||
const renderForm = () => (
|
||||
<DialogContent className="sm:max-w-[425px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Almost there!</DialogTitle>
|
||||
<DialogDescription>
|
||||
Enter your email to join the waiting list. We'll let you know when
|
||||
we're ready.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
fetch('/api/waitlist', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ email: value }),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}).then((res) => {
|
||||
if (res.ok) {
|
||||
setSuccess(true);
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
<div className="relative w-full">
|
||||
<input
|
||||
placeholder="Enter your email"
|
||||
className={cn(
|
||||
'border border-slate-100 rounded-md shadow-sm bg-white h-12 w-full px-4 outline-none focus:ring-1 ring-black text-blue-darker',
|
||||
className
|
||||
)}
|
||||
value={value}
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
/>
|
||||
<Button type="submit" className="absolute right-1 top-1">
|
||||
Join waitlist
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</DialogContent>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
{success ? renderSuccess() : renderForm()}
|
||||
</Dialog>
|
||||
<div className="flex gap-4">
|
||||
<Button
|
||||
size="lg"
|
||||
className="text-lg h-12"
|
||||
onClick={() => {
|
||||
setOpen(true);
|
||||
}}
|
||||
>
|
||||
Join waitlist now
|
||||
</Button>
|
||||
<Link
|
||||
href="https://dashboard.openpanel.dev/share/overview/ZQsEhG"
|
||||
target="_blank"
|
||||
rel="nofollow"
|
||||
>
|
||||
<Button size="lg" variant="outline" className="text-lg h-12">
|
||||
Demo
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -2,7 +2,6 @@ import { cn } from '@/utils/cn';
|
||||
|
||||
import '@/styles/globals.css';
|
||||
|
||||
import { Logo } from '@/components/Logo';
|
||||
import type { Metadata } from 'next';
|
||||
import { Bricolage_Grotesque } from 'next/font/google';
|
||||
import Script from 'next/script';
|
||||
@@ -36,13 +35,6 @@ export default function RootLayout({
|
||||
font.className
|
||||
)}
|
||||
>
|
||||
<div className="absolute top-0 left-0 right-0 py-6 z-10">
|
||||
<div className="container">
|
||||
<div className="flex justify-between">
|
||||
<Logo />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="w-full h-screen text-blue-950 bg-[radial-gradient(circle_at_2px_2px,#D9DEF6_2px,transparent_0)] absolute top-0 left-0 right-0 z-0"
|
||||
style={{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
const title = 'Openpanel.dev | An open-source alternative to Mixpanel';
|
||||
const title = 'An open-source alternative to Mixpanel | Openpanel.dev';
|
||||
const description =
|
||||
'Unlock actionable insights effortlessly with Insightful, the open-source analytics library that combines the power of Mixpanel with the simplicity of Plausible. Enjoy a unified overview, predictable pricing, and a vibrant community. Join us in democratizing analytics today!';
|
||||
|
||||
|
||||
@@ -13,13 +13,8 @@ export default async function Page() {
|
||||
return (
|
||||
<div>
|
||||
<Hero waitlistCount={waitlistCount} />
|
||||
<div className="bg-gradient-to-b from-blue-light to-[#FFFFFF] pb-16 text-center">
|
||||
<div className="relative -top-20">
|
||||
<PreviewCarousel />
|
||||
</div>
|
||||
</div>
|
||||
<div className="container">
|
||||
<div className="mb-24">
|
||||
<div className="my-24">
|
||||
<Heading2 className="md:text-5xl mb-2 leading-none">
|
||||
Analytics should be easy
|
||||
<br />
|
||||
|
||||
@@ -10,29 +10,23 @@ import {
|
||||
CheckCircle,
|
||||
ClockIcon,
|
||||
CloudIcon,
|
||||
CloudLightning,
|
||||
CloudLightningIcon,
|
||||
CompassIcon,
|
||||
ConeIcon,
|
||||
DatabaseIcon,
|
||||
DollarSignIcon,
|
||||
DownloadIcon,
|
||||
FileIcon,
|
||||
FilterIcon,
|
||||
FolderIcon,
|
||||
FolderOpenIcon,
|
||||
HandCoinsIcon,
|
||||
HandshakeIcon,
|
||||
KeyIcon,
|
||||
PieChartIcon,
|
||||
PointerIcon,
|
||||
RouteIcon,
|
||||
ServerIcon,
|
||||
ShieldPlusIcon,
|
||||
ShoppingCartIcon,
|
||||
SquareUserRound,
|
||||
StarIcon,
|
||||
ThumbsUp,
|
||||
ThumbsUpIcon,
|
||||
TrendingUpIcon,
|
||||
UserRoundSearchIcon,
|
||||
@@ -40,23 +34,11 @@ import {
|
||||
WebhookIcon,
|
||||
} from 'lucide-react';
|
||||
|
||||
import {
|
||||
Blob1,
|
||||
Blob2,
|
||||
Blob3,
|
||||
Blob4,
|
||||
Blob5,
|
||||
Blob6,
|
||||
Blob7,
|
||||
Blob8,
|
||||
Blob9,
|
||||
} from './blob';
|
||||
import { Heading2, Lead2 } from './copy';
|
||||
import { Widget } from './widget';
|
||||
|
||||
interface SectionItem {
|
||||
title: string;
|
||||
description: string;
|
||||
description: string | React.ReactNode;
|
||||
icon: LucideIcon;
|
||||
color: string;
|
||||
soon?: string;
|
||||
@@ -67,8 +49,18 @@ interface SectionItem {
|
||||
const sections: SectionItem[] = [
|
||||
{
|
||||
title: 'Own Your Own Data',
|
||||
description:
|
||||
'Take control of your data privacy and ownership with our platform, ensuring full transparency and security.',
|
||||
description: (
|
||||
<>
|
||||
<p>
|
||||
Take control of your data privacy and ownership with our platform,
|
||||
ensuring full transparency and security.
|
||||
</p>
|
||||
<p>
|
||||
All our serveres are hosted in EU (Stockholm) and we are fully GDPR
|
||||
compliant.
|
||||
</p>
|
||||
</>
|
||||
),
|
||||
icon: KeyIcon,
|
||||
color: '#2563EB',
|
||||
icons: [FolderIcon, DatabaseIcon, ShieldPlusIcon, KeyIcon],
|
||||
@@ -76,8 +68,12 @@ const sections: SectionItem[] = [
|
||||
},
|
||||
{
|
||||
title: 'Cloud or Self-Hosting',
|
||||
description:
|
||||
'Choose between the flexibility of cloud-based hosting or the autonomy of self-hosting to tailor your analytics infrastructure to your needs.',
|
||||
description: (
|
||||
<p>
|
||||
Choose between the flexibility of cloud-based hosting or the autonomy of
|
||||
self-hosting to tailor your analytics infrastructure to your needs.
|
||||
</p>
|
||||
),
|
||||
icon: CloudIcon,
|
||||
color: '#ff7557',
|
||||
icons: [CloudIcon, CheckCircle, ServerIcon, DownloadIcon],
|
||||
@@ -85,8 +81,12 @@ const sections: SectionItem[] = [
|
||||
},
|
||||
{
|
||||
title: 'Real-Time Events',
|
||||
description:
|
||||
'Stay up-to-date with real-time event tracking, enabling instant insights into user actions as they happen.',
|
||||
description: (
|
||||
<p>
|
||||
Stay up-to-date with real-time event tracking, enabling instant insights
|
||||
into user actions as they happen.
|
||||
</p>
|
||||
),
|
||||
icon: ClockIcon,
|
||||
color: '#7fe1d8',
|
||||
icons: [CloudLightningIcon, ShoppingCartIcon, ArrowUpFromDotIcon],
|
||||
@@ -94,8 +94,13 @@ const sections: SectionItem[] = [
|
||||
},
|
||||
{
|
||||
title: 'Deep Dive into User Behaviors',
|
||||
description:
|
||||
"Gain profound insights into user behavior with comprehensive analytics tools, allowing you to understand your audience's actions and preferences.",
|
||||
description: (
|
||||
<p>
|
||||
Gain profound insights into user behavior with comprehensive analytics
|
||||
tools, allowing you to understand your audience's actions and
|
||||
preferences.
|
||||
</p>
|
||||
),
|
||||
icon: UserRoundSearchIcon,
|
||||
color: '#f8bc3c',
|
||||
icons: [UsersIcon, RouteIcon, BookmarkIcon],
|
||||
@@ -103,8 +108,12 @@ const sections: SectionItem[] = [
|
||||
},
|
||||
{
|
||||
title: 'Powerful Report Explorer',
|
||||
description:
|
||||
'Explore and analyze your data effortlessly with our powerful report explorer, simplifying the process of deriving meaningful insights.',
|
||||
description: (
|
||||
<p>
|
||||
Explore and analyze your data effortlessly with our powerful report
|
||||
explorer, simplifying the process of deriving meaningful insights.
|
||||
</p>
|
||||
),
|
||||
icon: CompassIcon,
|
||||
color: '#b3596e',
|
||||
icons: [ThumbsUpIcon, TrendingUpIcon, PieChartIcon, BarChart2Icon],
|
||||
@@ -113,8 +122,12 @@ const sections: SectionItem[] = [
|
||||
{
|
||||
soon: 'Coming soon',
|
||||
title: 'Funnels',
|
||||
description:
|
||||
'Track user conversion funnels seamlessly, providing valuable insights into user journey optimization.',
|
||||
description: (
|
||||
<p>
|
||||
Track user conversion funnels seamlessly, providing valuable insights
|
||||
into user journey optimization.
|
||||
</p>
|
||||
),
|
||||
icon: ConeIcon,
|
||||
color: '#72bef4',
|
||||
icons: [ConeIcon, FilterIcon],
|
||||
@@ -123,8 +136,13 @@ const sections: SectionItem[] = [
|
||||
{
|
||||
soon: 'Coming with our native app',
|
||||
title: 'Push Notifications',
|
||||
description:
|
||||
'Stay informed about conversions, events, and peaks with our upcoming push notification tool, empowering you to monitor and respond to critical activities in real-time.',
|
||||
description: (
|
||||
<p>
|
||||
Stay informed about conversions, events, and peaks with our upcoming
|
||||
push notification tool, empowering you to monitor and respond to
|
||||
critical activities in real-time.
|
||||
</p>
|
||||
),
|
||||
icon: BellIcon,
|
||||
color: '#ffb27a',
|
||||
icons: [WebhookIcon, BellIcon],
|
||||
@@ -132,8 +150,12 @@ const sections: SectionItem[] = [
|
||||
},
|
||||
{
|
||||
title: 'Cost-Effective Alternative to Mixpanel',
|
||||
description:
|
||||
'Enjoy the same powerful analytics capabilities as Mixpanel at a fraction of the cost, ensuring affordability without compromising on quality.',
|
||||
description: (
|
||||
<p>
|
||||
Enjoy the same powerful analytics capabilities as Mixpanel at a fraction
|
||||
of the cost, ensuring affordability without compromising on quality.
|
||||
</p>
|
||||
),
|
||||
icon: DollarSignIcon,
|
||||
color: '#0f7ea0',
|
||||
icons: [DollarSignIcon, HandCoinsIcon, HandshakeIcon, StarIcon],
|
||||
@@ -142,8 +164,13 @@ const sections: SectionItem[] = [
|
||||
{
|
||||
soon: 'Something Plausible lacks',
|
||||
title: 'Great Support for React Native',
|
||||
description:
|
||||
'Benefit from robust support for React Native, ensuring seamless integration and compatibility for your projects, a feature notably lacking in other platforms like Plausible.',
|
||||
description: (
|
||||
<p>
|
||||
Benefit from robust support for React Native, ensuring seamless
|
||||
integration and compatibility for your projects, a feature notably
|
||||
lacking in other platforms like Plausible.
|
||||
</p>
|
||||
),
|
||||
icon: (({ className }: LucideProps) => {
|
||||
return (
|
||||
<img src="/react-native.svg" alt="React Native" className={className} />
|
||||
@@ -151,7 +178,7 @@ const sections: SectionItem[] = [
|
||||
}) as unknown as LucideIcon,
|
||||
color: '#3ba974',
|
||||
icons: [FolderIcon, DatabaseIcon, ShieldPlusIcon, KeyIcon],
|
||||
className: 'bg-[#f8bc3c]',
|
||||
className: 'bg-[#e19900]',
|
||||
},
|
||||
];
|
||||
|
||||
@@ -187,7 +214,7 @@ export function Sections() {
|
||||
: ['-top-10 -left-20 rotate-12', 'top-10 -rotate-12', '-right-5'];
|
||||
|
||||
const className = even
|
||||
? cn('text-white [&_h3]:text-white col-span-2', section.className)
|
||||
? cn('[&_*]:text-white/90 col-span-2', section.className)
|
||||
: cn('border border-border', section.className);
|
||||
|
||||
return (
|
||||
@@ -198,7 +225,7 @@ export function Sections() {
|
||||
icons={section.icons}
|
||||
offsets={offsets}
|
||||
>
|
||||
<p>{section.description}</p>
|
||||
{section.description}
|
||||
</Widget>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -22,12 +22,12 @@ export function Widget({
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'p-10 rounded-xl relative overflow-hidden flex flex-col hover:scale-105 transition-all duration-300 ease-in-out bg-white hover:shadow min-h-[300px] max-md:col-span-3',
|
||||
'p-10 rounded-xl relative overflow-hidden flex flex-col hover:scale-[101%] transition-all duration-300 ease-in-out bg-white hover:shadow min-h-[300px] max-md:col-span-3',
|
||||
className
|
||||
)}
|
||||
>
|
||||
<Heading3 className="mb-4">{title}</Heading3>
|
||||
{children}
|
||||
<Heading3 className="mb-2">{title}</Heading3>
|
||||
<div className="prose-xl">{children}</div>
|
||||
<div className="flex justify-between mt-auto">
|
||||
{icons.map((Icon, i) => (
|
||||
<Icon
|
||||
|
||||
Reference in New Issue
Block a user