// WealthX Design System — Typography (rem-based, Figtree) // // Ported from packages/ui/src/theme/typography.ts. // Font updated: Proxima Nova → Figtree (WealthX shadcn design system). // Base: 16px (1rem). All values match Figma design spec. // // Usage: // import { TYPOGRAPHY, getTypographyCssVars } from "@wealthx/shadcn/typography" // // CSS classes (applied via globals.css @layer utilities): // .text-display-large, .text-display-medium, .text-display-small // .text-h1 … .text-h6 // .text-body-large, .text-body-medium, .text-body-small // .text-label-large, .text-label-medium, .text-label-small // .text-button, .text-button-xs // .text-caption, .text-overline, .text-code export const FONT_FAMILY_SANS = '"Figtree", ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"'; export interface TypographyStyle { fontWeight: number; fontSize: string; lineHeight: string; letterSpacing?: string; textTransform?: "uppercase" | "none"; } function style( weight: number, size: string, lineHeight: string, extra?: Partial>, ): TypographyStyle { return { fontWeight: weight, fontSize: size, lineHeight, ...extra }; } /** Display — hero and major titles */ export const TYPOGRAPHY_DISPLAY = { large: style(700, "4rem", "4.5rem", { letterSpacing: "-0.03125rem" }), medium: style(700, "3rem", "3.5rem"), small: style(600, "2.125rem", "2.625rem"), } as const; /** Headings H1–H6 */ export const TYPOGRAPHY_HEADING = { h1: style(700, "1.875rem", "2.25rem"), h2: style(600, "1.75rem", "2.25rem"), h3: style(600, "1.5rem", "2rem"), h4: style(600, "1.25rem", "1.625rem"), h5: style(600, "1.125rem", "1.5rem"), h6: style(600, "0.9375rem", "1.25rem"), } as const; /** Body text */ export const TYPOGRAPHY_BODY = { large: style(400, "1.125rem", "1.75rem"), medium: style(400, "1rem", "1.5rem"), small: style(400, "0.875rem", "1.25rem"), } as const; /** Labels — buttons, form labels, badges */ export const TYPOGRAPHY_LABEL = { large: style(600, "1rem", "1.25rem"), medium: style(600, "0.9375rem", "1.25rem"), small: style(600, "0.8125rem", "1.125rem", { letterSpacing: "0.03125rem" }), } as const; /** Button — interactive control text (weight 600 / SemiBold) */ export const TYPOGRAPHY_BUTTON = { default: style(600, "0.875rem", "1.25rem"), xs: style(600, "0.75rem", "1.25rem"), } as const; /** Utility — caption, overline, code */ export const TYPOGRAPHY_UTILITY = { caption: style(500, "0.8125rem", "1.125rem"), overline: style(600, "0.75rem", "1rem", { letterSpacing: "0.09375rem", textTransform: "uppercase", }), code: style(400, "0.875rem", "1.25rem"), } as const; export const TYPOGRAPHY = { display: TYPOGRAPHY_DISPLAY, heading: TYPOGRAPHY_HEADING, body: TYPOGRAPHY_BODY, label: TYPOGRAPHY_LABEL, button: TYPOGRAPHY_BUTTON, utility: TYPOGRAPHY_UTILITY, } as const; /** Responsive overrides — Desktop (default), Tablet (≤1024px), Mobile (≤768px) */ export const TYPOGRAPHY_RESPONSIVE = { tablet: { "display-large": style(700, "3.6rem", "4.05rem", { letterSpacing: "-0.03125rem", }), "display-medium": style(700, "2.7rem", "3.15rem"), "display-small": style(600, "1.9125rem", "2.3625rem"), }, mobile: { "display-large": style(700, "2.5rem", "2.8125rem", { letterSpacing: "-0.03125rem", }), "display-medium": style(700, "2rem", "2.334rem"), h1: style(700, "1.625rem", "2rem"), h2: style(600, "1.5rem", "1.93rem"), }, } as const; /** Build all typography CSS variables as a flat Record for :root injection */ export function getTypographyCssVars( fontFamily = FONT_FAMILY_SANS, ): Record { const vars: Record = {}; // Font family vars["--font-family-sans"] = fontFamily; const allGroups: [string, Record][] = [ [ "display", TYPOGRAPHY_DISPLAY as unknown as Record, ], ["", TYPOGRAPHY_HEADING as unknown as Record], ["body", TYPOGRAPHY_BODY as unknown as Record], ["label", TYPOGRAPHY_LABEL as unknown as Record], ["button", TYPOGRAPHY_BUTTON as unknown as Record], ["", TYPOGRAPHY_UTILITY as unknown as Record], ]; for (const [prefix, group] of allGroups) { for (const [key, s] of Object.entries(group)) { const token = prefix ? `${prefix}-${key}` : key; vars[`--typography-${token}-size`] = s.fontSize; vars[`--typography-${token}-leading`] = s.lineHeight; vars[`--typography-${token}-weight`] = String(s.fontWeight); if (s.letterSpacing) vars[`--typography-${token}-tracking`] = s.letterSpacing; if (s.textTransform) vars[`--typography-${token}-transform`] = s.textTransform; } } return vars; } /** Build responsive CSS variable overrides for a given breakpoint */ export function getResponsiveTypographyCssVars( breakpoint: "tablet" | "mobile", ): Record { const vars: Record = {}; for (const [key, s] of Object.entries(TYPOGRAPHY_RESPONSIVE[breakpoint])) { vars[`--typography-${key}-size`] = s.fontSize; vars[`--typography-${key}-leading`] = s.lineHeight; vars[`--typography-${key}-weight`] = String(s.fontWeight); if (s.letterSpacing) vars[`--typography-${key}-tracking`] = s.letterSpacing; } return vars; }