Files
aragrp/lib/typography.ts
ar.badieiandCursor c2376b3b9e
Build & Push Docker Image / build (push) Has been cancelled
feat: redesign landing page with brand system and section blocks
Refresh the homepage around new hero/services/roadmap sections, shared design tokens, and updated brand assets.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-23 17:00:55 +03:30

184 lines
7.9 KiB
TypeScript

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 `<Typography>`.
*/
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<typeof typographyVariants>["variant"]
>;
/** Shorthand class strings for inline use: `className={type.h2}` */
export const type: Record<TypographyVariant, string> = {
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);
}