mirror of
https://github.com/hanzo-apps/gallery.git
synced 2026-10-30 14:48:38 +00:00
236 lines
9.0 KiB
TypeScript
236 lines
9.0 KiB
TypeScript
'use client';
|
|
|
|
import Image from 'next/image';
|
|
import { YStack, XStack, Text } from '@hanzo/ui';
|
|
import { Grid } from '@hanzo/ui/grid';
|
|
import {
|
|
Action,
|
|
Chip,
|
|
Claim,
|
|
Display,
|
|
Feature,
|
|
Lattice,
|
|
Leaf,
|
|
Lede,
|
|
Lift,
|
|
More,
|
|
Section,
|
|
Title,
|
|
} from '@hanzo/ui/marketing';
|
|
import { shot } from './lib/shot';
|
|
import { templates, CATEGORIES } from './templates-data';
|
|
import { c, round, lift } from './lib/design';
|
|
import { DOCS } from './lib/links';
|
|
import { Stars } from './components/stars';
|
|
|
|
type Template = (typeof templates)[number];
|
|
|
|
/** A template as a card: its picture, its name and stack, what it is for. */
|
|
function TemplateCard({ template }: { template: Template }) {
|
|
return (
|
|
<Leaf href={`/templates/${template.slug}`} p={12} bg={c.card} borderColor={c.edge} rounded={round.card} hoverStyle={{ borderColor: c.strong }}>
|
|
<YStack position="relative" aspectRatio={16 / 9} rounded={round.frame} overflow="hidden" bg={c.raised}>
|
|
<Image src={shot(template.screenshot)} alt={template.displayName} fill style={{ objectFit: 'cover' }} data-zoom="" />
|
|
</YStack>
|
|
<YStack gap={6} px={8} pt={16} pb={8}>
|
|
<XStack items="center" justify="space-between" gap={8}>
|
|
<Lift render="h3" m={0} fontSize="$6" lineHeight={24} fontWeight="500" color={c.ink}>
|
|
{template.displayName}
|
|
</Lift>
|
|
<Stars n={template.rating} size="$2" />
|
|
</XStack>
|
|
<Text fontSize="$2" lineHeight={18} color={c.muted}>
|
|
{template.framework}
|
|
</Text>
|
|
<Text fontSize="$2" lineHeight={18} color={c.faint}>
|
|
{template.useCase}
|
|
</Text>
|
|
<XStack gap={6} pt={6} flexWrap="wrap">
|
|
<Chip px={10} py={2}>
|
|
Tier {template.tier}
|
|
</Chip>
|
|
<Chip px={10} py={2}>
|
|
{template.category}
|
|
</Chip>
|
|
</XStack>
|
|
</YStack>
|
|
</Leaf>
|
|
);
|
|
}
|
|
|
|
/** A tile of the ruled stack grid. */
|
|
function Stack({ name, count }: { name: string; count: number }) {
|
|
return (
|
|
<YStack gap={4} p={24} bg={c.ground}>
|
|
<Text fontSize="$7" lineHeight={26} fontWeight="500" color={c.ink}>
|
|
{name}
|
|
</Text>
|
|
<Text fontSize="$2" lineHeight={18} color={c.muted}>
|
|
{count} templates
|
|
</Text>
|
|
</YStack>
|
|
);
|
|
}
|
|
|
|
/** What someone is building, and how many templates start there. */
|
|
function UseCase({ title, description, count }: { title: string; description: string; count: number }) {
|
|
return (
|
|
<Leaf href="/gallery" p={24} bg={c.card} borderColor={c.edge} rounded={round.card} hoverStyle={{ borderColor: c.strong }}>
|
|
<YStack gap={8}>
|
|
<Text fontSize="$6" lineHeight={24} fontWeight="500" color={c.ink}>
|
|
{title}
|
|
</Text>
|
|
<Text fontSize="$3" lineHeight={22} color={c.muted}>
|
|
{description}
|
|
</Text>
|
|
<Lift step fontSize="$2" lineHeight={18} fontWeight="500" color={c.muted} pt={8}>
|
|
{count} templates →
|
|
</Lift>
|
|
</YStack>
|
|
</Leaf>
|
|
);
|
|
}
|
|
|
|
const has = (needle: string) => (s: string) => s.toLowerCase().includes(needle);
|
|
|
|
export default function GalleryHome() {
|
|
const featured = templates.filter((x) => x.tier === 1).slice(0, 6);
|
|
const components = templates.reduce((sum, x) => {
|
|
const m = x.components.match(/(\d+)/);
|
|
return sum + (m ? parseInt(m[1]) : 0);
|
|
}, 0);
|
|
const byFramework = (...needles: string[]) => templates.filter((x) => needles.some((n) => has(n)(x.framework))).length;
|
|
const byUse = (...needles: string[]) => templates.filter((x) => needles.some((n) => has(n)(x.useCase))).length;
|
|
|
|
const stats: [string, string][] = [
|
|
[String(templates.length), 'templates'],
|
|
[String(CATEGORIES.length), 'categories'],
|
|
[`${components}+`, 'components'],
|
|
];
|
|
|
|
return (
|
|
<YStack>
|
|
{/* Hero: the promise, the counts, the two ways in. */}
|
|
<YStack render="section" position="relative" overflow="hidden" px="var(--page-gutter)" pt={96} pb={88} $md={{ pt: 136, pb: 112 }}>
|
|
<Lattice
|
|
position="absolute"
|
|
t={0}
|
|
l={0}
|
|
r={0}
|
|
b={0}
|
|
size={56}
|
|
opacity={0.06}
|
|
style={{ maskImage: 'radial-gradient(ellipse 60% 70% at 50% 30%, #000 30%, transparent 100%)' }}
|
|
/>
|
|
<YStack position="relative" width="100%" maxW={880} mx="auto" items="center" gap={20}>
|
|
<Chip px={14} py={6} fontSize="$2">
|
|
gallery.hanzo.ai
|
|
</Chip>
|
|
<Display text="center">Hanzo Templates Gallery</Display>
|
|
<Claim text="center">Premium UI/UX templates for your next project</Claim>
|
|
<XStack flexWrap="wrap" justify="center" gap={8} items="center">
|
|
{stats.map(([n, label], i) => (
|
|
<XStack key={label} items="center" gap={8}>
|
|
{i > 0 ? (
|
|
<Text color={c.faint} fontSize="$3">
|
|
·
|
|
</Text>
|
|
) : null}
|
|
<Text fontSize="$3" lineHeight={22} color={c.muted}>
|
|
<Text fontSize="$3" fontWeight="500" color={c.ink}>
|
|
{n}
|
|
</Text>{' '}
|
|
{label}
|
|
</Text>
|
|
</XStack>
|
|
))}
|
|
<XStack items="center" gap={8}>
|
|
<Text color={c.faint} fontSize="$3">
|
|
·
|
|
</Text>
|
|
<Text fontSize="$3" lineHeight={22} color={c.muted}>
|
|
production ready
|
|
</Text>
|
|
</XStack>
|
|
</XStack>
|
|
<XStack flexWrap="wrap" justify="center" gap={12} pt={12}>
|
|
<Action fill href="/gallery">
|
|
Browse templates
|
|
</Action>
|
|
<Action href={DOCS}>Documentation</Action>
|
|
</XStack>
|
|
</YStack>
|
|
</YStack>
|
|
|
|
<Section title="Featured templates" lede="Our highest-rated Tier 1 templates" measure={1280}>
|
|
<Grid columns={{ min: 300, max: 3 }} gap={20}>
|
|
{featured.map((template) => (
|
|
<TemplateCard key={template.id} template={template} />
|
|
))}
|
|
</Grid>
|
|
<XStack justify="center">
|
|
<More href="/gallery">View all templates →</More>
|
|
</XStack>
|
|
</Section>
|
|
|
|
<Section title="Technology stacks" lede="Built with modern web technologies" measure={1280}>
|
|
<YStack rounded={round.card} overflow="hidden" borderWidth={1} borderColor={c.edge} bg={c.edge}>
|
|
<Grid columns={{ min: 220, max: 4 }} gap={1}>
|
|
<Stack name="Next.js" count={byFramework('next')} />
|
|
<Stack name="React" count={byFramework('react')} />
|
|
<Stack name="TypeScript" count={byFramework('typescript', 'ts')} />
|
|
<Stack name="HTML/CSS" count={byFramework('html', 'gulp')} />
|
|
</Grid>
|
|
</YStack>
|
|
</Section>
|
|
|
|
<Section title="Perfect for" lede="Whatever you are building, we have a template" measure={1280}>
|
|
<Grid columns={{ min: 280, max: 3 }} gap={20}>
|
|
<UseCase title="SaaS Startups" description="Launch faster with production-ready templates" count={byUse('saas')} />
|
|
<UseCase title="Creative Agencies" description="Beautiful portfolios and agency sites" count={byUse('portfolio', 'agency', 'creative')} />
|
|
<UseCase title="Mobile Apps" description="Modern app landing pages" count={byUse('app', 'mobile')} />
|
|
<UseCase title="Dashboards" description="Admin panels and analytics platforms" count={byUse('dashboard')} />
|
|
<UseCase title="E-commerce" description="Online stores and marketplaces" count={byUse('commerce', 'store')} />
|
|
<UseCase title="Social Platforms" description="Community and social networking" count={byUse('social')} />
|
|
</Grid>
|
|
</Section>
|
|
|
|
<Section title="Why choose Hanzo Templates" measure={1280}>
|
|
<Grid columns={{ min: 240, max: 4 }} gap={20}>
|
|
<Feature title="Lightning Fast">Built with Next.js 14+ for optimal performance and SEO</Feature>
|
|
<Feature title="Beautiful Design">Premium UI/UX from top designers worldwide</Feature>
|
|
<Feature title="Fully Responsive">Perfect on mobile, tablet, and desktop devices</Feature>
|
|
<Feature title="Easy to Customize">Clean code with TypeScript and modern best practices</Feature>
|
|
</Grid>
|
|
</Section>
|
|
|
|
{/* Closing: deploy, or keep browsing. */}
|
|
<YStack render="section" px="var(--page-gutter)" pt={32} pb={112}>
|
|
<YStack
|
|
width="100%"
|
|
maxW={1280}
|
|
mx="auto"
|
|
items="center"
|
|
gap={16}
|
|
px={24}
|
|
py={64}
|
|
rounded={round.card}
|
|
borderWidth={1}
|
|
borderColor={c.edge}
|
|
bg={c.card}
|
|
boxShadow={lift.card}
|
|
>
|
|
<Title text="center">Deploy instantly with Hanzo AI</Title>
|
|
<Lede text="center">One-click deployment to global edge network</Lede>
|
|
<XStack flexWrap="wrap" justify="center" gap={12} pt={12}>
|
|
<Action fill href="https://hanzo.ai">
|
|
Get started free
|
|
</Action>
|
|
<Action href="/gallery">Browse gallery</Action>
|
|
</XStack>
|
|
</YStack>
|
|
</YStack>
|
|
</YStack>
|
|
);
|
|
}
|