Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | 1x 1x 13x 13x | import type { ComponentPropsWithoutRef, ElementType } from "react";
import { cn } from "@/utils/class-name";
/**
* Semantic text variants for the dark theme.
* Update a variant here and every usage in the app updates automatically.
*
* Hierarchy:
* display / title — Syne, large headings, full white
* eyebrow / sectionTitle — Syne, labels and panel headings
* subtitle / body — DM Sans, primary content copy
* muted / caption — DM Sans, secondary/supporting copy
* helper — DM Sans, tiny disclaimers / helper text
* inherit / error / warning — special-purpose
*/
const TEXT_VARIANT_CLASSES = {
// ── Headings (Syne) ──────────────────────────────────────────────────────
display: "font-syne text-3xl font-bold tracking-tight text-white",
title: "font-syne text-2xl font-bold text-white",
/** Uppercase section label: "EMPLOYEE ASSISTANT", "CURRENT CHAT" */
eyebrow:
"font-syne text-xs font-semibold uppercase tracking-[0.2em] text-white/55",
/** Quiet uppercase section label */
eyebrowMuted:
"font-syne text-xs font-semibold uppercase tracking-[0.2em] text-white/50",
/** Panel / card heading: "User mode selection", "Provider" */
sectionTitle: "font-syne text-sm font-semibold text-white/80",
// ── Body copy (DM Sans) ──────────────────────────────────────────────────
/** Long-form subtitle under a heading */
subtitle: "font-dm-sans text-[15px] leading-7 text-white/70",
/** Default body text */
body: "font-dm-sans text-sm text-white/80",
/** Emphasized body text (same size, stronger weight) */
bodyStrong: "font-dm-sans text-sm font-medium text-white/80",
/** One step quieter than body — descriptions, secondary info */
muted: "font-dm-sans text-sm text-white/65",
/** Small supporting text — timestamps, previews, labels */
caption: "font-dm-sans text-xs text-white/60",
/** Small supporting text with stronger contrast */
captionStrong: "font-dm-sans text-xs text-white/65",
/** Small supporting text with softer contrast */
captionMuted: "font-dm-sans text-xs text-white/55",
/** Tiny disclaimer / hint text under inputs */
helper: "font-dm-sans text-[11px] text-white/50",
// ── Special-purpose ──────────────────────────────────────────────────────
/** Inherits color from parent — useful inside colored containers */
inherit: "text-sm text-inherit",
error: "font-dm-sans text-sm text-red-400",
warning: "font-dm-sans text-xs text-amber-400",
} as const;
type TextVariant = keyof typeof TEXT_VARIANT_CLASSES;
type TextProps<T extends ElementType> = {
as?: T;
variant?: TextVariant;
className?: string;
} & Omit<ComponentPropsWithoutRef<T>, "as" | "className">;
export function Text<T extends ElementType = "p">({
as,
variant = "body",
className,
...props
}: TextProps<T>) {
const Component = as ?? "p";
return (
<Component
className={cn(TEXT_VARIANT_CLASSES[variant], className)}
{...props}
/>
);
}
|