refactor(ui): rewrite Heading component to act as central typography system, fix line-heights, and apply to MDX pages
Former-commit-id: dc62485b60679b810b5f74bfb5a22915b7a62314
This commit is contained in:
@@ -1,28 +1,56 @@
|
||||
import React from 'react';
|
||||
import { cn } from './utils';
|
||||
|
||||
export function Heading({
|
||||
level = 2,
|
||||
children,
|
||||
className,
|
||||
subtitle,
|
||||
align = 'left'
|
||||
}: {
|
||||
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;
|
||||
subtitle?: string;
|
||||
align?: 'left' | 'center' | 'right';
|
||||
}) {
|
||||
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 sizes = {
|
||||
1: 'text-2xl md:text-4xl lg:text-5xl font-bold leading-[1.1] tracking-tight',
|
||||
2: 'text-xl md:text-3xl lg:text-4xl font-bold leading-[1.2] tracking-tight',
|
||||
3: 'text-lg md:text-2xl lg:text-3xl font-bold leading-[1.3] tracking-tight',
|
||||
4: 'text-lg md:text-xl lg:text-2xl font-bold leading-[1.4]',
|
||||
5: 'text-base md:text-lg font-bold leading-[1.5]',
|
||||
6: 'text-base md:text-lg font-semibold leading-[1.6]',
|
||||
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-[1.0]',
|
||||
'section': 'font-heading text-3xl md:text-4xl lg:text-5xl font-extrabold tracking-tight leading-[1.1]',
|
||||
'subsection': 'font-heading text-2xl md:text-3xl lg:text-4xl font-bold tracking-tight leading-[1.15]',
|
||||
'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-[1.0] tracking-tight',
|
||||
'2': 'text-xl md:text-3xl lg:text-4xl font-bold leading-[1.1] tracking-tight',
|
||||
'3': 'text-lg md:text-2xl lg:text-3xl font-bold leading-[1.15] tracking-tight',
|
||||
'4': 'text-lg md:text-xl lg:text-2xl font-bold leading-[1.2]',
|
||||
'5': 'text-base md:text-lg font-bold leading-[1.3]',
|
||||
'6': 'text-base md:text-lg font-semibold leading-[1.4]',
|
||||
};
|
||||
|
||||
const variants = {
|
||||
primary: 'text-primary',
|
||||
neutral: 'text-text-primary',
|
||||
white: 'text-white',
|
||||
dark: 'text-neutral-dark',
|
||||
};
|
||||
|
||||
const alignments = {
|
||||
@@ -32,13 +60,32 @@ export function Heading({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={cn('mb-6 md:mb-12 text-primary', alignments[align], className)}>
|
||||
{subtitle && (
|
||||
<span className="inline-block text-accent font-bold tracking-widest uppercase text-xs md:text-sm mb-3 md:mb-4 animate-fade-in">
|
||||
<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[level as keyof typeof sizes])}>
|
||||
|
||||
<Tag className={cn(sizes[effectiveSize], variants[variant])}>
|
||||
{children}
|
||||
</Tag>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user