feat: design system v2 with liquid glass aesthetic

Overhaul design constants for OLED-first dark mode:
- Colors: brand palette, phase colors, glass/shadow tokens, gradients
- Typography: Inter font scale matching iOS type system
- Spacing: 4px base unit with layout constants
- Border radius: liquid glass card radii
- Animations: spring/timing presets for UI transitions

Add v2 product docs (PRD, PDD, BDSD) and update CLAUDE.md.
Install expo-video, expo-sharing, @expo-google-fonts/inter.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Millian Lamiaux
2026-02-20 13:23:18 +01:00
parent 54ac8326fa
commit 511e207762
15 changed files with 2865 additions and 312 deletions

View File

@@ -0,0 +1,19 @@
<claude-mem-context>
# Recent Activity
<!-- This section is auto-generated by claude-mem. Edit content outside the tags. -->
### Feb 18, 2026
| ID | Time | T | Title | Read |
|----|------|---|-------|------|
| #4831 | 2:57 PM | 🔄 | Spacing Constants Enhanced with Semantic Aliases and Documentation | ~358 |
| #4830 | 2:56 PM | 🔵 | Design System Constants Index Review | ~293 |
| #4829 | " | 🔵 | Existing Animation Constants Reviewed | ~331 |
| #4828 | " | 🔵 | Color System Constants Review for BDSD Planning | ~412 |
| #4827 | " | 🔵 | Spacing Constants Implementation Review | ~254 |
| #4826 | " | 🔵 | Shadow System Constants Reviewed | ~296 |
| #4825 | " | 🔵 | Typography Constants Implementation Verification | ~264 |
| #4779 | 1:21 PM | 🔵 | Typography System Constants Analysis | ~354 |
| #4778 | 1:20 PM | 🔵 | Design System Color Constants Analysis | ~392 |
</claude-mem-context>

View File

