Files
zeekayandGitHub 949608fd9d i18n: pages and components c–l draw their copy through @hanzo/i18n (#67)
Calculator, captable, careers, cloud, compare, contact and contact-sales,
customers, dashboards, dataroom, datastore, dev, download, enso,
enterprise, extension, family, integrations, investors, kai, learn,
legal and their components say their words through t() in client code,
<T> in server components and phrase() where copy is held as data; one
sentence is one key, figures and links its arguments.

Rebased on main: in every conflict main's file stands (its copy, layout
and styling) and only the wrapping is added back, including copy main
added since.

Claude-Session: https://claude.ai/code/session_015XYKLjZtDTJEnMo4eK3AFH
2026-10-30 01:15:31 -07:00

94 lines
2.9 KiB
TypeScript

'use client'
/**
* The map at the foot of a family page: its models, its benchmarks, its
* writing. The page passes the links. This file owns none of them, and it
* does not point at another family — that comparison is /models.
*
* A row is the link. The arrow is drawn here, so a label does not carry one.
*/
import { ArrowRight, ArrowUpRight } from 'lucide-react'
import { useI18n } from '@hanzo/i18n/react'
import { Band, Card, Head } from '@/components/marketing/band'
import { Text, XStack, YStack } from '@hanzo/ui'
import { Grid } from '@hanzo/ui/grid'
export type ReadLink = { href: string; label: string; note?: string }
export type ReadGroup = { label: string; links: readonly ReadLink[] }
function labelOf(label: string) {
return label.endsWith(' →') ? label.slice(0, -2) : label
}
function Row({ link }: { link: ReadLink }) {
const { t } = useI18n()
const external = link.href.startsWith('http')
return (
<XStack
render={
<a
href={link.href}
{...(external ? { target: '_blank', rel: 'noreferrer noopener' } : {})}
/>
}
group
width="100%"
items="center"
gap="$3"
px="$5"
py="$3"
borderTopWidth={1}
borderColor="var(--border)"
hoverStyle={{ bg: '$hover' }}
focusVisibleStyle={{ outlineWidth: 2, outlineStyle: 'solid', outlineColor: 'var(--ring)', outlineOffset: -2 }}
$touchable={{ minH: 44 }}
$platform-web={{ textDecoration: 'none', transition: 'background-color var(--duration-fast) var(--ease-in-out)' }}
>
<YStack flex={1} minW={0} gap="$1">
<Text fontSize="$3" lineHeight="$3" fontWeight="500" color="$ink">
{labelOf(t(link.label))}
</Text>
{link.note ? (
<Text fontSize="$2" lineHeight="$2" color="var(--text-secondary)">
{t(link.note)}
</Text>
) : null}
</YStack>
<Text display="inline-flex" color="var(--muted-foreground)" $group-hover={{ color: '$ink' }} $group-focusVisible={{ color: '$ink' }}>
{external ? <ArrowUpRight size={16} data-i18n-mirror /> : <ArrowRight size={16} data-i18n-mirror />}
</Text>
</XStack>
)
}
export function Read({
eyebrow,
title,
lede,
groups,
}: {
eyebrow: string
title: string
lede: string
groups: readonly ReadGroup[]
}) {
const { t } = useI18n()
return (
<Band measure={1024}>
<Head eyebrow={t(eyebrow)} title={t(title)} lede={t(lede)} />
<Grid columns={{ min: 280, max: 3 }} gap={16} items="flex-start">
{groups.map((group) => (
<Card key={group.label} p={0} overflow="hidden">
<Text render="h3" px="$5" pt="$5" pb="$4" fontSize="$4" lineHeight="$4" fontWeight="600" color="$ink">
{t(group.label)}
</Text>
{group.links.map((link) => (
<Row key={link.href + link.label} link={link} />
))}
</Card>
))}
</Grid>
</Band>
)
}