Files
hanzo.ai/components/commerce/UseCases.tsx
zeekay 3e01d979b7 Light theme: the black bands and white page ink that made /payments, /analytics, /commerce and /kai/guide unreadable follow the theme
INK, the band that drops to true black on dark, stayed black on a light
page while its headings and paragraphs took the light theme's dark ink:
1.06:1 on /payments, /analytics and /kai/guide, and /commerce's use
cases sat on --black the same way. INK is light-dark() now, the quiet
card surface on light and pure black on dark as before, and /commerce
and /kai/guide name it instead of a black of their own. Sixteen page
roots inked their whole tree --white, so anything that inherits (card
headings, bullets) was white on light; they take --foreground, the ink
the rest of the site is set in. Measured with a contrast walk over
fourteen of those pages in light: 57 runs under 3:1 on /analytics and
13 on /payments before, 7 left across all of them (chart labels and two
install chips), and none new on dark.
2026-10-18 17:07:46 -07:00

112 lines
4.0 KiB
TypeScript

import React from "react";
import { Box, XStack, YStack } from '@/components/ui'
import { Band, Head, INK, Words } from '@/components/marketing/band'
// The `color` prop is gone. It was interpolated into class names
// (`text-${color}-400`, `from-${color}-500/20`), and Tailwind only generates a
// utility it can SEE spelled out in the source — an interpolated name is never
// spelled out, so those three classes emitted no rule and the amber accent has
// never rendered. The brand is monochrome anyway; these are now the site's own
// foreground tokens, written literally.
//
// NO TAILWIND. The section frame is `Band` + `Head`; the cards and the wash
// are gui props. "Learn more" is `More`, @hanzo/ui's quiet link — the
// hover it used to carry asked for the ink it already rested at, so nothing is
// lost and the role's own lift replaces it.
interface UseCaseCardProps {
title: string;
description: string;
image?: string;
index: number;
}
/** The frame each card is cut from. */
const EDGE = {
position: 'relative',
overflow: 'hidden',
height: '100%',
borderWidth: 1,
borderColor: 'var(--neutral-800)',
borderRadius: 'var(--radius-xl)',
} as const
/** The wash over the numeral. */
const WASH = 'linear-gradient(to bottom right in oklab, color-mix(in oklab, var(--pure-white) 10%, transparent), transparent)'
const UseCaseCard = ({ title, description, image, index }: UseCaseCardProps) => {
return (
<YStack {...EDGE} $md={{ flexDirection: index % 2 === 0 ? 'row-reverse' : 'row' }}>
<YStack width="100%" p="$6" justify="center" $md={{ width: '50%' }}>
<Words render="h3" mb={16} fontSize="$7" lineHeight="1.3333" weight="700" color="var(--foreground)">{title}</Words>
<Words render="p" mb={16} color="var(--muted-foreground)">{description}</Words>
</YStack>
<Box
position="relative"
overflow="hidden"
width="100%"
height={192}
bg="$panel"
$md={{ width: '50%', height: 'auto' }}
>
<Box position="absolute" t={0} r={0} b={0} l={0} opacity={0.5} backgroundImage={WASH} />
<XStack
position="absolute"
t={0}
r={0}
b={0}
l={0}
items="center"
justify="center"
>
{/* The display ramp's top rung, which gui's size table does not carry. */}
<Words fontSize="var(--text-9xl)" lineHeight="1" weight="700" opacity={0.1} color="var(--foreground)">{index + 1}</Words>
</XStack>
</Box>
</YStack>
);
};
const UseCases = () => {
const useCases = [
{
title: "A storefront you designed",
description: "You have a front end you like and do not want a theme system telling you how it works. This gives you the catalogue, the cart and the checkout as calls, and stays out of the rendering."
},
{
title: "One catalogue, several stores",
description: "A brand with regional shops, or an agency running stores for clients. Stores are scoped by organisation and can price the same product differently."
},
{
title: "Selling a subscription and a thing",
description: "Hardware with a plan, or a course with a membership. Orders and subscriptions are in one service, so the customer has one history rather than two accounts."
},
{
title: "Taking payment inside your own product",
description: "You are not building a shop, you are charging for what you already made. Use the checkout, the invoices and the meters, and ignore the rest."
}
];
return (
<Band rule={false} pad="$10" ground={INK}>
<Head
title="When to reach for it"
lede="Four shapes that fit. If none of them is yours, a hosted shop is probably the easier answer."
/>
<YStack gap="$6">
{useCases.map((useCase, index) => (
<UseCaseCard
key={index}
title={useCase.title}
description={useCase.description}
index={index}
/>
))}
</YStack>
</Band>
);
};
export default UseCases;