121 lines
3 KiB
TypeScript
121 lines
3 KiB
TypeScript
import clsx from 'clsx'
|
|
import {
|
|
type ComponentProps,
|
|
type ElementType,
|
|
type PropsWithChildren,
|
|
} from 'react'
|
|
|
|
import HybridLink, { type Props as HybridLinkProps } from './HybridLink'
|
|
import { IconProps } from './icons'
|
|
|
|
function ChevronRightIcon(props: IconProps) {
|
|
return (
|
|
<svg viewBox="0 0 16 16" fill="none" aria-hidden="true" {...props}>
|
|
<path
|
|
d="M6.75 5.75 9.25 8l-2.5 2.25"
|
|
strokeWidth="1.5"
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
/>
|
|
</svg>
|
|
)
|
|
}
|
|
|
|
export function CardLink({ children, ...props }: HybridLinkProps) {
|
|
return (
|
|
<>
|
|
<div className="absolute -inset-y-6 -inset-x-4 z-0 scale-95 bg-zinc-50 opacity-0 transition group-hover:scale-100 group-hover:opacity-100 dark:bg-zinc-800/50 sm:-inset-x-6 sm:rounded-2xl" />
|
|
<HybridLink {...props}>
|
|
<span className="absolute -inset-y-6 -inset-x-4 z-20 sm:-inset-x-6 sm:rounded-2xl" />
|
|
<span className="relative z-10">{children}</span>
|
|
</HybridLink>
|
|
</>
|
|
)
|
|
}
|
|
|
|
export function CardTitle({
|
|
as: Component = 'h2',
|
|
href,
|
|
children,
|
|
}: PropsWithChildren<{
|
|
href: string
|
|
as?: ElementType
|
|
}>) {
|
|
return (
|
|
<Component className="text-base font-semibold tracking-tight text-zinc-800 dark:text-zinc-100">
|
|
{href ? <CardLink href={href}>{children}</CardLink> : children}
|
|
</Component>
|
|
)
|
|
}
|
|
|
|
export function CardDescription({ children }: PropsWithChildren) {
|
|
return (
|
|
<p className="relative z-10 mt-2 flex-1 text-sm text-zinc-600 dark:text-zinc-400">
|
|
{children}
|
|
</p>
|
|
)
|
|
}
|
|
|
|
export function CardTags({ children }: PropsWithChildren) {
|
|
return <div className="z-10 mt-2 flex w-full gap-1 text-sm">{children}</div>
|
|
}
|
|
|
|
export function CardCta({ children }: PropsWithChildren) {
|
|
return (
|
|
<div
|
|
aria-hidden="true"
|
|
className="relative z-10 mt-4 flex items-center text-sm font-medium text-teal-500"
|
|
>
|
|
{children}
|
|
<ChevronRightIcon className="ml-1 h-4 w-4 stroke-current" />
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export function CardEyebrow<C extends ElementType>({
|
|
as: Component = 'p',
|
|
decorate = false,
|
|
children,
|
|
...props
|
|
}: ComponentProps<C> & {
|
|
as: C
|
|
decorate?: boolean
|
|
}) {
|
|
return (
|
|
<Component
|
|
className={clsx(
|
|
'className' in props ? props.className : '',
|
|
'relative z-10 order-first mb-3 flex items-center text-sm text-zinc-400 dark:text-zinc-500',
|
|
decorate && 'pl-3.5',
|
|
)}
|
|
{...props}
|
|
>
|
|
{decorate && (
|
|
<span
|
|
className="absolute inset-y-0 left-0 flex items-center"
|
|
aria-hidden="true"
|
|
>
|
|
<span className="h-4 w-0.5 rounded-full bg-zinc-200 dark:bg-zinc-500" />
|
|
</span>
|
|
)}
|
|
{children}
|
|
</Component>
|
|
)
|
|
}
|
|
|
|
export default function Card({
|
|
as: Component = 'div',
|
|
className,
|
|
children,
|
|
id,
|
|
}: PropsWithChildren<{ id?: string; className?: string; as?: ElementType }>) {
|
|
return (
|
|
<Component
|
|
id={id}
|
|
className={clsx(className, 'group relative flex flex-col items-start')}
|
|
>
|
|
{children}
|
|
</Component>
|
|
)
|
|
}
|