first commit
tests / ci (push) Canceled after 0s

This commit is contained in:
2026-09-28 13:52:25 +07:00
commit 566275348c
262 changed files with 37008 additions and 0 deletions
@@ -0,0 +1,77 @@
import type { ReactNode } from 'react';
import Eyebrow from '@/components/site/eyebrow';
import { cn } from '@/lib/utils';
type SectionHeaderProps = {
eyebrow?: ReactNode;
eyebrowIcon?: string;
title: ReactNode;
description?: ReactNode;
/**
* `left`: stacked, `center`: centered, `split`: title on the left and the
* description (or `aside`) aligned to the bottom right.
*/
align?: 'left' | 'center' | 'split';
aside?: ReactNode;
className?: string;
};
export default function SectionHeader({
eyebrow,
eyebrowIcon,
title,
description,
align = 'left',
aside,
className,
}: SectionHeaderProps) {
const heading = (
<div
className={cn(
'flex flex-col gap-3',
align === 'center' && 'items-center text-center',
align === 'split' ? 'max-w-2xl' : 'max-w-3xl',
)}
>
{eyebrow && <Eyebrow icon={eyebrowIcon}>{eyebrow}</Eyebrow>}
<h2 className="font-display text-on-surface lg:text-headline-xl text-3xl font-extrabold tracking-tight">
{title}
</h2>
{description && align !== 'split' && (
<p className="text-on-surface-variant text-base leading-relaxed">
{description}
</p>
)}
</div>
);
if (align === 'split') {
return (
<div
className={cn(
'flex flex-col justify-between gap-6 md:flex-row md:items-end',
className,
)}
>
{heading}
{aside ??
(description && (
<p className="text-on-surface-variant max-w-md text-sm leading-relaxed">
{description}
</p>
))}
</div>
);
}
return (
<div
className={cn(
align === 'center' && 'mx-auto flex justify-center',
className,
)}
>
{heading}
</div>
);
}