feat(brand-system): Implement comprehensive CHORUS brand system with typography and design tokens
This commit implements a complete brand system overhaul including: TYPOGRAPHY SYSTEM: - Add Exo font family (Thin, Light, Regular, ExtraLight) as primary brand font - Implement SF Pro Display/Text hierarchy for UI components - Create comprehensive Typography component with all brand variants - Update all components to use new typography tokens DESIGN TOKEN SYSTEM: - Create complete design token system in theme/designTokens.ts - Define Carbon Black (#1a1a1a), Walnut Brown (#8B4513), Brushed Aluminum (#A8A8A8) palette - Implement CSS custom properties for consistent theming - Update Ant Design theme integration COMPONENT UPDATES: - Enhance Hero section with Exo Thin typography and improved layout - Update navigation with SF Pro font hierarchy - Redesign Button component with new variants and accessibility - Apply brand colors and typography across all showcase sections - Improve Footer with consistent brand application PERFORMANCE & ACCESSIBILITY: - Self-host Exo fonts for optimal loading performance - Implement proper font-display strategies - Add comprehensive accessibility audit documentation - Include responsive testing verification DOCUMENTATION: - Add brand system demo and implementation guides - Include QA testing reports and accessibility audits - Document design token usage and component patterns 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -4,79 +4,220 @@ import React from 'react';
|
||||
import { Button as AntButton, ButtonProps as AntButtonProps } from 'antd';
|
||||
import { motion, MotionProps } from 'framer-motion';
|
||||
import { cn } from '@/utils/cn';
|
||||
import { Typography } from './Typography';
|
||||
|
||||
// Extend Ant Design ButtonProps with custom variants
|
||||
// Extend Ant Design ButtonProps with custom variants aligned to brand system
|
||||
interface CustomButtonProps {
|
||||
variant?: 'primary' | 'secondary' | 'outline' | 'ghost' | 'gradient';
|
||||
variant?: 'primary' | 'secondary' | 'tertiary' | 'ghost' | 'gradient' | 'walnut';
|
||||
size?: 'small' | 'regular' | 'large';
|
||||
fullWidth?: boolean;
|
||||
animated?: boolean;
|
||||
icon?: React.ReactNode;
|
||||
iconPosition?: 'left' | 'right';
|
||||
}
|
||||
|
||||
type ButtonProps = Omit<AntButtonProps, 'type'> & CustomButtonProps & Partial<MotionProps>;
|
||||
type ButtonProps = Omit<AntButtonProps, 'type' | 'size'> & CustomButtonProps & Partial<MotionProps>;
|
||||
|
||||
const MotionButton = motion(AntButton);
|
||||
|
||||
export const Button: React.FC<ButtonProps> = ({
|
||||
variant = 'primary',
|
||||
size = 'regular',
|
||||
fullWidth = false,
|
||||
animated = true,
|
||||
icon,
|
||||
iconPosition = 'left',
|
||||
className,
|
||||
children,
|
||||
...antProps
|
||||
}) => {
|
||||
const getVariantClasses = () => {
|
||||
const getVariantStyles = () => {
|
||||
const baseStyles = {
|
||||
fontFamily: 'var(--font-body)',
|
||||
fontWeight: 600,
|
||||
lineHeight: 'var(--leading-tight)',
|
||||
letterSpacing: 'var(--tracking-wider)',
|
||||
borderRadius: 'var(--radius-button)',
|
||||
transition: 'all var(--duration-normal) var(--easing-ease-out)',
|
||||
cursor: 'pointer',
|
||||
border: 'none',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
gap: icon ? 'var(--space-sm)' : '0',
|
||||
};
|
||||
|
||||
switch (variant) {
|
||||
case 'primary':
|
||||
return 'bg-chorus-blue hover:bg-blue-600 border-chorus-blue hover:border-blue-600 text-white shadow-lg hover:shadow-xl';
|
||||
return {
|
||||
...baseStyles,
|
||||
backgroundColor: 'var(--interactive-primary)',
|
||||
color: '#ffffff',
|
||||
boxShadow: 'var(--shadow-button)',
|
||||
};
|
||||
case 'secondary':
|
||||
return 'bg-chorus-green hover:bg-green-600 border-chorus-green hover:border-green-600 text-white shadow-lg hover:shadow-xl';
|
||||
case 'outline':
|
||||
return 'bg-transparent border-2 border-chorus-blue text-chorus-blue hover:bg-chorus-blue hover:text-white';
|
||||
return {
|
||||
...baseStyles,
|
||||
backgroundColor: 'transparent',
|
||||
color: 'var(--interactive-primary)',
|
||||
border: '2px solid var(--interactive-primary)',
|
||||
};
|
||||
case 'tertiary':
|
||||
return {
|
||||
...baseStyles,
|
||||
backgroundColor: 'var(--interactive-secondary)',
|
||||
color: 'var(--text-inverse)',
|
||||
};
|
||||
case 'ghost':
|
||||
return 'bg-transparent border-transparent text-white hover:bg-white/10 hover:border-white/20';
|
||||
return {
|
||||
...baseStyles,
|
||||
backgroundColor: 'transparent',
|
||||
color: 'var(--text-secondary)',
|
||||
border: '1px solid var(--border-secondary)',
|
||||
};
|
||||
case 'gradient':
|
||||
return 'bg-gradient-chorus border-transparent text-white shadow-lg hover:shadow-xl';
|
||||
return {
|
||||
...baseStyles,
|
||||
background: 'linear-gradient(135deg, var(--color-primary) 0%, var(--color-success) 100%)',
|
||||
color: '#ffffff',
|
||||
boxShadow: 'var(--shadow-button)',
|
||||
};
|
||||
case 'walnut':
|
||||
return {
|
||||
...baseStyles,
|
||||
backgroundColor: 'var(--chorus-walnut-deep)',
|
||||
color: '#ffffff',
|
||||
boxShadow: 'var(--shadow-button)',
|
||||
};
|
||||
default:
|
||||
return '';
|
||||
return baseStyles;
|
||||
}
|
||||
};
|
||||
|
||||
const buttonClasses = cn(
|
||||
'font-semibold transition-all duration-200 border-0 rounded-lg',
|
||||
'focus:ring-2 focus:ring-chorus-blue focus:ring-opacity-50 focus:outline-none',
|
||||
getVariantClasses(),
|
||||
fullWidth && 'w-full',
|
||||
className
|
||||
);
|
||||
const getSizeStyles = () => {
|
||||
switch (size) {
|
||||
case 'small':
|
||||
return {
|
||||
fontSize: 'var(--text-interface-small)',
|
||||
padding: 'var(--space-sm) var(--space-md)',
|
||||
minHeight: '36px',
|
||||
};
|
||||
case 'large':
|
||||
return {
|
||||
fontSize: 'var(--text-interface)',
|
||||
padding: 'var(--space-lg) var(--space-2xl)',
|
||||
minHeight: '52px',
|
||||
};
|
||||
default: // regular
|
||||
return {
|
||||
fontSize: 'var(--text-interface)',
|
||||
padding: 'var(--space-md) var(--space-xl)',
|
||||
minHeight: '44px',
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const getHoverStyles = () => {
|
||||
switch (variant) {
|
||||
case 'primary':
|
||||
return {
|
||||
backgroundColor: 'var(--interactive-primary-hover)',
|
||||
boxShadow: 'var(--shadow-button-hover)',
|
||||
transform: 'translateY(-2px)',
|
||||
};
|
||||
case 'secondary':
|
||||
return {
|
||||
backgroundColor: 'var(--interactive-primary)',
|
||||
color: '#ffffff',
|
||||
transform: 'translateY(-2px)',
|
||||
boxShadow: 'var(--shadow-button)',
|
||||
};
|
||||
case 'tertiary':
|
||||
return {
|
||||
backgroundColor: 'var(--interactive-secondary-hover)',
|
||||
transform: 'translateY(-2px)',
|
||||
boxShadow: 'var(--shadow-md)',
|
||||
};
|
||||
case 'ghost':
|
||||
return {
|
||||
color: 'var(--text-primary)',
|
||||
borderColor: 'var(--border-primary)',
|
||||
backgroundColor: 'var(--bg-tertiary)',
|
||||
};
|
||||
case 'gradient':
|
||||
return {
|
||||
boxShadow: 'var(--shadow-button-hover)',
|
||||
transform: 'translateY(-2px)',
|
||||
};
|
||||
case 'walnut':
|
||||
return {
|
||||
backgroundColor: 'var(--chorus-walnut-medium)',
|
||||
boxShadow: 'var(--shadow-button-hover)',
|
||||
transform: 'translateY(-2px)',
|
||||
};
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
const buttonStyles = {
|
||||
...getVariantStyles(),
|
||||
...getSizeStyles(),
|
||||
width: fullWidth ? '100%' : 'auto',
|
||||
};
|
||||
|
||||
const animationProps = animated
|
||||
? {
|
||||
whileHover: { scale: 1.02 },
|
||||
whileTap: { scale: 0.98 },
|
||||
transition: { duration: 0.2 },
|
||||
whileHover: getHoverStyles(),
|
||||
whileTap: {
|
||||
transform: 'translateY(0)',
|
||||
boxShadow: variant === 'primary' || variant === 'gradient' || variant === 'walnut'
|
||||
? 'var(--shadow-button)'
|
||||
: undefined
|
||||
},
|
||||
transition: { duration: 0.2, ease: [0, 0, 0.2, 1] },
|
||||
}
|
||||
: {};
|
||||
|
||||
const renderContent = () => {
|
||||
if (!icon) {
|
||||
return <Typography.Button size={size}>{children}</Typography.Button>;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{iconPosition === 'left' && icon}
|
||||
<Typography.Button size={size}>{children}</Typography.Button>
|
||||
{iconPosition === 'right' && icon}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
if (animated) {
|
||||
return (
|
||||
<MotionButton
|
||||
className={buttonClasses}
|
||||
style={buttonStyles}
|
||||
className={cn('focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] focus:ring-opacity-50', className)}
|
||||
{...animationProps}
|
||||
{...antProps}
|
||||
>
|
||||
{children}
|
||||
{renderContent()}
|
||||
</MotionButton>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<AntButton className={buttonClasses} {...antProps}>
|
||||
{children}
|
||||
<AntButton
|
||||
style={buttonStyles}
|
||||
className={cn('focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] focus:ring-opacity-50', className)}
|
||||
{...antProps}
|
||||
>
|
||||
{renderContent()}
|
||||
</AntButton>
|
||||
);
|
||||
};
|
||||
|
||||
// Specific button variants for common use cases
|
||||
// Specific button variants for common use cases aligned to brand system
|
||||
export const PrimaryButton: React.FC<Omit<ButtonProps, 'variant'>> = (props) => (
|
||||
<Button variant="primary" {...props} />
|
||||
);
|
||||
@@ -85,8 +226,8 @@ export const SecondaryButton: React.FC<Omit<ButtonProps, 'variant'>> = (props) =
|
||||
<Button variant="secondary" {...props} />
|
||||
);
|
||||
|
||||
export const OutlineButton: React.FC<Omit<ButtonProps, 'variant'>> = (props) => (
|
||||
<Button variant="outline" {...props} />
|
||||
export const TertiaryButton: React.FC<Omit<ButtonProps, 'variant'>> = (props) => (
|
||||
<Button variant="tertiary" {...props} />
|
||||
);
|
||||
|
||||
export const GhostButton: React.FC<Omit<ButtonProps, 'variant'>> = (props) => (
|
||||
@@ -97,4 +238,13 @@ export const GradientButton: React.FC<Omit<ButtonProps, 'variant'>> = (props) =>
|
||||
<Button variant="gradient" {...props} />
|
||||
);
|
||||
|
||||
export const WalnutButton: React.FC<Omit<ButtonProps, 'variant'>> = (props) => (
|
||||
<Button variant="walnut" {...props} />
|
||||
);
|
||||
|
||||
// Legacy alias for backward compatibility
|
||||
export const OutlineButton: React.FC<Omit<ButtonProps, 'variant'>> = (props) => (
|
||||
<Button variant="secondary" {...props} />
|
||||
);
|
||||
|
||||
export default Button;
|
||||
423
components/ui/Typography.tsx
Normal file
423
components/ui/Typography.tsx
Normal file
@@ -0,0 +1,423 @@
|
||||
'use client';
|
||||
|
||||
import React from 'react';
|
||||
import { Typography as AntTypography } from 'antd';
|
||||
import { designTokens } from '../../theme/designTokens';
|
||||
|
||||
const { Title, Paragraph, Text } = AntTypography;
|
||||
|
||||
// =============================================================================
|
||||
// TYPOGRAPHY COMPONENT INTERFACES
|
||||
// =============================================================================
|
||||
|
||||
interface BaseTypographyProps {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
id?: string;
|
||||
}
|
||||
|
||||
interface HeadingProps extends BaseTypographyProps {
|
||||
level?: 1 | 2 | 3 | 4 | 5;
|
||||
color?: 'primary' | 'secondary' | 'tertiary' | 'inverse';
|
||||
gradient?: boolean;
|
||||
}
|
||||
|
||||
interface BodyTextProps extends BaseTypographyProps {
|
||||
size?: 'large' | 'regular' | 'small';
|
||||
color?: 'primary' | 'secondary' | 'tertiary' | 'disabled' | 'inverse';
|
||||
weight?: 'light' | 'regular' | 'medium' | 'semibold' | 'bold';
|
||||
}
|
||||
|
||||
interface CodeTextProps extends BaseTypographyProps {
|
||||
inline?: boolean;
|
||||
language?: string;
|
||||
}
|
||||
|
||||
interface ButtonTextProps extends BaseTypographyProps {
|
||||
size?: 'small' | 'regular' | 'large';
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// HERO DISPLAY TYPOGRAPHY
|
||||
// =============================================================================
|
||||
|
||||
export const HeroDisplay: React.FC<HeadingProps> = ({
|
||||
children,
|
||||
className = '',
|
||||
style = {},
|
||||
color = 'primary',
|
||||
gradient = false,
|
||||
...props
|
||||
}) => {
|
||||
const colorMap = {
|
||||
primary: 'var(--text-primary)',
|
||||
secondary: 'var(--text-secondary)',
|
||||
tertiary: 'var(--text-tertiary)',
|
||||
inverse: 'var(--text-inverse)',
|
||||
};
|
||||
|
||||
const heroStyles: React.CSSProperties = {
|
||||
fontFamily: 'var(--font-display)',
|
||||
fontSize: 'var(--text-hero)',
|
||||
fontWeight: 100, // Exo Thin
|
||||
lineHeight: 'var(--leading-tight)',
|
||||
letterSpacing: 'var(--tracking-tight)',
|
||||
color: gradient ? 'transparent' : colorMap[color],
|
||||
background: gradient ? 'linear-gradient(135deg, var(--color-primary) 0%, var(--color-success) 100%)' : 'none',
|
||||
WebkitBackgroundClip: gradient ? 'text' : 'initial',
|
||||
backgroundClip: gradient ? 'text' : 'initial',
|
||||
margin: 0,
|
||||
...style,
|
||||
};
|
||||
|
||||
return (
|
||||
<h1
|
||||
className={`text-hero ${gradient ? 'text-gradient' : ''} ${className}`}
|
||||
style={heroStyles}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</h1>
|
||||
);
|
||||
};
|
||||
|
||||
// =============================================================================
|
||||
// DISPLAY TYPOGRAPHY HIERARCHY
|
||||
// =============================================================================
|
||||
|
||||
export const DisplayHeading: React.FC<HeadingProps> = ({
|
||||
children,
|
||||
level = 1,
|
||||
className = '',
|
||||
style = {},
|
||||
color = 'primary',
|
||||
gradient = false,
|
||||
...props
|
||||
}) => {
|
||||
const colorMap = {
|
||||
primary: 'var(--text-primary)',
|
||||
secondary: 'var(--text-secondary)',
|
||||
tertiary: 'var(--text-tertiary)',
|
||||
inverse: 'var(--text-inverse)',
|
||||
};
|
||||
|
||||
const levelStyles = {
|
||||
1: {
|
||||
fontSize: 'var(--text-display-1)',
|
||||
fontWeight: 200, // Exo ExtraLight
|
||||
lineHeight: 'var(--leading-snug)',
|
||||
},
|
||||
2: {
|
||||
fontSize: 'var(--text-display-2)',
|
||||
fontWeight: 300, // Exo Light
|
||||
lineHeight: 'var(--leading-normal)',
|
||||
},
|
||||
3: {
|
||||
fontSize: 'clamp(20px, 3vw, 28px)',
|
||||
fontWeight: 400, // Exo Regular
|
||||
lineHeight: 'var(--leading-normal)',
|
||||
},
|
||||
4: {
|
||||
fontSize: 'clamp(18px, 2.5vw, 24px)',
|
||||
fontWeight: 400,
|
||||
lineHeight: 'var(--leading-normal)',
|
||||
},
|
||||
5: {
|
||||
fontSize: 'clamp(16px, 2vw, 20px)',
|
||||
fontWeight: 500,
|
||||
lineHeight: 'var(--leading-normal)',
|
||||
},
|
||||
};
|
||||
|
||||
const displayStyles: React.CSSProperties = {
|
||||
fontFamily: 'var(--font-display)',
|
||||
color: gradient ? 'transparent' : colorMap[color],
|
||||
background: gradient ? 'linear-gradient(135deg, var(--color-primary) 0%, var(--color-success) 100%)' : 'none',
|
||||
WebkitBackgroundClip: gradient ? 'text' : 'initial',
|
||||
backgroundClip: gradient ? 'text' : 'initial',
|
||||
letterSpacing: 'var(--tracking-snug)',
|
||||
margin: 0,
|
||||
...levelStyles[level],
|
||||
...style,
|
||||
};
|
||||
|
||||
const Tag = `h${level}` as keyof JSX.IntrinsicElements;
|
||||
|
||||
return (
|
||||
<Tag
|
||||
className={`text-display-${level} ${gradient ? 'text-gradient' : ''} ${className}`}
|
||||
style={displayStyles}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</Tag>
|
||||
);
|
||||
};
|
||||
|
||||
// =============================================================================
|
||||
// INTERFACE TYPOGRAPHY
|
||||
// =============================================================================
|
||||
|
||||
export const InterfaceText: React.FC<BodyTextProps> = ({
|
||||
children,
|
||||
size = 'regular',
|
||||
className = '',
|
||||
style = {},
|
||||
color = 'primary',
|
||||
weight = 'medium',
|
||||
...props
|
||||
}) => {
|
||||
const colorMap = {
|
||||
primary: 'var(--text-primary)',
|
||||
secondary: 'var(--text-secondary)',
|
||||
tertiary: 'var(--text-tertiary)',
|
||||
disabled: 'var(--text-disabled)',
|
||||
inverse: 'var(--text-inverse)',
|
||||
};
|
||||
|
||||
const sizeMap = {
|
||||
small: 'var(--text-interface-small)',
|
||||
regular: 'var(--text-interface)',
|
||||
large: 'var(--text-interface)',
|
||||
};
|
||||
|
||||
const weightMap = {
|
||||
light: 300,
|
||||
regular: 400,
|
||||
medium: 500,
|
||||
semibold: 600,
|
||||
bold: 700,
|
||||
};
|
||||
|
||||
const interfaceStyles: React.CSSProperties = {
|
||||
fontFamily: 'var(--font-interface)',
|
||||
fontSize: sizeMap[size],
|
||||
fontWeight: weightMap[weight],
|
||||
lineHeight: 'var(--leading-normal)',
|
||||
letterSpacing: size === 'small' ? 'var(--tracking-wider)' : 'var(--tracking-wide)',
|
||||
color: colorMap[color],
|
||||
margin: 0,
|
||||
...style,
|
||||
};
|
||||
|
||||
return (
|
||||
<span
|
||||
className={`text-interface ${className}`}
|
||||
style={interfaceStyles}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
// =============================================================================
|
||||
// BODY TYPOGRAPHY
|
||||
// =============================================================================
|
||||
|
||||
export const BodyText: React.FC<BodyTextProps> = ({
|
||||
children,
|
||||
size = 'regular',
|
||||
className = '',
|
||||
style = {},
|
||||
color = 'primary',
|
||||
weight = 'regular',
|
||||
...props
|
||||
}) => {
|
||||
const colorMap = {
|
||||
primary: 'var(--text-primary)',
|
||||
secondary: 'var(--text-secondary)',
|
||||
tertiary: 'var(--text-tertiary)',
|
||||
disabled: 'var(--text-disabled)',
|
||||
inverse: 'var(--text-inverse)',
|
||||
};
|
||||
|
||||
const sizeMap = {
|
||||
large: 'var(--text-body-large)',
|
||||
regular: 'var(--text-body)',
|
||||
small: 'var(--text-body-small)',
|
||||
};
|
||||
|
||||
const weightMap = {
|
||||
light: 300,
|
||||
regular: 400,
|
||||
medium: 500,
|
||||
semibold: 600,
|
||||
bold: 700,
|
||||
};
|
||||
|
||||
const lineHeightMap = {
|
||||
large: 'var(--leading-reading)',
|
||||
regular: 'var(--leading-loose)',
|
||||
small: 'var(--leading-relaxed)',
|
||||
};
|
||||
|
||||
const bodyStyles: React.CSSProperties = {
|
||||
fontFamily: 'var(--font-body)',
|
||||
fontSize: sizeMap[size],
|
||||
fontWeight: weightMap[weight],
|
||||
lineHeight: lineHeightMap[size],
|
||||
letterSpacing: size === 'small' ? 'var(--tracking-wide)' : 'var(--tracking-normal)',
|
||||
color: colorMap[color],
|
||||
margin: 0,
|
||||
...style,
|
||||
};
|
||||
|
||||
return (
|
||||
<p
|
||||
className={`text-body text-body-${size} ${className}`}
|
||||
style={bodyStyles}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</p>
|
||||
);
|
||||
};
|
||||
|
||||
// =============================================================================
|
||||
// CODE TYPOGRAPHY
|
||||
// =============================================================================
|
||||
|
||||
export const CodeText: React.FC<CodeTextProps> = ({
|
||||
children,
|
||||
inline = false,
|
||||
className = '',
|
||||
style = {},
|
||||
language,
|
||||
...props
|
||||
}) => {
|
||||
const codeStyles: React.CSSProperties = {
|
||||
fontFamily: 'var(--font-mono)',
|
||||
fontSize: inline ? '0.9em' : 'var(--text-code)',
|
||||
lineHeight: 'var(--leading-relaxed)',
|
||||
letterSpacing: 'var(--tracking-normal)',
|
||||
color: 'var(--text-primary)',
|
||||
backgroundColor: inline ? 'var(--bg-tertiary)' : 'var(--bg-secondary)',
|
||||
padding: inline ? '2px 6px' : 'var(--space-md)',
|
||||
borderRadius: inline ? '4px' : 'var(--radius-md)',
|
||||
border: inline ? '1px solid var(--border-secondary)' : 'none',
|
||||
margin: 0,
|
||||
...style,
|
||||
};
|
||||
|
||||
if (inline) {
|
||||
return (
|
||||
<code
|
||||
className={`text-code ${className}`}
|
||||
style={codeStyles}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</code>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<pre
|
||||
className={`text-code ${className}`}
|
||||
style={{
|
||||
...codeStyles,
|
||||
display: 'block',
|
||||
overflow: 'auto',
|
||||
whiteSpace: 'pre-wrap',
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
<code>{children}</code>
|
||||
</pre>
|
||||
);
|
||||
};
|
||||
|
||||
// =============================================================================
|
||||
// BUTTON TYPOGRAPHY
|
||||
// =============================================================================
|
||||
|
||||
export const ButtonText: React.FC<ButtonTextProps> = ({
|
||||
children,
|
||||
size = 'regular',
|
||||
className = '',
|
||||
style = {},
|
||||
...props
|
||||
}) => {
|
||||
const sizeMap = {
|
||||
small: 'var(--text-interface-small)',
|
||||
regular: 'var(--text-interface)',
|
||||
large: 'var(--text-interface)',
|
||||
};
|
||||
|
||||
const buttonStyles: React.CSSProperties = {
|
||||
fontFamily: 'var(--font-body)',
|
||||
fontSize: sizeMap[size],
|
||||
fontWeight: 600,
|
||||
lineHeight: 'var(--leading-tight)',
|
||||
letterSpacing: 'var(--tracking-wider)',
|
||||
margin: 0,
|
||||
...style,
|
||||
};
|
||||
|
||||
return (
|
||||
<span
|
||||
className={`text-button ${className}`}
|
||||
style={buttonStyles}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
// =============================================================================
|
||||
// UTILITY COMPONENTS
|
||||
// =============================================================================
|
||||
|
||||
export const Gradient: React.FC<BaseTypographyProps> = ({
|
||||
children,
|
||||
className = '',
|
||||
style = {},
|
||||
...props
|
||||
}) => {
|
||||
const gradientStyles: React.CSSProperties = {
|
||||
background: 'linear-gradient(135deg, var(--color-primary) 0%, var(--color-success) 100%)',
|
||||
WebkitBackgroundClip: 'text',
|
||||
backgroundClip: 'text',
|
||||
color: 'transparent',
|
||||
backgroundSize: '200% 200%',
|
||||
animation: 'gradient-shift 5s ease-in-out infinite',
|
||||
...style,
|
||||
};
|
||||
|
||||
return (
|
||||
<span
|
||||
className={`text-gradient ${className}`}
|
||||
style={gradientStyles}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
// =============================================================================
|
||||
// COMPOUND COMPONENTS
|
||||
// =============================================================================
|
||||
|
||||
export const Typography = {
|
||||
Hero: HeroDisplay,
|
||||
Display: DisplayHeading,
|
||||
Interface: InterfaceText,
|
||||
Body: BodyText,
|
||||
Code: CodeText,
|
||||
Button: ButtonText,
|
||||
Gradient,
|
||||
|
||||
// Legacy compatibility with existing components
|
||||
H1: (props: HeadingProps) => <DisplayHeading level={1} {...props} />,
|
||||
H2: (props: HeadingProps) => <DisplayHeading level={2} {...props} />,
|
||||
H3: (props: HeadingProps) => <DisplayHeading level={3} {...props} />,
|
||||
H4: (props: HeadingProps) => <DisplayHeading level={4} {...props} />,
|
||||
H5: (props: HeadingProps) => <DisplayHeading level={5} {...props} />,
|
||||
P: (props: BodyTextProps) => <BodyText {...props} />,
|
||||
};
|
||||
|
||||
export default Typography;
|
||||
Reference in New Issue
Block a user