mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-30 14:58:08 +00:00
Every word the landing, the header menus, the phone sheet, the footer,
the help corner and the way in (sign-in, sign-up, the reset, the
invitation, the callback, onboarding, the 404) write goes through
@hanzo/i18n's t(): one sentence is one message, its figures, names and
links arguments; copy written as data is phrase('…') where written and
t(label) where drawn. Plans, the chart, the API states and the lines
lib/kai and lib/enso build for the landing do the same.
Right to left: <html lang dir> is set before the first paint (the
package's bootScript first in the head); a figure, clock, range or
price outside a sentence is isolated; sides are logical gui props, the
layout's [data-join] corners logical, a bar grows from the start;
arrows along the line mirror (Onward for "words ->" links). The repo
dates read in the reader's language.
Gates select by slot, never by an English label (rtl, help). New gate
translated.spec.ts: in Arabic the landing at 1440 and 390, every menu
open, the phone sheet, sign-in, sign-up and the app signed in hold no
authored English, nothing scrolls sideways, every range is isolated;
French the same for English's commonest words. What another package
draws with no words prop is listed by name (@hanzo/ui's Footer label,
@hanzo/build's composer row and suggestions).
@hanzo/i18n 0.5.3 (the catalogs: 1,443 keys, every offered locale
complete), @hanzo/app 0.2.1 (the app's words). The link gate imports
the terms versions from @hanzo/app, the one SDK main now resolves.
66 lines
2.4 KiB
TypeScript
66 lines
2.4 KiB
TypeScript
'use client'
|
|
|
|
import Link from 'next/link'
|
|
import { useI18n } from '@hanzo/i18n/react'
|
|
import { Text, View, YStack } from '@hanzo/ui'
|
|
import { featured, href, stories } from '@/lib/data/customers'
|
|
|
|
/**
|
|
* Trusted by: every company whose launch Hanzo ran (lib/data/customers, the
|
|
* ones with a wordmark), drawn in the page's ink and scrolling slowly past,
|
|
* each mark opening its story on /customers. The strip is drawn twice so the
|
|
* loop has no seam (`[data-marquee]` in app/layout.tsx); the second copy is
|
|
* hidden from assistive tech. It stops under the pointer and, for a reader who
|
|
* asked for less motion, stands still as one wrapped row. The track runs left
|
|
* to right in every language (`data-i18n-ltr`): it slides left by half its
|
|
* width, and a right-to-left row would slide its marks off their own end.
|
|
*/
|
|
|
|
export const BRANDS = [...featured, ...stories]
|
|
.filter((s) => s.logo)
|
|
.map((s) => ({ id: s.id, name: s.company, logo: s.logo!, href: href(s) }))
|
|
|
|
export function Strip({ copy, height = 28 }: { copy?: boolean; height?: number }) {
|
|
const { t } = useI18n()
|
|
return (
|
|
<View flexDirection="row" items="center" gap={56} pr={56} aria-hidden={copy || undefined}>
|
|
{BRANDS.map((b) => (
|
|
<View
|
|
key={b.id}
|
|
render={<Link href={b.href} aria-label={b.name} tabIndex={copy ? -1 : undefined} />}
|
|
shrink={0}
|
|
opacity={0.65}
|
|
hoverStyle={{ opacity: 1 }}
|
|
filter="brightness(0) invert(1)"
|
|
$theme-light={{ filter: 'brightness(0)' }}
|
|
>
|
|
<View render={<img src={b.logo} alt={t('{name} logo', { name: b.name })} loading="lazy" />} display="block" height={height} width="auto" $platform-web={{ maxWidth: '160px', objectFit: 'contain' }} />
|
|
</View>
|
|
))}
|
|
</View>
|
|
)
|
|
}
|
|
|
|
export function TrustedMarquee({ height = 28 }: { height?: number }) {
|
|
return (
|
|
<View data-marquee-wrap="" data-i18n-ltr="" width="100%" overflow="hidden">
|
|
<View data-marquee="">
|
|
<Strip height={height} />
|
|
<Strip copy height={height} />
|
|
</View>
|
|
</View>
|
|
)
|
|
}
|
|
|
|
export default function Trusted() {
|
|
const { t } = useI18n()
|
|
return (
|
|
<YStack render="section" aria-label={t('Trusted by')} width="100%" maxW={1240} mx="auto" px="var(--page-gutter)" py={40} gap={20} $md={{ py: 56 }}>
|
|
<Text fontFamily="$mono" fontSize="$1" color="var(--muted-foreground)" text="center">
|
|
{t('Trusted by')}
|
|
</Text>
|
|
<TrustedMarquee />
|
|
</YStack>
|
|
)
|
|
}
|