mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-28 07:32:52 +00:00
Every import, the catalog pin and the docs name @hanzo/app and github.com/hanzoai/app. lib/rooms.ts is lib/app.ts and components/rooms.tsx is components/app.tsx.
84 lines
2.5 KiB
TypeScript
84 lines
2.5 KiB
TypeScript
'use client'
|
|
|
|
import React from "react";
|
|
import { motion } from "@/components/motion";
|
|
import { LucideIcon } from "lucide-react";
|
|
import { BrandColor } from "@/lib/constants/brand";
|
|
import { Text, XStack } from '@hanzo/ui'
|
|
import { Leaf, Line } from '@hanzo/ui/marketing'
|
|
import { mix } from '@hanzo/app/lib/mix'
|
|
|
|
/**
|
|
* NO TAILWIND AND NO STYLE OBJECTS. The card is `Leaf` — @hanzo/ui's component
|
|
* for a card whose edge lifts on approach — and everything inside it is gui
|
|
* props over @hanzo/design's tokens.
|
|
*/
|
|
|
|
interface FeatureCardProps {
|
|
title: string;
|
|
description: string;
|
|
icon: LucideIcon;
|
|
color?: BrandColor;
|
|
delay?: number;
|
|
}
|
|
|
|
/** The card's edge at rest and on approach, and the room inside it. */
|
|
const LEAF = {
|
|
p: 24,
|
|
borderColor: 'var(--border)',
|
|
hoverStyle: { borderColor: 'color-mix(in oklab, var(--pure-white) 40%, transparent)' },
|
|
} as const
|
|
|
|
/** The wash behind it, at the two strengths `color` picks between. */
|
|
const wash = (to: string): string =>
|
|
`linear-gradient(to bottom right in oklab, color-mix(in oklab, var(--pure-white) 20%, transparent), ${to})`
|
|
|
|
const PRIMARY = wash('color-mix(in oklab, var(--pure-white) 5%, transparent)')
|
|
const SECONDARY = wash('color-mix(in oklab, var(--pure-white) 10%, transparent)')
|
|
|
|
/**
|
|
* Reusable feature card with consistent brand styling.
|
|
* Use this instead of duplicating card patterns across components.
|
|
*/
|
|
const FeatureCard: React.FC<FeatureCardProps> = ({
|
|
title,
|
|
description,
|
|
icon: Icon,
|
|
color = "primary",
|
|
delay = 0,
|
|
}) => {
|
|
const isPrimary = color === "primary";
|
|
|
|
return (
|
|
<Leaf
|
|
render={<motion.div initial={{ opacity: 0, y: 20 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} transition={{ duration: 0.4, delay }} />}
|
|
backgroundImage={isPrimary ? PRIMARY : SECONDARY}
|
|
{...LEAF}
|
|
>
|
|
<XStack
|
|
items="center"
|
|
justify="center"
|
|
width={48}
|
|
height={48}
|
|
mb="$4"
|
|
rounded="var(--radius-lg)"
|
|
bg="$raised"
|
|
>
|
|
<Icon
|
|
width={24}
|
|
height={24}
|
|
color={isPrimary ? 'var(--foreground)' : 'color-mix(in oklab, var(--foreground) 70%, transparent)'}
|
|
/>
|
|
</XStack>
|
|
<Line render="h3" display="block" mb={8} fontSize="$6" fontWeight="700" color="var(--white)" lineHeight="1.4">
|
|
{title}
|
|
</Line>
|
|
<Text render="p" display="block" color={mix('var(--foreground)', 80)}>
|
|
{description}
|
|
</Text>
|
|
</Leaf>
|
|
);
|
|
};
|
|
|
|
export default FeatureCard;
|