import { cva, type VariantProps } from "class-variance-authority"; import { cn } from "@/lib/utils"; /** * Site-wide typography scale optimized for Persian (RTL) readability. * * Hierarchy (requested → variant): * - Heading 1 → `h1` / `display` * - Heading 2 → `h2` * - Heading 3 → `h3` * - Body Large → `body-lg` * - Body → `body` * - Small Text → `body-sm` * - Caption → `caption` * - Button Text → `button` / `button-lg` * - Navigation → `nav` / `nav-mobile` * * Alignment: prefer `text-start` (right in RTL). * Section captions (`section-intro`) use justify + last-line start for 2+ lines. * Reading measure: keep paragraphs within `typeMeasure.*` max-widths. * * Use via `typography({ variant })`, `type.*` shorthand, or ``. */ export const typographyVariants = cva("tracking-normal", { variants: { variant: { /** Large marketing hero titles — may be centered via className */ display: "text-start text-3xl font-bold leading-[1.55] text-balance sm:text-4xl lg:text-5xl", /** Main page titles */ h1: "text-start text-3xl font-extrabold leading-[1.45] text-balance md:text-4xl lg:text-5xl", /** Major section headings */ h2: "text-start text-2xl font-bold leading-[1.45] text-balance md:text-3xl lg:text-4xl", /** Subsection / feature block headings */ h3: "text-start text-lg font-bold leading-[1.5] md:text-xl", /** Compact block headings, mini sections */ h4: "text-start text-base font-bold leading-[1.5] md:text-lg", /** Prominent intro paragraphs — same size as section captions */ "body-lg": "text-start text-lg leading-[1.8]", /** Standard paragraphs */ body: "text-start text-base leading-[1.8]", /** Secondary / denser copy */ "body-sm": "text-start text-sm leading-[1.75]", /** Metadata, timestamps, footnotes — fixed size site-wide */ caption: "text-start text-xs leading-[1.6] text-muted-foreground", /** Dense UI chrome (mockups, compact badges, timeline steps) — prefer caption when possible */ micro: "text-start text-[0.6875rem] leading-[1.5]", /** Section eyebrow / badge labels */ eyebrow: "text-start text-sm font-medium leading-normal text-muted-foreground", /** Form labels, settings labels */ label: "text-start text-sm font-medium leading-none", /** Header / menu items (desktop) */ nav: "text-start text-sm font-medium leading-none", /** Mobile navigation items */ "nav-mobile": "text-start text-base font-medium leading-snug", /** Default / sm button labels — optically centered in the control */ button: "text-sm font-medium leading-none", /** Large button labels (hero / marketing CTAs) */ "button-lg": "text-base font-medium leading-none", /** Titles inside cards and panels */ "card-title": "text-start text-base font-bold leading-snug", /** Supporting copy inside cards */ "card-desc": "text-start text-sm leading-[1.75] text-muted-foreground", /** Chat message bubbles */ chat: "text-start text-sm leading-[1.75]", /** Log panels, JSON, technical output */ code: "text-start font-mono text-xs leading-relaxed", /** Large stat numbers */ stat: "text-start text-4xl font-bold leading-none text-primary md:text-5xl", /** Inline nav / footer links */ link: "text-start text-sm leading-normal text-muted-foreground", /** * Section captions under headings (muted). * Multi-line: justify; last line stays start (right in RTL). * Single-line: justify has no visual stretch — stays start-aligned. */ "section-intro": "text-justify text-lg leading-[1.8] text-muted-foreground [text-align-last:start]", /** Pull quotes / emphasis blocks */ emphasis: "text-start text-base font-medium leading-[1.8] text-foreground", /** Accordion / list item titles */ "list-title": "text-start text-base font-medium leading-snug", /** CTA section headings (slightly larger than h2) */ "cta-title": "text-start text-2xl font-bold leading-[1.45] text-balance md:text-3xl lg:text-4xl", /** Decorative error page numbers */ "display-mono": "text-start text-[5rem] font-black leading-none sm:text-[6rem] md:text-[8rem]", }, }, defaultVariants: { variant: "body", }, }); export type TypographyVariant = NonNullable< VariantProps["variant"] >; /** Shorthand class strings for inline use: `className={type.h2}` */ export const type: Record = { display: "text-3xl font-bold leading-[1.55] tracking-normal text-balance text-start sm:text-4xl lg:text-5xl", h1: "text-3xl font-extrabold leading-[1.45] tracking-normal text-balance text-start md:text-4xl lg:text-5xl", h2: "text-2xl font-bold leading-[1.45] tracking-normal text-balance text-start md:text-3xl lg:text-4xl", h3: "text-lg font-bold leading-[1.5] tracking-normal text-start md:text-xl", h4: "text-base font-bold leading-[1.5] tracking-normal text-start md:text-lg", "body-lg": "text-lg leading-[1.8] tracking-normal text-start", body: "text-base leading-[1.8] tracking-normal text-start", "body-sm": "text-sm leading-[1.75] tracking-normal text-start", caption: "text-xs leading-[1.6] tracking-normal text-start text-muted-foreground", micro: "text-[0.6875rem] leading-[1.5] tracking-normal text-start", eyebrow: "text-sm font-medium leading-normal tracking-normal text-start text-muted-foreground", label: "text-sm font-medium leading-none tracking-normal text-start", nav: "text-sm font-medium leading-none tracking-normal text-start", "nav-mobile": "text-base font-medium leading-snug tracking-normal text-start", button: "text-sm font-medium leading-none tracking-normal", "button-lg": "text-base font-medium leading-none tracking-normal", "card-title": "text-base font-bold leading-snug tracking-normal text-start", "card-desc": "text-sm leading-[1.75] tracking-normal text-start text-muted-foreground", chat: "text-sm leading-[1.75] tracking-normal text-start", code: "font-mono text-xs leading-relaxed tracking-normal text-start", stat: "text-4xl font-bold leading-none tracking-normal text-start text-primary md:text-5xl", link: "text-sm leading-normal tracking-normal text-start text-muted-foreground", "section-intro": "text-lg leading-[1.8] tracking-normal text-justify text-muted-foreground [text-align-last:start]", emphasis: "text-base font-medium leading-[1.8] tracking-normal text-start text-foreground", "list-title": "text-base font-medium leading-snug tracking-normal text-start", "cta-title": "text-2xl font-bold leading-[1.45] tracking-normal text-balance text-start md:text-3xl lg:text-4xl", "display-mono": "text-[5rem] font-black leading-none tracking-tighter text-start sm:text-[6rem] md:text-[8rem]", }; /** * Comfortable reading widths for Persian body copy. * Prefer these over stretching lines across the full container. */ export const typeMeasure = { /** Short support line under a heading */ narrow: "max-w-md", /** Standard paragraph / section intro */ prose: "max-w-2xl", /** Longer multi-paragraph blocks */ wide: "max-w-3xl", } as const; /** Text-related spacing tied to typography hierarchy */ export const typeSpacing = { /** Eyebrow → heading */ eyebrowToHeading: "mt-2", /** Heading → supporting paragraph */ headingToBody: "mt-3", /** Section title block */ sectionStack: "space-y-4", /** Page hero block */ heroStack: "space-y-6", /** Card title → description */ cardStack: "gap-1.5", /** Stacked paragraphs */ paragraphStack: "space-y-4", /** Label → input */ labelToControl: "gap-1.5", /** Message → timestamp in chat */ chatMeta: "space-y-1", } as const; export function typography( variant: TypographyVariant, className?: string ): string { return cn(typographyVariants({ variant }), className); }