mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-29 07:20:43 +00:00
141 lines
4.8 KiB
TypeScript
141 lines
4.8 KiB
TypeScript
'use client'
|
|
|
|
/**
|
|
* /analytics is one page in fifteen files, and every one of them drew the same
|
|
* tile, the same disc, the same dashboard chrome and the same five lines of
|
|
* type. They are stated once here.
|
|
*
|
|
* The tile is a 16px corner over a faint ground at 32px of padding — not
|
|
* `components/marketing/band`'s `Card` (24px corner, 28px padding) and not
|
|
* /payments' (12px corner, 24px padding). Three pages, three card designs, and
|
|
* folding them would change the picture on two of them. It is BUILT on `Card`
|
|
* so the edge and the ink stay one decision.
|
|
*
|
|
* Each leading is design's token for its rung, in rem (`--leading-xl` 1.55,
|
|
* `--leading-lg` 1.4, `--leading-base` 1.35, `--leading-sm` 1.15,
|
|
* `--leading-xs` 1), because gui takes no var() for a line height.
|
|
*/
|
|
|
|
import type { ComponentProps, ReactNode } from 'react'
|
|
import { Card } from '@/components/marketing/band'
|
|
import { Box, Text, View, XStack } from '@hanzo/ui'
|
|
|
|
type TextProps = ComponentProps<typeof Text>
|
|
|
|
export function Tile(props: ComponentProps<typeof Card>) {
|
|
return <Card rounded="var(--radius-xl)" bg="$panel" p="$6" {...props} />
|
|
}
|
|
|
|
/** A panel of the dashboard mock — a screen inside a screen. */
|
|
export function Panel({ children, ...rest }: ComponentProps<typeof Box>) {
|
|
return (
|
|
<Box rounded="var(--radius-lg)" bg="$hover" p="$4" {...rest}>
|
|
{children}
|
|
</Box>
|
|
)
|
|
}
|
|
|
|
/** A share of the foreground or of pure white, for the inks and washes this page dims by degree. */
|
|
export const ink = (pct: number) => `color-mix(in oklab, var(--foreground) ${pct}%, transparent)`
|
|
export const tint = (pct: number) => `color-mix(in oklab, var(--primary) ${pct}%, transparent)`
|
|
|
|
/** A tile's heading. */
|
|
export function Title({ children, ...p }: { children: ReactNode } & Omit<TextProps, 'children'>) {
|
|
return (
|
|
<Text render="h3" display="block" mb={12} fontSize="$6" lineHeight="1.55rem" fontWeight="700" color="inherit" {...p}>
|
|
{children}
|
|
</Text>
|
|
)
|
|
}
|
|
|
|
/** The paragraph under a tile's heading. */
|
|
export function Body({ children, ...p }: { children: ReactNode } & Omit<TextProps, 'children'>) {
|
|
return (
|
|
<Text render="p" display="block" fontSize="$3" lineHeight="1.35rem" color="var(--muted-foreground)" {...p}>
|
|
{children}
|
|
</Text>
|
|
)
|
|
}
|
|
|
|
/** A smaller heading inside a panel: the 15px rung at its leading. */
|
|
export function Sub({ children, ...p }: { children: ReactNode } & Omit<TextProps, 'children'>) {
|
|
return (
|
|
<Text render="h4" display="block" fontSize="$4" lineHeight="1.4rem" fontWeight="500" color="inherit" {...p}>
|
|
{children}
|
|
</Text>
|
|
)
|
|
}
|
|
|
|
/** A label above a figure in the dashboard mock, the figure, and its change. */
|
|
export function Label({ children, ...p }: { children: ReactNode } & Omit<TextProps, 'children'>) {
|
|
return (
|
|
<Text display="block" fontSize="$2" lineHeight="1.15rem" color="var(--muted-foreground)" {...p}>
|
|
{children}
|
|
</Text>
|
|
)
|
|
}
|
|
|
|
export function Figure({ children, ...p }: { children: ReactNode } & Omit<TextProps, 'children'>) {
|
|
return (
|
|
<Text display="block" mt={4} fontSize="$7" lineHeight="1.75rem" fontWeight="700" color="var(--foreground)" {...p}>
|
|
{children}
|
|
</Text>
|
|
)
|
|
}
|
|
|
|
export function Delta({ children, ...p }: { children: ReactNode } & Omit<TextProps, 'children'>) {
|
|
return (
|
|
<Text display="block" mt={4} fontSize="$1" lineHeight="1rem" $platform-web={{ color: ink(70) }} {...p}>
|
|
{children}
|
|
</Text>
|
|
)
|
|
}
|
|
|
|
/** A three-dot window chrome. */
|
|
export function Dots() {
|
|
return (
|
|
<XStack gap={4}>
|
|
{[0, 1, 2].map((i) => (
|
|
<View key={i} render="span" width={12} height={12} rounded={9999} $platform-web={{ backgroundColor: tint(10) }} />
|
|
))}
|
|
</XStack>
|
|
)
|
|
}
|
|
|
|
/** A disc a mark sits on: an outline at full round, on the accent at `pct`. */
|
|
export function Disc({ pct = 10, pad = 12, edge = 'var(--white-30)', children }: { pct?: number; pad?: number; edge?: string; children: ReactNode }) {
|
|
return (
|
|
<XStack
|
|
render="span"
|
|
display="inline-flex"
|
|
items="center"
|
|
p={pad}
|
|
rounded={9999}
|
|
borderWidth={edge === 'none' ? 0 : 1}
|
|
$platform-web={{ color: 'var(--foreground)', borderColor: edge === 'none' ? undefined : edge, backgroundColor: tint(pct) }}
|
|
>
|
|
{children}
|
|
</XStack>
|
|
)
|
|
}
|
|
|
|
/** A ruled backdrop behind a band: a decorative grid in the foreground ink at `opacity`. */
|
|
export function Lattice({ size, opacity }: { size: number; opacity: number }) {
|
|
const line = `color-mix(in srgb, var(--foreground) ${opacity * 100}%, transparent)`
|
|
return (
|
|
<View
|
|
aria-hidden
|
|
position="absolute"
|
|
t={0}
|
|
r={0}
|
|
b={0}
|
|
l={0}
|
|
pointerEvents="none"
|
|
$platform-web={{
|
|
backgroundImage: `linear-gradient(to right, ${line} 1px, transparent 1px), linear-gradient(to bottom, ${line} 1px, transparent 1px)`,
|
|
backgroundSize: `${size}px ${size}px`,
|
|
}}
|
|
/>
|
|
)
|
|
}
|