feat(landing): add audience tabs to hero
This commit is contained in:
parent
4cc68d34cf
commit
312ec968ef
|
|
@ -23,6 +23,7 @@ export interface SegmentTabProps<T = string> {
|
|||
inactiveClassName?: string
|
||||
size?: 'sm' | 'md' | 'lg'
|
||||
variant?: 'default' | 'compact'
|
||||
distribution?: 'fill' | 'fit'
|
||||
disabled?: boolean
|
||||
responsiveWrap?: boolean
|
||||
ref?: Ref<HTMLDivElement>
|
||||
|
|
@ -69,6 +70,7 @@ export function SegmentTab<T = string>({
|
|||
inactiveClassName,
|
||||
size = 'md',
|
||||
variant = 'default',
|
||||
distribution = 'fill',
|
||||
disabled = false,
|
||||
responsiveWrap = false,
|
||||
ref,
|
||||
|
|
@ -82,7 +84,8 @@ export function SegmentTab<T = string>({
|
|||
role="tablist"
|
||||
aria-disabled={disabled || undefined}
|
||||
className={cn(
|
||||
'container-type-[inline-size] relative flex w-full items-center',
|
||||
'container-type-[inline-size] relative flex items-center',
|
||||
distribution === 'fill' ? 'w-full' : 'w-fit max-w-full',
|
||||
styles.container,
|
||||
disabled && 'pointer-events-none opacity-60',
|
||||
containerClassName,
|
||||
|
|
@ -90,7 +93,8 @@ export function SegmentTab<T = string>({
|
|||
>
|
||||
<div
|
||||
className={cn(
|
||||
'relative flex w-full items-center',
|
||||
'relative flex items-center',
|
||||
distribution === 'fill' ? 'w-full' : 'w-fit max-w-full',
|
||||
responsiveWrap && 'flex-wrap',
|
||||
)}
|
||||
>
|
||||
|
|
@ -110,12 +114,14 @@ export function SegmentTab<T = string>({
|
|||
'focus-visible:ring-accent/30 focus:outline-none focus-visible:ring-2',
|
||||
sizeClass,
|
||||
styles.button,
|
||||
responsiveWrap
|
||||
? clsx(
|
||||
'@[0px]:flex-none @[0px]:basis-[calc(50%-0.125rem)] @[0px]:px-2',
|
||||
'@[420px]:flex-1 @[420px]:basis-auto @[420px]:px-3',
|
||||
)
|
||||
: clsx('flex-1', '@[0px]:px-2 @[420px]:px-3'),
|
||||
distribution === 'fit'
|
||||
? 'flex-none'
|
||||
: responsiveWrap
|
||||
? clsx(
|
||||
'@[0px]:flex-none @[0px]:basis-[calc(50%-0.125rem)] @[0px]:px-2',
|
||||
'@[420px]:flex-1 @[420px]:basis-auto @[420px]:px-3',
|
||||
)
|
||||
: clsx('flex-1', '@[0px]:px-2 @[420px]:px-3'),
|
||||
isActive
|
||||
? cn('text-text', activeClassName)
|
||||
: cn(
|
||||
|
|
@ -150,7 +156,6 @@ export function SegmentTab<T = string>({
|
|||
// Floating pill indicator with subtle border & shadow
|
||||
'pointer-events-none absolute inset-x-0 inset-y-0.5 z-[-1]',
|
||||
styles.indicator,
|
||||
responsiveWrap && 'hidden @[420px]:block',
|
||||
className,
|
||||
)}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import { ParticlesAura } from '~/components/ui/effects/ParticlesAura'
|
|||
import { TiltCard } from '~/components/ui/effects/TiltCard'
|
||||
import { Highlighter } from '~/components/ui/highlighter'
|
||||
import { PanelSplitter } from '~/components/ui/panel/PanelSplitter'
|
||||
import { SegmentTab } from '~/components/ui/segment-tab'
|
||||
import { Link as LocalizedLink } from '~/i18n/routing'
|
||||
import type { HeroTimelineItem } from '~/lib/landing-data'
|
||||
|
||||
|
|
@ -26,6 +27,8 @@ type LandingHeroProps = {
|
|||
items: HeroTimelineItem[]
|
||||
}
|
||||
|
||||
type AudienceKey = 'human' | 'agent'
|
||||
|
||||
export const LandingHero: Component<LandingHeroProps> = ({ items }) => {
|
||||
const ref = React.useRef<HTMLElement | null>(null)
|
||||
const { scrollYProgress } = useScroll({
|
||||
|
|
@ -35,6 +38,28 @@ export const LandingHero: Component<LandingHeroProps> = ({ items }) => {
|
|||
const bgY = useTransform(scrollYProgress, [0, 1], [0, -150])
|
||||
const heroT = useTranslations('landing.hero')
|
||||
const actionsT = useTranslations('common.actions')
|
||||
const [audience, setAudience] = React.useState<AudienceKey>('human')
|
||||
|
||||
const audienceItems: {
|
||||
value: AudienceKey
|
||||
label: string
|
||||
icon: React.ReactNode
|
||||
}[] = [
|
||||
{
|
||||
value: 'human',
|
||||
label: heroT('humanTab'),
|
||||
icon: (
|
||||
<i className="i-mingcute-user-3-line size-4 shrink-0" aria-hidden />
|
||||
),
|
||||
},
|
||||
{
|
||||
value: 'agent',
|
||||
label: heroT('agentTab'),
|
||||
icon: (
|
||||
<i className="i-mingcute-android-2-line size-4 shrink-0" aria-hidden />
|
||||
),
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<section ref={ref} className="relative isolate w-full">
|
||||
|
|
@ -75,48 +100,75 @@ export const LandingHero: Component<LandingHeroProps> = ({ items }) => {
|
|||
<p>{heroT('bodyLine2')}</p>
|
||||
</div>
|
||||
|
||||
<div className="mt-8 flex items-center">
|
||||
<NextLink
|
||||
href="https://app.folo.is"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
<span className="relative inline-flex">
|
||||
<Button className="group relative text-base overflow-hidden px-5 py-2.5 bg-accent text-accent-foreground shadow-[0_0_0_1px_var(--color-accent-40)] ![filter:drop-shadow(0_0_24px_color-mix(in_oklab,var(--color-accent)_35%,transparent))] rounded-xl">
|
||||
<span
|
||||
aria-hidden
|
||||
className={clsx(
|
||||
'pointer-events-none absolute -inset-1 rounded-[inherit] opacity-70 blur-md',
|
||||
'bg-[radial-gradient(closest-side,color-mix(in_oklab,var(--color-accent)_55%,transparent)_0%,transparent_70%)]',
|
||||
)}
|
||||
/>
|
||||
<div className="mt-8 max-w-xl">
|
||||
<SegmentTab
|
||||
items={audienceItems}
|
||||
value={audience}
|
||||
onChange={(value) => setAudience(value as AudienceKey)}
|
||||
containerClassName="inline-flex w-fit max-w-full rounded-full border-border/70 bg-background/45 shadow-[0_20px_40px_-32px_rgba(255,107,0,0.4)]"
|
||||
className="!inset-y-0 !rounded-full !border-accent/20 !bg-background !shadow-[0_12px_24px_-16px_rgba(255,107,0,0.6)]"
|
||||
activeClassName="text-accent"
|
||||
inactiveClassName="text-text-secondary/75 hover:text-text"
|
||||
distribution="fit"
|
||||
size="lg"
|
||||
responsiveWrap
|
||||
/>
|
||||
|
||||
<span className="relative z-10 inline-flex items-center">
|
||||
{actionsT('getStarted')}
|
||||
{audience === 'human' ? (
|
||||
<div className="mt-4 flex flex-wrap items-center gap-3">
|
||||
<NextLink
|
||||
href="https://app.folo.is"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
<span className="relative inline-flex">
|
||||
<Button className="group relative overflow-hidden rounded-xl bg-accent px-5 py-2.5 text-base text-accent-foreground shadow-[0_0_0_1px_var(--color-accent-40)] ![filter:drop-shadow(0_0_24px_color-mix(in_oklab,var(--color-accent)_35%,transparent))]">
|
||||
<span
|
||||
aria-hidden
|
||||
className={clsx(
|
||||
'pointer-events-none absolute -inset-1 rounded-[inherit] opacity-70 blur-md',
|
||||
'bg-[radial-gradient(closest-side,color-mix(in_oklab,var(--color-accent)_55%,transparent)_0%,transparent_70%)]',
|
||||
)}
|
||||
/>
|
||||
|
||||
<span className="relative z-10 inline-flex items-center">
|
||||
{actionsT('getStarted')}
|
||||
</span>
|
||||
<BorderBeam colorFrom="#fff" colorTo="#ff5c00" />
|
||||
</Button>
|
||||
|
||||
<ParticlesAura className="-inset-2" />
|
||||
</span>
|
||||
<BorderBeam colorFrom="#fff" colorTo="#ff5c00" />
|
||||
</Button>
|
||||
|
||||
<ParticlesAura className="-inset-2" />
|
||||
</span>
|
||||
</NextLink>
|
||||
<LocalizedLink href="/download">
|
||||
<Button variant="ghost" className="ml-4">
|
||||
<span className="relative z-10 inline-flex items-center text-base">
|
||||
{actionsT('download')}
|
||||
</span>
|
||||
</Button>
|
||||
</LocalizedLink>
|
||||
{/* <a
|
||||
href="#learn-more"
|
||||
className="text-text-secondary hover:text-text inline-flex items-center gap-1 text-sm"
|
||||
>
|
||||
<span className="rounded-full border border-border bg-fill px-2 py-1 text-xs">
|
||||
New
|
||||
</span>
|
||||
<span>Product Intelligence</span>
|
||||
<i className="i-mingcute-right-line" aria-hidden />
|
||||
</a> */}
|
||||
</NextLink>
|
||||
<LocalizedLink href="/download">
|
||||
<Button variant="ghost">
|
||||
<span className="relative z-10 inline-flex items-center text-base">
|
||||
{actionsT('download')}
|
||||
</span>
|
||||
</Button>
|
||||
</LocalizedLink>
|
||||
</div>
|
||||
) : (
|
||||
<div className="bg-material-medium/60 border-border mt-4 rounded-2xl border p-5 backdrop-blur-md">
|
||||
<p className="text-text text-sm font-semibold">
|
||||
{heroT('agentTitle')}
|
||||
</p>
|
||||
<p className="text-text-secondary mt-2 text-sm leading-6">
|
||||
{heroT.rich('agentBody', {
|
||||
skill: (chunks) => (
|
||||
<a
|
||||
href="https://api.folo.is/skill.md"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
className="text-accent underline decoration-accent/40 underline-offset-4 transition-colors hover:text-accent/80"
|
||||
>
|
||||
{chunks}
|
||||
</a>
|
||||
),
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,11 @@
|
|||
"eyebrow": "AI RSS Reader · Clean Timeline",
|
||||
"title": "<brand>Folo</brand> is the <highlight>AI RSS Reader</highlight>",
|
||||
"bodyLine1": "The AI that reads the internet for you,",
|
||||
"bodyLine2": "cutting through noise to surface the knowledge you actually care about."
|
||||
"bodyLine2": "cutting through noise to surface the knowledge you actually care about.",
|
||||
"humanTab": "I'm a Human",
|
||||
"agentTab": "I'm an Agent",
|
||||
"agentTitle": "Use Folo",
|
||||
"agentBody": "Read <skill>https://api.folo.is/skill.md</skill> and follow the instructions to use Folo."
|
||||
},
|
||||
"features": {
|
||||
"headline": "Reading reimagined in the AI era",
|
||||
|
|
|
|||
|
|
@ -22,7 +22,11 @@
|
|||
"eyebrow": "AI RSS Reader · クリーンタイムライン",
|
||||
"title": "<brand>Folo</brand> はあなたの <highlight>AI RSS Reader</highlight>",
|
||||
"bodyLine1": "インターネットの情報を AI が代わりに読み、",
|
||||
"bodyLine2": "雑音を取り除いて、本当に必要な知識だけを届けます。"
|
||||
"bodyLine2": "雑音を取り除いて、本当に必要な知識だけを届けます。",
|
||||
"humanTab": "I'm a Human",
|
||||
"agentTab": "I'm an Agent",
|
||||
"agentTitle": "Use Folo",
|
||||
"agentBody": "Read <skill>https://api.folo.is/skill.md</skill> and follow the instructions to use Folo."
|
||||
},
|
||||
"features": {
|
||||
"headline": "AI 時代のための新しい読書体験",
|
||||
|
|
|
|||
|
|
@ -22,7 +22,11 @@
|
|||
"eyebrow": "AI RSS Reader · 洁净时间线",
|
||||
"title": "<brand>Folo</brand> 是你的 <highlight>AI RSS Reader</highlight>",
|
||||
"bodyLine1": "这款 AI 为你读遍全网,",
|
||||
"bodyLine2": "穿透噪音,只呈现你真正关心的洞见。"
|
||||
"bodyLine2": "穿透噪音,只呈现你真正关心的洞见。",
|
||||
"humanTab": "I'm a Human",
|
||||
"agentTab": "I'm an Agent",
|
||||
"agentTitle": "Use Folo",
|
||||
"agentBody": "Read <skill>https://api.folo.is/skill.md</skill> and follow the instructions to use Folo."
|
||||
},
|
||||
"features": {
|
||||
"headline": "在 AI 时代重新定义阅读",
|
||||
|
|
|
|||
Loading…
Reference in New Issue