Heading

Semantic heading with configurable element, size, color, and weight.

API Reference

PropTypeDefault
as"h1" | "h2" | "h3" | "h4" | "h5" | "h6""h2"
size"xl" | "lg" | "md" | "sm" | "xs""md"
color"root" | "root-contrast" | "root-100" | "root-100-contrast" | "root-200" | "root-200-contrast" | "primary" | "primary-contrast" | "secondary" | "secondary-contrast" | "accent" | "accent-contrast""root-contrast"
weight100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900

Sizes

Heading xl

Heading lg

Heading md

Heading sm

Heading xs

<Heading size="xl">Heading xl</Heading>
<Heading size="lg">Heading lg</Heading>
<Heading size="md">Heading md</Heading>
<Heading size="sm">Heading sm</Heading>
<Heading size="xs">Heading xs</Heading>

Colors

Root Contrast

Primary

Secondary

Accent

<Heading color="root-contrast">Root Contrast</Heading>
<Heading color="primary">Primary</Heading>
<Heading color="secondary">Secondary</Heading>
<Heading color="accent">Accent</Heading>

Weights

Light (300)

Normal (400)

Semibold (600)

Bold (700)

Black (900)

<Heading weight={300}>Light (300)</Heading>
<Heading weight={400}>Normal (400)</Heading>
<Heading weight={600}>Semibold (600)</Heading>
<Heading weight={700}>Bold (700)</Heading>
<Heading weight={900}>Black (900)</Heading>

Basic Usage

import { Heading } from "@/ui";

<Heading>Title</Heading>

Source

src/ui/Heading.tsx
import { cn } from "@/utils";

type HeadingColor =
  | "root"
  | "root-contrast"
  | "root-100"
  | "root-100-contrast"
  | "root-200"
  | "root-200-contrast"
  | "primary"
  | "primary-contrast"
  | "secondary"
  | "secondary-contrast"
  | "accent"
  | "accent-contrast";

type HeadingSize = "xl" | "lg" | "md" | "sm" | "xs";

type HeadingElement = "h1" | "h2" | "h3" | "h4" | "h5" | "h6";

type FontWeight = 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900;

const weightClass: Record<FontWeight, string> = {
  100: "font-thin",
  200: "font-extralight",
  300: "font-light",
  400: "font-normal",
  500: "font-medium",
  600: "font-semibold",
  700: "font-bold",
  800: "font-extrabold",
  900: "font-black",
};

const sizeClass: Record<HeadingSize, string> = {
  xl: "text-6xl font-bold leading-tight tracking-tight text-balance md:text-7xl lg:text-9xl",
  lg: "text-5xl font-semibold leading-tight tracking-tight text-balance sm:text-6xl lg:text-7xl",
  md: "text-3xl font-semibold leading-tight text-balance sm:text-4xl",
  sm: "text-2xl font-semibold leading-snug text-balance",
  xs: "text-base font-semibold leading-snug",
};

const colorClass: Record<HeadingColor, string> = {
  root: "text-root",
  "root-contrast": "text-root-contrast",
  "root-100": "text-root-100",
  "root-100-contrast": "text-root-100-contrast",
  "root-200": "text-root-200",
  "root-200-contrast": "text-root-200-contrast",
  primary: "text-primary",
  "primary-contrast": "text-primary-contrast",
  secondary: "text-secondary",
  "secondary-contrast": "text-secondary-contrast",
  accent: "text-accent",
  "accent-contrast": "text-accent-contrast",
};

interface HeadingProps
  extends Omit<React.HTMLAttributes<HTMLHeadingElement>, "color"> {
  as?: HeadingElement;
  size?: HeadingSize;
  color?: HeadingColor;
  weight?: FontWeight;
}

export function Heading({
  as: Tag = "h2",
  size = "md",
  color = "root-contrast",
  weight,
  className,
  children,
  ...props
}: HeadingProps) {
  return (
    <Tag
      className={cn(
        sizeClass[size],
        colorClass[color],
        weight && weightClass[weight],
        className,
      )}
      {...props}
    >
      {children}
    </Tag>
  );
}