Files
sent-shop/components/ui/Typography.tsx
2026-01-19 20:21:14 +01:00

182 lines
3.7 KiB
TypeScript

import clsx from 'clsx';
import React from 'react';
// Typography scale common props
interface TypographyProps {
children: React.ReactNode;
className?: string;
as?: React.ElementType;
}
// Heading component props
interface HeadingProps extends TypographyProps {
level?: 1 | 2 | 3 | 4;
align?: 'left' | 'center' | 'right';
}
export function Heading({
children,
className = '',
level = 2,
align = 'left',
as,
}: HeadingProps) {
const Component = as || `h${level}` as React.ElementType;
// Responsive sizes based on provided specs
const sizeClasses = {
// H1
// Desktop: 60px/76px, Mobile: 44px/52px
1: 'text-[44px] leading-[52px] lg:text-[60px] lg:leading-[76px] font-allenoire',
// H2
// Desktop: 48px/56px, Mobile: 36px/44px
2: 'text-[36px] leading-[44px] lg:text-[48px] lg:leading-[56px] font-allenoire',
// H3
// Desktop: 28px/36px, Mobile: 24px/32px
3: 'text-[24px] leading-[32px] lg:text-[28px] lg:leading-[36px] font-allenoire',
// H4
// Desktop: 20px/28px
4: 'text-[20px] leading-[28px] font-allenoire',
};
const alignClasses = {
left: 'text-left',
center: 'text-center',
right: 'text-right',
};
return (
<Component
className={clsx(
sizeClasses[level],
alignClasses[align],
'tracking-[0px]',
className
)}
>
{children}
</Component>
);
}
// Text component props
interface TextProps extends TypographyProps {
size?: 'xs' | 'sm' | 'base' | 'lg' | 'xl';
weight?: 'regular' | 'semibold';
color?: 'default' | 'muted' | 'accent';
}
export function Text({
children,
className = '',
size = 'base',
weight = 'regular',
color = 'default',
as = 'p',
}: TextProps) {
const Component = as;
// Base size classes without font family
const sizeClasses = {
xs: 'text-[12px] leading-[20px]',
sm: 'text-[14px] leading-[22px]',
base: 'text-[16px] leading-[24px]',
lg: 'text-[16px] leading-[28px]',
xl: 'text-[20px] leading-[28px]',
};
// Use Poppins for all text, either regular or semibold
const fontClass = weight === 'regular' ? 'font-poppins' : 'font-poppins-semibold';
const colorClasses = {
default: 'text-black',
muted: 'text-gray-600',
accent: 'text-blue-600',
};
return (
<Component
className={clsx(
sizeClasses[size],
fontClass,
colorClasses[color],
'tracking-[0px]',
className
)}
>
{children}
</Component>
);
}
// Caption component props
interface CaptionProps extends TypographyProps {
size?: 'sm' | 'base';
}
export function Caption({
children,
className = '',
size = 'sm',
as = 'span',
}: CaptionProps) {
const Component = as;
const sizeClasses = {
sm: 'text-[12px] leading-[20px]',
base: 'text-[14px] leading-[22px]',
};
return (
<Component
className={clsx(
sizeClasses[size],
'text-gray-500',
className
)}
>
{children}
</Component>
);
}
// Label component props
interface LabelProps extends TypographyProps {
size?: 'sm' | 'base' | 'lg';
required?: boolean;
htmlFor?: string;
}
export function Label({
children,
className = '',
size = 'base',
required = false,
htmlFor,
as = 'label',
}: LabelProps) {
const Component = as;
const sizeClasses = {
sm: 'text-[14px] leading-[22px]',
base: 'text-[16px] leading-[24px]',
lg: 'text-[18px] leading-[28px]',
};
return (
<Component
htmlFor={htmlFor}
className={clsx(
sizeClasses[size],
'font-allenoire tracking-[0px]',
className
)}
>
{children}
{required && <span className="ml-1 text-red-500">*</span>}
</Component>
);
}