@@ -1,51 +1,132 @@
/**
* TabataFit Animation System
* Liquid Glass — fluid, organic motion
*/
import { Easing } from 'react-native'
// ═══════════════════════════════════════════════════════════════════════════
// DURATIONS
// ═══════════════════════════════════════════════════════════════════════════
export const DURATION = {
SNAP: 60,
QUICK: 120,
FAST: 300,
NORMAL: 400,
SLOW: 600,
XSLOW: 1000,
BREATH: 1800,
INSTANT: 100,
FAST: 200,
NORMAL: 300,
SLOW: 500,
XSLOW: 800,
// Special
BREATH: 2500, // Breathing animation cycle
BREATH_HALF: 1250, // Half breath cycle
LIQUID: 600, // Liquid morph duration
RIPPLE: 400, // Tap ripple
PHASE_CHANGE: 300, // Phase transition
} as const
export const EASING = {
STANDARD: Easing.inOut(Easing.ease),
DECELERATE: Easing.out(Easing.ease),
LINEAR: Easing.linear,
// ═══════════════════════════════════════════════════════════════════════════
// EASING CURVES
// ═══════════════════════════════════════════════════════════════════════════
export const EASE = {
// Standard iOS easing
DEFAULT: Easing.bezier(0.25, 0.1, 0.25, 1),
EASE_OUT: Easing.bezier(0, 0, 0.2, 1),
EASE_IN: Easing.bezier(0.4, 0, 1, 1),
EASE_IN_OUT: Easing.bezier(0.4, 0, 0.2, 1),
// Liquid / Fluid
LIQUID: Easing.bezier(0.4, 0, 0.2, 1),
BOUNCE: Easing.bezier(0.68, -0.55, 0.265, 1.55),
// Sharp / Snappy
SNAPPY: Easing.bezier(0.2, 0, 0, 1),
} as const
// ═══════════════════════════════════════════════════════════════════════════
// SPRING CONFIGS (for react-native Animated.spring)
// ═══════════════════════════════════════════════════════════════════════════
export const SPRING = {
BOUNCY: { tension: 50, friction: 7 },
// Bouncy (playful)
BOUNCY: {
damping: 15,
stiffness: 180,
mass: 1,
},
// Gentle (smooth)
GENTLE: {
damping: 20,
stiffness: 100,
mass: 1,
},
// Snappy (quick)
SNAPPY: {
damping: 18,
stiffness: 300,
mass: 1,
},
// Slow (dramatic)
SLOW: {
damping: 25,
stiffness: 80,
mass: 1.5,
},
// Liquid (fluid)
LIQUID: {
damping: 20,
stiffness: 200,
mass: 1,
},
} as const
// ═══════════════════════════════════════════════════════════════════════════
// PRESET ANIMATION CONFIGS
// ═══════════════════════════════════════════════════════════════════════════
export const ANIMATION = {
PULSE_UP: {
toValue: 1.08,
duration: DURATION.SNAP,
useNativeDriver: true,
},
PULSE_DOWN: {
toValue: 1,
duration: DURATION.QUICK,
easing: EASING.DECELERATE,
useNativeDriver: true,
},
GRADIENT_CROSSFADE: {
toValue: 1,
duration: DURATION.SLOW,
easing: EASING.STANDARD,
useNativeDriver: true,
},
// Fade in
FADE_IN: {
toValue: 1,
duration: DURATION.NORMAL,
useNativeDriver: true,
easing: EASE.EASE_OUT,
},
BREATH_HALF: {
// Scale up (pop in)
POP_IN: {
spring: SPRING.BOUNCY,
},
// Slide up
SLIDE_UP: {
duration: DURATION.NORMAL,
easing: EASE.EASE_OUT,
},
// Liquid morph
LIQUID_MORPH: {
duration: DURATION.LIQUID,
easing: EASE.LIQUID,
},
// Breathing glow
BREATHE: {
duration: DURATION.BREATH,
easing: EASING.STANDARD,
useNativeDriver: false,
easing: EASE.EASE_IN_OUT,
},
// Timer tick pulse
TIMER_TICK: {
duration: 150,
easing: EASE.EASE_OUT,
},
// Phase transition
PHASE_TRANSITION: {
duration: DURATION.PHASE_CHANGE,
easing: EASE.EASE_IN_OUT,
},
} as const

View File

@@ -1,11 +1,23 @@
/**
* TabataFit Border Radius System
* Liquid Glass uses generous rounding
*/
export const RADIUS = {
NONE: 0,
XS: 4,
SM: 6,
SM: 8,
MD: 12,
LG: 16,
XL: 20,
'2XL': 28,
'3XL': 32,
'4XL': 36,
XXL: 24,
XXXL: 32,
// Special
FULL: 9999,
// Liquid glass specific
GLASS_CARD: 20,
GLASS_MODAL: 28,
GLASS_BUTTON: 14,
} as const

View File

@@ -1,76 +1,248 @@
export const PHASE_COLORS = {
IDLE: '#1E1E2E',
GET_READY: '#EAB308',
WORK: '#F97316',
REST: '#3B82F6',
COMPLETE: '#22C55E',
} as const
/**
* TabataFit Color System
* Liquid Glass Design (iOS 18.4 inspired)
*/
export const PHASE_GRADIENTS = {
IDLE: ['#0A0A14', '#12101F', '#1E1E2E'] as const,
GET_READY: ['#451A03', '#92400E', '#D97706'] as const,
WORK: ['#450A0A', '#991B1B', '#EA580C'] as const,
REST: ['#0C1929', '#1E3A5F', '#2563EB'] as const,
COMPLETE: ['#052E16', '#166534', '#16A34A'] as const,
} as const
export const ACCENT = {
ORANGE: '#F97316',
ORANGE_GLOW: '#FB923C',
RED_HOT: '#EF4444',
GOLD: '#FBBF24',
WHITE: '#FFFFFF',
WHITE_DIM: 'rgba(255, 255, 255, 0.6)',
WHITE_FAINT: 'rgba(255, 255, 255, 0.15)',
} as const
// ═══════════════════════════════════════════════════════════════════════════
// BRAND COLORS
// ═══════════════════════════════════════════════════════════════════════════
export const BRAND = {
PRIMARY: '#F97316',
PRIMARY_LIGHT: '#FB923C',
SECONDARY: '#FBBF24',
DANGER: '#EF4444',
SUCCESS: '#22C55E',
INFO: '#3B82F6',
PRIMARY: '#FF6B35',
PRIMARY_LIGHT: '#FF8C5A',
PRIMARY_DARK: '#E55A25',
SECONDARY: '#FFD60A',
DANGER: '#FF453A',
SUCCESS: '#30D158',
INFO: '#5AC8FA',
} as const
export const SURFACE = {
BASE: '#0A0A14',
RAISED: '#12101F',
ELEVATED: '#1E1E2E',
OVERLAY_LIGHT: 'rgba(255, 255, 255, 0.08)',
OVERLAY_MEDIUM: 'rgba(255, 255, 255, 0.15)',
OVERLAY_STRONG: 'rgba(255, 255, 255, 0.25)',
SCRIM: 'rgba(0, 0, 0, 0.3)',
// ═══════════════════════════════════════════════════════════════════════════
// BACKGROUND COLORS (Pure black for OLED)
// ═══════════════════════════════════════════════════════════════════════════
export const DARK = {
BASE: '#000000',
SURFACE: '#1C1C1E',
ELEVATED: '#2C2C2E',
OVERLAY_1: 'rgba(255, 255, 255, 0.05)',
OVERLAY_2: 'rgba(255, 255, 255, 0.08)',
OVERLAY_3: 'rgba(255, 255, 255, 0.12)',
SCRIM: 'rgba(0, 0, 0, 0.6)',
} as const
// ═══════════════════════════════════════════════════════════════════════════
// TEXT COLORS
// ═══════════════════════════════════════════════════════════════════════════
export const TEXT = {
PRIMARY: '#FFFFFF',
SECONDARY: 'rgba(255, 255, 255, 0.85)',
TERTIARY: 'rgba(255, 255, 255, 0.75)',
MUTED: 'rgba(255, 255, 255, 0.6)',
HINT: 'rgba(255, 255, 255, 0.45)',
DISABLED: 'rgba(255, 255, 255, 0.15)',
SECONDARY: '#EBEBF5',
TERTIARY: 'rgba(235, 235, 245, 0.6)',
MUTED: 'rgba(235, 235, 245, 0.5)',
HINT: 'rgba(235, 235, 245, 0.3)',
DISABLED: '#3A3A3C',
} as const
export const BORDER = {
SUBTLE: 'rgba(255, 255, 255, 0.05)',
LIGHT: 'rgba(255, 255, 255, 0.06)',
MEDIUM: 'rgba(255, 255, 255, 0.15)',
STRONG: 'rgba(255, 255, 255, 0.3)',
// ═══════════════════════════════════════════════════════════════════════════
// PHASE COLORS (Timer phases)
// ═══════════════════════════════════════════════════════════════════════════
export const PHASE = {
PREP: '#FF9500',
PREP_LIGHT: 'rgba(255, 149, 0, 0.2)',
WORK: '#FF6B35',
WORK_LIGHT: 'rgba(255, 107, 53, 0.2)',
WORK_GLOW: 'rgba(255, 107, 53, 0.5)',
REST: '#5AC8FA',
REST_LIGHT: 'rgba(90, 200, 250, 0.2)',
REST_GLOW: 'rgba(90, 200, 250, 0.5)',
COMPLETE: '#30D158',
COMPLETE_LIGHT: 'rgba(48, 209, 88, 0.2)',
} as const
export const APP_GRADIENTS = {
HOME: ['#0A0A14', '#1A0E2E', '#2D1810'] as const,
} as const
// ═══════════════════════════════════════════════════════════════════════════
// LIQUID GLASS SYSTEM
// ═══════════════════════════════════════════════════════════════════════════
export const GLASS = {
FILL: 'rgba(255, 255, 255, 0.03)',
FILL_MEDIUM: 'rgba(255, 255, 255, 0.06)',
FILL_STRONG: 'rgba(255, 255, 255, 0.10)',
BORDER: 'rgba(255, 255, 255, 0.08)',
BORDER_TOP: 'rgba(255, 255, 255, 0.15)',
BLUR_LIGHT: 15,
BLUR_MEDIUM: 25,
BLUR_HEAVY: 40,
BLUR_ATMOSPHERE: 60,
// Base glass surface
BASE: {
backgroundColor: 'rgba(255, 255, 255, 0.05)',
borderColor: 'rgba(255, 255, 255, 0.1)',
borderWidth: 1,
},
// Elevated glass (cards, modals)
ELEVATED: {
backgroundColor: 'rgba(255, 255, 255, 0.08)',
borderColor: 'rgba(255, 255, 255, 0.15)',
borderWidth: 1,
},
// Inset glass (inputs, controls)
INSET: {
backgroundColor: 'rgba(0, 0, 0, 0.25)',
borderColor: 'rgba(255, 255, 255, 0.05)',
borderWidth: 1,
},
// Brand tinted glass
TINTED: {
backgroundColor: 'rgba(255, 107, 53, 0.12)',
borderColor: 'rgba(255, 107, 53, 0.25)',
borderWidth: 1,
},
// Success tinted
SUCCESS_TINTED: {
backgroundColor: 'rgba(48, 209, 88, 0.12)',
borderColor: 'rgba(48, 209, 88, 0.25)',
borderWidth: 1,
},
// Blur intensities (for expo-blur)
BLUR_LIGHT: 20,
BLUR_MEDIUM: 40,
BLUR_HEAVY: 60,
BLUR_ULTRA: 80,
} as const
// ═══════════════════════════════════════════════════════════════════════════
// SHADOWS & GLOWS
// ═══════════════════════════════════════════════════════════════════════════
export const SHADOW = {
// Glass shadows
sm: {
shadowColor: '#000',
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.15,
shadowRadius: 12,
elevation: 4,
},
md: {
shadowColor: '#000',
shadowOffset: { width: 0, height: 8 },
shadowOpacity: 0.25,
shadowRadius: 24,
elevation: 8,
},
lg: {
shadowColor: '#000',
shadowOffset: { width: 0, height: 12 },
shadowOpacity: 0.35,
shadowRadius: 40,
elevation: 12,
},
xl: {
shadowColor: '#000',
shadowOffset: { width: 0, height: 16 },
shadowOpacity: 0.4,
shadowRadius: 48,
elevation: 16,
},
// Brand glow
BRAND_GLOW: {
shadowColor: BRAND.PRIMARY,
shadowOffset: { width: 0, height: 0 },
shadowOpacity: 0.5,
shadowRadius: 20,
elevation: 10,
},
BRAND_GLOW_SUBTLE: {
shadowColor: BRAND.PRIMARY,
shadowOffset: { width: 0, height: 0 },
shadowOpacity: 0.3,
shadowRadius: 12,
elevation: 6,
},
// Liquid glow (breathing effect base)
LIQUID_GLOW: {
shadowColor: BRAND.PRIMARY,
shadowOffset: { width: 0, height: 0 },
shadowOpacity: 0.4,
shadowRadius: 30,
elevation: 15,
},
} as const
// ═══════════════════════════════════════════════════════════════════════════
// BORDER COLORS
// ═══════════════════════════════════════════════════════════════════════════
export const BORDER = {
GLASS: 'rgba(255, 255, 255, 0.1)',
GLASS_LIGHT: 'rgba(255, 255, 255, 0.05)',
GLASS_STRONG: 'rgba(255, 255, 255, 0.2)',
BRAND: 'rgba(255, 107, 53, 0.3)',
SUCCESS: 'rgba(48, 209, 88, 0.3)',
} as const
// ═══════════════════════════════════════════════════════════════════════════
// GRADIENTS
// ═══════════════════════════════════════════════════════════════════════════
export const GRADIENTS = {
// Video overlays
VIDEO_OVERLAY: ['transparent', 'rgba(0, 0, 0, 0.8)'],
VIDEO_TOP: ['rgba(0, 0, 0, 0.5)', 'transparent'],
// Phase gradients
WORK: [BRAND.PRIMARY, BRAND.PRIMARY_LIGHT],
REST: [PHASE.REST, '#7DD3FC'],
PREP: [PHASE.PREP, '#FFB340'],
// CTA
CTA: [BRAND.PRIMARY, BRAND.PRIMARY_LIGHT],
// Glass shimmers
GLASS_SHIMMER: ['rgba(255,255,255,0.1)', 'rgba(255,255,255,0.05)', 'rgba(255,255,255,0.1)'],
} as const
// ═══════════════════════════════════════════════════════════════════════════
// PHASE COLORS (For timer UI)
// ═══════════════════════════════════════════════════════════════════════════
export const PHASE_COLORS: Record<string, { fill: string; glow: string }> = {
PREP: {
fill: PHASE.PREP,
glow: 'rgba(255, 149, 0, 0.5)',
},
WORK: {
fill: BRAND.PRIMARY,
glow: PHASE.WORK_GLOW,
},
REST: {
fill: PHASE.REST,
glow: PHASE.REST_GLOW,
},
COMPLETE: {
fill: PHASE.COMPLETE,
glow: 'rgba(48, 209, 88, 0.5)',
},
}
// ═══════════════════════════════════════════════════════════════════════════
// BARREL EXPORT
// ═══════════════════════════════════════════════════════════════════════════
export const COLORS = {
...BRAND,
...DARK,
...TEXT,
...PHASE,
...SHADOW,
BRAND,
DARK,
TEXT,
PHASE,
GLASS,
BORDER,
GRADIENTS,
} as const

View File

@@ -1,16 +1,10 @@
export {
PHASE_COLORS,
PHASE_GRADIENTS,
ACCENT,
BRAND,
SURFACE,
TEXT,
BORDER,
APP_GRADIENTS,
GLASS,
} from './colors'
export { TYPOGRAPHY } from './typography'
export { SPACING, LAYOUT } from './spacing'
export { SHADOW, TEXT_SHADOW } from './shadows'
export { RADIUS } from './borderRadius'
export { DURATION, EASING, SPRING, ANIMATION } from './animations'
/**
* TabataFit Design System Constants
* Liquid Glass Design
*/
export * from './colors'
export * from './typography'
export * from './spacing'
export * from './borderRadius'
export * from './animations'

View File

@@ -1,25 +1,42 @@
/**
* TabataFit Spacing System
* Base: 4px
*/
export const SPACING = {
0: 0,
0.5: 2,
1: 4,
1.5: 6,
2: 8,
2.5: 10,
3: 12,
3.5: 14,
4: 16,
5: 20,
6: 24,
7: 28,
8: 32,
10: 40,
12: 48,
14: 56,
15: 60,
16: 64,
20: 80,
24: 96,
} as const
export const SPACE = {
NONE: 0,
XS: 4,
SM: 8,
MD: 16,
LG: 24,
XL: 32,
XXL: 48,
} as const
export const LAYOUT = {
PAGE_HORIZONTAL: 24,
SECTION_GAP: 40,
INLINE_GAP: 16,
CONTROLS_GAP: 32,
SCREEN_PADDING: 24,
CARD_PADDING: 16,
BUTTON_HEIGHT: 56,
BUTTON_HEIGHT_SM: 44,
TAB_BAR_HEIGHT: 83,
HEADER_HEIGHT: 44,
TOUCH_TARGET: 44,
} as const

View File

@@ -1,73 +1,262 @@
import type { TextStyle } from 'react-native'
/**
* TabataFit Typography System
* Inter font family, Apple-inspired scale
*/
import { TextStyle } from 'react-native'
// ═══════════════════════════════════════════════════════════════════════════
// FONT WEIGHTS (using Inter from @expo-google-fonts/inter)
// ═══════════════════════════════════════════════════════════════════════════
const FONT = {
REGULAR: 'Inter_400Regular',
MEDIUM: 'Inter_500Medium',
SEMIBOLD: 'Inter_600SemiBold',
BOLD: 'Inter_700Bold',
BLACK: 'Inter_900Black',
} as const
// ═══════════════════════════════════════════════════════════════════════════
// TYPE SCALE
// ═══════════════════════════════════════════════════════════════════════════
export const TYPOGRAPHY = {
countdown: {
fontSize: 140,
fontWeight: '900',
fontVariant: ['tabular-nums'],
// Display / Hero
HERO: {
fontFamily: FONT.BLACK,
fontSize: 48,
lineHeight: 56,
letterSpacing: -1,
} as TextStyle,
timeDisplay: {
fontSize: 72,
fontWeight: '900',
fontVariant: ['tabular-nums'],
// Large Title (like iOS)
LARGE_TITLE: {
fontFamily: FONT.BOLD,
fontSize: 34,
lineHeight: 41,
letterSpacing: 0.37,
} as TextStyle,
brandTitle: {
fontSize: 56,
fontWeight: '900',
letterSpacing: 12,
// Title 1
TITLE_1: {
fontFamily: FONT.BOLD,
fontSize: 28,
lineHeight: 34,
letterSpacing: 0.36,
} as TextStyle,
displayLarge: {
fontSize: 42,
fontWeight: '900',
letterSpacing: 4,
// Title 2
TITLE_2: {
fontFamily: FONT.BOLD,
fontSize: 22,
lineHeight: 28,
letterSpacing: 0.35,
} as TextStyle,
displaySmall: {
fontSize: 32,
fontWeight: '900',
letterSpacing: 20,
} as TextStyle,
buttonHero: {
fontSize: 30,
fontWeight: '900',
letterSpacing: 5,
} as TextStyle,
heading: {
fontSize: 24,
fontWeight: '800',
} as TextStyle,
buttonMedium: {
// Title 3
TITLE_3: {
fontFamily: FONT.SEMIBOLD,
fontSize: 20,
fontWeight: '700',
letterSpacing: 2,
lineHeight: 25,
letterSpacing: 0.38,
} as TextStyle,
body: {
fontSize: 18,
fontWeight: '700',
// Headline
HEADLINE: {
fontFamily: FONT.SEMIBOLD,
fontSize: 17,
lineHeight: 22,
letterSpacing: -0.41,
} as TextStyle,
caption: {
// Body
BODY: {
fontFamily: FONT.REGULAR,
fontSize: 17,
lineHeight: 22,
letterSpacing: -0.41,
} as TextStyle,
// Body Bold
BODY_BOLD: {
fontFamily: FONT.SEMIBOLD,
fontSize: 17,
lineHeight: 22,
letterSpacing: -0.41,
} as TextStyle,
// Callout
CALLOUT: {
fontFamily: FONT.REGULAR,
fontSize: 16,
fontWeight: '500',
lineHeight: 21,
letterSpacing: -0.32,
} as TextStyle,
label: {
// Subheadline
SUBHEADLINE: {
fontFamily: FONT.REGULAR,
fontSize: 15,
fontWeight: '800',
letterSpacing: 1,
lineHeight: 20,
letterSpacing: -0.24,
} as TextStyle,
overline: {
fontSize: 11,
fontWeight: '600',
textTransform: 'uppercase',
letterSpacing: 1,
// Footnote
FOOTNOTE: {
fontFamily: FONT.REGULAR,
fontSize: 13,
lineHeight: 18,
letterSpacing: -0.08,
} as TextStyle,
// Caption 1
CAPTION_1: {
fontFamily: FONT.REGULAR,
fontSize: 12,
lineHeight: 16,
letterSpacing: 0,
} as TextStyle,
// Caption 2
CAPTION_2: {
fontFamily: FONT.REGULAR,
fontSize: 11,
lineHeight: 13,
letterSpacing: 0.07,
} as TextStyle,
// ─────────────────────────────────────────────────────────────────────────
// SPECIAL: TIMER TYPOGRAPHY
// ─────────────────────────────────────────────────────────────────────────
// Main countdown number
TIMER_NUMBER: {
fontFamily: FONT.BLACK,
fontSize: 96,
lineHeight: 96,
letterSpacing: -2,
fontVariant: ['tabular-nums'],
} as TextStyle,
// Timer number (compact version)
TIMER_NUMBER_COMPACT: {
fontFamily: FONT.BLACK,
fontSize: 72,
lineHeight: 72,
letterSpacing: -1.5,
fontVariant: ['tabular-nums'],
} as TextStyle,
// Phase label (WORK, REST)
TIMER_PHASE: {
fontFamily: FONT.BOLD,
fontSize: 24,
lineHeight: 28,
letterSpacing: 2,
textTransform: 'uppercase',
} as TextStyle,
// Round indicator
TIMER_ROUND: {
fontFamily: FONT.MEDIUM,
fontSize: 17,
lineHeight: 22,
letterSpacing: 0,
fontVariant: ['tabular-nums'],
} as TextStyle,
// Exercise name during workout
EXERCISE_NAME: {
fontFamily: FONT.BOLD,
fontSize: 28,
lineHeight: 34,
letterSpacing: 0.36,
textAlign: 'center',
} as TextStyle,
// ─────────────────────────────────────────────────────────────────────────
// SPECIAL: BUTTON TYPOGRAPHY
// ─────────────────────────────────────────────────────────────────────────
BUTTON_LARGE: {
fontFamily: FONT.SEMIBOLD,
fontSize: 18,
lineHeight: 22,
letterSpacing: 0.5,
} as TextStyle,
BUTTON_MEDIUM: {
fontFamily: FONT.SEMIBOLD,
fontSize: 16,
lineHeight: 20,
letterSpacing: 0.3,
} as TextStyle,
BUTTON_SMALL: {
fontFamily: FONT.SEMIBOLD,
fontSize: 14,
lineHeight: 18,
letterSpacing: 0.2,
} as TextStyle,
// ─────────────────────────────────────────────────────────────────────────
// SPECIAL: CARD TYPOGRAPHY
// ─────────────────────────────────────────────────────────────────────────
CARD_TITLE: {
fontFamily: FONT.SEMIBOLD,
fontSize: 17,
lineHeight: 22,
letterSpacing: -0.41,
} as TextStyle,
CARD_SUBTITLE: {
fontFamily: FONT.REGULAR,
fontSize: 14,
lineHeight: 18,
letterSpacing: -0.15,
} as TextStyle,
CARD_METADATA: {
fontFamily: FONT.MEDIUM,
fontSize: 13,
lineHeight: 16,
letterSpacing: 0,
} as TextStyle,
// ─────────────────────────────────────────────────────────────────────────
// SPECIAL: STATS TYPOGRAPHY
// ─────────────────────────────────────────────────────────────────────────
STAT_VALUE: {
fontFamily: FONT.BLACK,
fontSize: 32,
lineHeight: 38,
letterSpacing: -0.5,
fontVariant: ['tabular-nums'],
} as TextStyle,
STAT_LABEL: {
fontFamily: FONT.MEDIUM,
fontSize: 12,
lineHeight: 16,
letterSpacing: 0.5,
textTransform: 'uppercase',
} as TextStyle,
// ─────────────────────────────────────────────────────────────────────────
// SPECIAL: OVERLINE / SECTION HEADER
// ─────────────────────────────────────────────────────────────────────────
OVERLINE: {
fontFamily: FONT.SEMIBOLD,
fontSize: 13,
lineHeight: 16,
letterSpacing: 1.5,
textTransform: 'uppercase',
} as TextStyle,
} as const
export { FONT }