94 lines
3.3 KiB
TypeScript
94 lines
3.3 KiB
TypeScript
import React from 'react';
|
|
import { cn } from './utils';
|
|
|
|
export type HeadingVariant = 'primary' | 'neutral' | 'white' | 'dark';
|
|
export type HeadingSize = 'hero' | 'section' | 'subsection' | 'card' | 'small' | '1' | '2' | '3' | '4' | '5' | '6';
|
|
|
|
interface HeadingProps {
|
|
level?: 1 | 2 | 3 | 4 | 5 | 6;
|
|
size?: HeadingSize;
|
|
variant?: HeadingVariant;
|
|
align?: 'left' | 'center' | 'right';
|
|
subtitle?: React.ReactNode;
|
|
children: React.ReactNode;
|
|
className?: string;
|
|
subtitleClassName?: string;
|
|
badgeStyle?: 'industrial' | 'simple';
|
|
}
|
|
|
|
export function Heading({
|
|
level = 2,
|
|
size,
|
|
variant = 'dark',
|
|
align = 'left',
|
|
subtitle,
|
|
children,
|
|
className,
|
|
subtitleClassName,
|
|
badgeStyle = 'industrial'
|
|
}: HeadingProps) {
|
|
const Tag = `h${level}` as any;
|
|
|
|
const effectiveSize = size || String(level) as HeadingSize;
|
|
|
|
const sizes: Record<string, string> = {
|
|
'hero': 'font-heading text-4xl md:text-5xl lg:text-6xl font-black tracking-tight !leading-[0.9]',
|
|
'section': 'font-heading text-3xl md:text-4xl lg:text-5xl font-extrabold tracking-tight !leading-[0.95]',
|
|
'subsection': 'font-heading text-2xl md:text-3xl lg:text-4xl font-bold tracking-tight leading-tight',
|
|
'card': 'text-xl md:text-2xl font-bold leading-snug',
|
|
'small': 'text-lg font-semibold leading-snug',
|
|
// Legacy support
|
|
'1': 'text-2xl md:text-4xl lg:text-5xl font-bold leading-[0.9] tracking-tight',
|
|
'2': 'text-xl md:text-3xl lg:text-4xl font-bold leading-[0.95] tracking-tight',
|
|
'3': 'text-lg md:text-2xl lg:text-3xl font-bold leading-tight tracking-tight',
|
|
'4': 'text-lg md:text-xl lg:text-2xl font-bold leading-snug',
|
|
'5': 'text-base md:text-lg font-bold leading-normal',
|
|
'6': 'text-base md:text-lg font-semibold leading-normal',
|
|
};
|
|
|
|
const variants = {
|
|
primary: 'text-primary',
|
|
neutral: 'text-text-primary',
|
|
white: 'text-white',
|
|
dark: 'text-neutral-dark',
|
|
};
|
|
|
|
const alignments = {
|
|
left: 'text-left',
|
|
center: 'text-center mx-auto',
|
|
right: 'text-right',
|
|
};
|
|
|
|
return (
|
|
<div className={cn('mb-6 md:mb-8', alignments[align], className)}>
|
|
{subtitle && badgeStyle === 'industrial' && (
|
|
<span className={cn(
|
|
"inline-flex items-center space-x-2 px-3 py-1.5 rounded-full mb-6 border shadow-sm text-xs font-bold uppercase tracking-widest",
|
|
variant === 'white' ? 'bg-white/10 text-white border-white/20' : 'bg-primary/5 text-primary border-primary/20',
|
|
subtitleClassName
|
|
)}>
|
|
<span className="relative flex h-2 w-2">
|
|
<span className={cn("animate-ping absolute inline-flex h-full w-full rounded-full opacity-75", variant === 'white' ? 'bg-white' : 'bg-primary')}></span>
|
|
<span className={cn("relative inline-flex rounded-full h-2 w-2", variant === 'white' ? 'bg-white' : 'bg-primary')}></span>
|
|
</span>
|
|
<span>{subtitle}</span>
|
|
</span>
|
|
)}
|
|
|
|
{subtitle && badgeStyle === 'simple' && (
|
|
<span className={cn(
|
|
"inline-block font-bold tracking-widest uppercase text-xs md:text-sm mb-3 md:mb-4 animate-fade-in",
|
|
variant === 'white' ? 'text-white/80' : 'text-accent',
|
|
subtitleClassName
|
|
)}>
|
|
{subtitle}
|
|
</span>
|
|
)}
|
|
|
|
<Tag className={cn(sizes[effectiveSize], variants[variant])}>
|
|
{children}
|
|
</Tag>
|
|
</div>
|
|
);
|
|
}
|