Files
hanzo.ai/components/product/ProductArticlesSection.tsx
wbin90620 3d377818fa The creative studios and the pages added since Oct 16 name the design system's values, so the design gate passes again
CI/CD's test job has failed on main since Oct 16: hanzo-design-lint counted
154 raw colours, 159 style objects, 8 bare z-indexes and 2 all-caps labels
past hanzo-design.allow.json, almost all in the creative studio surface, the
creative model registry, /research-access and the product sections. The
allowance is untouched; the code now meets it.

- Colour is a token. Each creative model's accent is one of @hanzo/design's
  art palette (tide, iris, ember); stems, faders and axes read the same art
  and state tokens; red/amber/green window dots are --chrome-dot-*; every
  black or white wash is --pure-black/--pure-white mixed to a share. The
  unused accentRgb field is gone with its literals.
- Style is gui props. style={} objects became @hanzo/gui props; buttons and
  links are gui stacks rendering the native element; /research-access's
  fields are @hanzo/ui Input, Textarea and Picker. What props cannot say
  (keyframes, a media element's fill and filter, an SVG overlay's place in
  the stack) sits in the studio's existing style block, keyed by data-*.
- Stacking is the ladder: the studio overlays and the beta strip's layers
  are var(--z-base) and calc() off it, the same order as before.
- "HANZO / ENSO" reads in sentence case, as the system's labels do, and the
  creative toggles' 11px labels are the 12px step of the type scale.

The next gate in line, audit-model-counts, also failed on two counts typed
on Oct 16: /playground's description says "every model in the catalog"
again, and Enso's model card draws <Models /> instead of "100+".

No feature is removed: every studio keeps its controls, state and motion.
The visible changes are the accent hues and a few pixels of spacing.
2026-10-20 02:24:15 -04:00

113 lines
3.5 KiB
TypeScript

'use client'
import { ExternalLink, BookOpen, ArrowRight } from 'lucide-react'
import { Box, Text, XStack, YStack } from '@hanzo/ui'
import { Grid } from '@hanzo/ui/grid'
import { Chip, Eyebrow, Lede, More, Title } from '@hanzo/ui/marketing'
import { Band, Card, Head } from '@/components/marketing/band'
import { Rise } from '@/components/marketing/rise'
export interface ArticleItem {
title: string
href: string
date?: string
category: string
description: string
external?: boolean
}
export interface ProductArticlesSectionProps {
id?: string
eyebrow?: string
title?: string
lede?: string
articles: ArticleItem[]
moreHref?: string
moreLabel?: string
}
export function ProductArticlesSection({
id = 'articles',
eyebrow = 'Research & Engineering',
title = 'Deep-dives, benchmarks, and architectural specifications.',
lede = 'Research, benchmarks and engineering posts from the Hanzo team.',
articles,
moreHref = 'https://hanzo.blog',
moreLabel = 'More on the Hanzo blog →',
}: ProductArticlesSectionProps) {
return (
<Band id={id} measure={1180}>
<Head
eyebrow={eyebrow}
title={title}
lede={lede}
loud
/>
<Grid columns={{ min: 300, max: 3 }} gap={16} mt="$6">
{articles.map((article, i) => (
<Rise key={article.href} beat={i}>
<YStack
render={
<a
href={article.href}
target={article.href.startsWith('http') ? '_blank' : undefined}
rel={article.href.startsWith('http') ? 'noreferrer' : undefined}
/>
}
height="100%"
$platform-web={{ textDecoration: 'none', color: 'inherit' }}
>
<Card
display="flex"
flexDirection="column"
justify="space-between"
gap="$4"
p="$6"
height="100%"
borderColor="var(--border)"
bg="$panel"
hoverStyle={{ borderColor: 'var(--border-strong)' }}
>
<YStack gap="$3">
<XStack justify="space-between" items="center">
<Chip px={10} py={3} fontSize="$1" lineHeight="$1">
{article.category}
</Chip>
{article.date && (
<Text fontFamily="$mono" fontSize="$1" lineHeight="$1" color="var(--muted-foreground)">
{article.date}
</Text>
)}
</XStack>
<Text render="h3" fontSize="$4" lineHeight="$4" fontWeight="600" color="$ink">
{article.title}
</Text>
<Text fontSize="$2" lineHeight="$3" color="var(--text-secondary)">
{article.description}
</Text>
</YStack>
<XStack items="center" gap="$1" mt="auto" pt="$3">
<Text fontSize="$2" lineHeight="$2" fontWeight="600" color="var(--primary)">
Read the article
</Text>
<ArrowRight size={14} color="var(--primary)" />
</XStack>
</Card>
</YStack>
</Rise>
))}
</Grid>
{moreHref && (
<XStack mt="$6" justify="center">
<More href={moreHref}>{moreLabel}</More>
</XStack>
)}
</Band>
)
}