Headings

Versatile heading components with various styles, sizes, and customization options. Supports different HTML tags, sizes, weights, colors, and text transformations. previews

Default Heading

A basic heading component with default settings.

 
<h2
  class="text-xl font-bold text-left normal-case tracking-normal text-gray-500 dark:text-gray-400">
  Default Heading
</h2>
---
import { Heading } from '@/components/ui/heading';

---

<Heading>Default Heading</Heading>
---
// Imports
import { type HTMLAttributes } from "astro/types";
import { tv, type VariantProps } from "@utils/custom-tv";
import { twMerge } from "tailwind-merge";
import {
  type ColorName,
  colorPalette,
  getHardTextClass,
  getOutlinedTextClass,
} from "@utils/colorUtils";

// PropTypes for documentation
export const propTypes = {
  as: {
    type: ["h1", "h2", "h3", "h4", "h5", "h6", "p", "span", "div"],
    description: "The HTML element to render the heading as",
    default: "h2",
  },
  size: {
    type: ["xs", "sm", "base", "lg", "xl", "2xl", "3xl", "4xl", "5xl"],
    description: "The size of the heading",
    default: "xl",
  },
  weight: {
    type: ["light", "normal", "medium", "semibold", "bold", "extrabold"],
    description: "The font weight of the heading",
    default: "bold",
  },
  color: {
    type: Object.keys(colorPalette),
    description: "The color of the heading",
    default: "gray",
  },
  align: {
    type: ["left", "center", "right"],
    description: "The text alignment of the heading",
    default: "left",
  },
  transform: {
    type: ["normal", "uppercase", "lowercase", "capitalize"],
    description: "The text transform of the heading",
    default: "normal",
  },
  tracking: {
    type: ["tighter", "tight", "normal", "wide", "wider", "widest"],
    description: "The letter spacing of the heading",
    default: "normal",
  },
  class: {
    type: "string",
    description: "Additional CSS classes to apply to the heading",
  },
};

// Types and Interfaces
type HeadingVariants = VariantProps<typeof headingStyles>;

interface Props
  extends HTMLAttributes<
      "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p" | "span" | "div"
    >,
    HeadingVariants {
  as?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p" | "span" | "div";
  size?: "xs" | "sm" | "base" | "lg" | "xl" | "2xl" | "3xl" | "4xl" | "5xl";
  weight?: "light" | "normal" | "medium" | "semibold" | "bold" | "extrabold";
  color?: ColorName;
  align?: "left" | "center" | "right";
  transform?: "normal" | "uppercase" | "lowercase" | "capitalize";
  tracking?: "tighter" | "tight" | "normal" | "wide" | "wider" | "widest";
  class?: string;
}

// Component Logic
const {
  as: Element = "h2",
  size = "xl",
  weight = "bold",
  color = "gray",
  align = "left",
  transform = "normal",
  tracking = "normal",
  class: className = "",
  ...rest
} = Astro.props as Props;

// Styles
const headingStyles = tv({
  base: "leading-tight",
  variants: {
    size: {
      xs: "text-xs",
      sm: "text-sm",
      base: "text-base",
      lg: "text-lg",
      xl: "text-xl",
      "2xl": "text-2xl",
      "3xl": "text-3xl",
      "4xl": "text-4xl",
      "5xl": "text-5xl",
    },
    weight: {
      light: "font-light",
      normal: "font-normal",
      medium: "font-medium",
      semibold: "font-semibold",
      bold: "font-bold",
      extrabold: "font-extrabold",
    },
    align: {
      left: "text-left",
      center: "text-center",
      right: "text-right",
    },
    transform: {
      normal: "normal-case",
      uppercase: "uppercase",
      lowercase: "lowercase",
      capitalize: "capitalize",
    },
    tracking: {
      tighter: "tracking-tighter",
      tight: "tracking-tight",
      normal: "tracking-normal",
      wide: "tracking-wide",
      wider: "tracking-wider",
      widest: "tracking-widest",
    },
  },
  defaultVariants: {
    size: "xl",
    weight: "bold",
    align: "left",
    transform: "normal",
    tracking: "normal",
  },
});

const colorClass = getOutlinedTextClass(color, { includeHover: false });
---

<Element
  class={twMerge(
    headingStyles({ size, weight, align, transform, tracking }),
    colorClass,
    className,
  )}
  {...rest}
>
  <slot />
</Element>

Heading Levels

Different heading levels using the 'as' prop.

 
<div class="space-y-2">
  <h1
    class="text-xl font-bold text-left normal-case tracking-normal text-gray-500 dark:text-gray-400">
    Heading 1
  </h1>
  <h2
    class="text-xl font-bold text-left normal-case tracking-normal text-gray-500 dark:text-gray-400">
    Heading 2
  </h2>
  <h3
    class="text-xl font-bold text-left normal-case tracking-normal text-gray-500 dark:text-gray-400">
    Heading 3
  </h3>
  <h4
    class="text-xl font-bold text-left normal-case tracking-normal text-gray-500 dark:text-gray-400">
    Heading 4
  </h4>
  <h5
    class="text-xl font-bold text-left normal-case tracking-normal text-gray-500 dark:text-gray-400">
    Heading 5
  </h5>
  <h6
    class="text-xl font-bold text-left normal-case tracking-normal text-gray-500 dark:text-gray-400">
    Heading 6
  </h6>
</div>
---
import { Heading } from '@/components/ui/heading';

---

<Heading as="h1">Heading 1</Heading>
<Heading as="h2">Heading 2</Heading>
<Heading as="h3">Heading 3</Heading>
<Heading as="h4">Heading 4</Heading>
<Heading as="h5">Heading 5</Heading>
<Heading as="h6">Heading 6</Heading>
---
// Imports
import { type HTMLAttributes } from "astro/types";
import { tv, type VariantProps } from "@utils/custom-tv";
import { twMerge } from "tailwind-merge";
import {
  type ColorName,
  colorPalette,
  getHardTextClass,
  getOutlinedTextClass,
} from "@utils/colorUtils";

// PropTypes for documentation
export const propTypes = {
  as: {
    type: ["h1", "h2", "h3", "h4", "h5", "h6", "p", "span", "div"],
    description: "The HTML element to render the heading as",
    default: "h2",
  },
  size: {
    type: ["xs", "sm", "base", "lg", "xl", "2xl", "3xl", "4xl", "5xl"],
    description: "The size of the heading",
    default: "xl",
  },
  weight: {
    type: ["light", "normal", "medium", "semibold", "bold", "extrabold"],
    description: "The font weight of the heading",
    default: "bold",
  },
  color: {
    type: Object.keys(colorPalette),
    description: "The color of the heading",
    default: "gray",
  },
  align: {
    type: ["left", "center", "right"],
    description: "The text alignment of the heading",
    default: "left",
  },
  transform: {
    type: ["normal", "uppercase", "lowercase", "capitalize"],
    description: "The text transform of the heading",
    default: "normal",
  },
  tracking: {
    type: ["tighter", "tight", "normal", "wide", "wider", "widest"],
    description: "The letter spacing of the heading",
    default: "normal",
  },
  class: {
    type: "string",
    description: "Additional CSS classes to apply to the heading",
  },
};

// Types and Interfaces
type HeadingVariants = VariantProps<typeof headingStyles>;

interface Props
  extends HTMLAttributes<
      "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p" | "span" | "div"
    >,
    HeadingVariants {
  as?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p" | "span" | "div";
  size?: "xs" | "sm" | "base" | "lg" | "xl" | "2xl" | "3xl" | "4xl" | "5xl";
  weight?: "light" | "normal" | "medium" | "semibold" | "bold" | "extrabold";
  color?: ColorName;
  align?: "left" | "center" | "right";
  transform?: "normal" | "uppercase" | "lowercase" | "capitalize";
  tracking?: "tighter" | "tight" | "normal" | "wide" | "wider" | "widest";
  class?: string;
}

// Component Logic
const {
  as: Element = "h2",
  size = "xl",
  weight = "bold",
  color = "gray",
  align = "left",
  transform = "normal",
  tracking = "normal",
  class: className = "",
  ...rest
} = Astro.props as Props;

// Styles
const headingStyles = tv({
  base: "leading-tight",
  variants: {
    size: {
      xs: "text-xs",
      sm: "text-sm",
      base: "text-base",
      lg: "text-lg",
      xl: "text-xl",
      "2xl": "text-2xl",
      "3xl": "text-3xl",
      "4xl": "text-4xl",
      "5xl": "text-5xl",
    },
    weight: {
      light: "font-light",
      normal: "font-normal",
      medium: "font-medium",
      semibold: "font-semibold",
      bold: "font-bold",
      extrabold: "font-extrabold",
    },
    align: {
      left: "text-left",
      center: "text-center",
      right: "text-right",
    },
    transform: {
      normal: "normal-case",
      uppercase: "uppercase",
      lowercase: "lowercase",
      capitalize: "capitalize",
    },
    tracking: {
      tighter: "tracking-tighter",
      tight: "tracking-tight",
      normal: "tracking-normal",
      wide: "tracking-wide",
      wider: "tracking-wider",
      widest: "tracking-widest",
    },
  },
  defaultVariants: {
    size: "xl",
    weight: "bold",
    align: "left",
    transform: "normal",
    tracking: "normal",
  },
});

const colorClass = getOutlinedTextClass(color, { includeHover: false });
---

<Element
  class={twMerge(
    headingStyles({ size, weight, align, transform, tracking }),
    colorClass,
    className,
  )}
  {...rest}
>
  <slot />
</Element>

Heading Sizes

Headings in different sizes: xs, sm, base, lg, xl, 2xl, 3xl, 4xl, and 5xl.

 
<div class="space-y-2">
  <h2
    class="text-xs font-bold text-left normal-case tracking-normal text-gray-500 dark:text-gray-400">
    Extra Small
  </h2>
  <h2
    class="text-sm font-bold text-left normal-case tracking-normal text-gray-500 dark:text-gray-400">
    Small
  </h2>
  <h2
    class="text-base font-bold text-left normal-case tracking-normal text-gray-500 dark:text-gray-400">
    Base
  </h2>
  <h2
    class="text-lg font-bold text-left normal-case tracking-normal text-gray-500 dark:text-gray-400">
    Large
  </h2>
  <h2
    class="text-xl font-bold text-left normal-case tracking-normal text-gray-500 dark:text-gray-400">
    Extra Large
  </h2>
  <h2
    class="text-2xl font-bold text-left normal-case tracking-normal text-gray-500 dark:text-gray-400">
    2X Large
  </h2>
  <h2
    class="text-3xl font-bold text-left normal-case tracking-normal text-gray-500 dark:text-gray-400">
    3X Large
  </h2>
  <h2
    class="text-4xl font-bold text-left normal-case tracking-normal text-gray-500 dark:text-gray-400">
    4X Large
  </h2>
  <h2
    class="text-5xl font-bold text-left normal-case tracking-normal text-gray-500 dark:text-gray-400">
    5X Large
  </h2>
</div>
---
import { Heading } from '@/components/ui/heading';

---

<Heading size="xs">Extra Small</Heading>
<Heading size="sm">Small</Heading>
<Heading size="base">Base</Heading>
<Heading size="lg">Large</Heading>
<Heading size="xl">Extra Large</Heading>
<Heading size="2xl">2X Large</Heading>
<Heading size="3xl">3X Large</Heading>
<Heading size="4xl">4X Large</Heading>
<Heading size="5xl">5X Large</Heading>
---
// Imports
import { type HTMLAttributes } from "astro/types";
import { tv, type VariantProps } from "@utils/custom-tv";
import { twMerge } from "tailwind-merge";
import {
  type ColorName,
  colorPalette,
  getHardTextClass,
  getOutlinedTextClass,
} from "@utils/colorUtils";

// PropTypes for documentation
export const propTypes = {
  as: {
    type: ["h1", "h2", "h3", "h4", "h5", "h6", "p", "span", "div"],
    description: "The HTML element to render the heading as",
    default: "h2",
  },
  size: {
    type: ["xs", "sm", "base", "lg", "xl", "2xl", "3xl", "4xl", "5xl"],
    description: "The size of the heading",
    default: "xl",
  },
  weight: {
    type: ["light", "normal", "medium", "semibold", "bold", "extrabold"],
    description: "The font weight of the heading",
    default: "bold",
  },
  color: {
    type: Object.keys(colorPalette),
    description: "The color of the heading",
    default: "gray",
  },
  align: {
    type: ["left", "center", "right"],
    description: "The text alignment of the heading",
    default: "left",
  },
  transform: {
    type: ["normal", "uppercase", "lowercase", "capitalize"],
    description: "The text transform of the heading",
    default: "normal",
  },
  tracking: {
    type: ["tighter", "tight", "normal", "wide", "wider", "widest"],
    description: "The letter spacing of the heading",
    default: "normal",
  },
  class: {
    type: "string",
    description: "Additional CSS classes to apply to the heading",
  },
};

// Types and Interfaces
type HeadingVariants = VariantProps<typeof headingStyles>;

interface Props
  extends HTMLAttributes<
      "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p" | "span" | "div"
    >,
    HeadingVariants {
  as?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p" | "span" | "div";
  size?: "xs" | "sm" | "base" | "lg" | "xl" | "2xl" | "3xl" | "4xl" | "5xl";
  weight?: "light" | "normal" | "medium" | "semibold" | "bold" | "extrabold";
  color?: ColorName;
  align?: "left" | "center" | "right";
  transform?: "normal" | "uppercase" | "lowercase" | "capitalize";
  tracking?: "tighter" | "tight" | "normal" | "wide" | "wider" | "widest";
  class?: string;
}

// Component Logic
const {
  as: Element = "h2",
  size = "xl",
  weight = "bold",
  color = "gray",
  align = "left",
  transform = "normal",
  tracking = "normal",
  class: className = "",
  ...rest
} = Astro.props as Props;

// Styles
const headingStyles = tv({
  base: "leading-tight",
  variants: {
    size: {
      xs: "text-xs",
      sm: "text-sm",
      base: "text-base",
      lg: "text-lg",
      xl: "text-xl",
      "2xl": "text-2xl",
      "3xl": "text-3xl",
      "4xl": "text-4xl",
      "5xl": "text-5xl",
    },
    weight: {
      light: "font-light",
      normal: "font-normal",
      medium: "font-medium",
      semibold: "font-semibold",
      bold: "font-bold",
      extrabold: "font-extrabold",
    },
    align: {
      left: "text-left",
      center: "text-center",
      right: "text-right",
    },
    transform: {
      normal: "normal-case",
      uppercase: "uppercase",
      lowercase: "lowercase",
      capitalize: "capitalize",
    },
    tracking: {
      tighter: "tracking-tighter",
      tight: "tracking-tight",
      normal: "tracking-normal",
      wide: "tracking-wide",
      wider: "tracking-wider",
      widest: "tracking-widest",
    },
  },
  defaultVariants: {
    size: "xl",
    weight: "bold",
    align: "left",
    transform: "normal",
    tracking: "normal",
  },
});

const colorClass = getOutlinedTextClass(color, { includeHover: false });
---

<Element
  class={twMerge(
    headingStyles({ size, weight, align, transform, tracking }),
    colorClass,
    className,
  )}
  {...rest}
>
  <slot />
</Element>

Heading Weights

Headings with different font weights.

 
<div class="space-y-2">
  <h2
    class="text-xl font-light text-left normal-case tracking-normal text-gray-500 dark:text-gray-400">
    Light
  </h2>
  <h2
    class="text-xl font-normal text-left normal-case tracking-normal text-gray-500 dark:text-gray-400">
    Normal
  </h2>
  <h2
    class="text-xl font-medium text-left normal-case tracking-normal text-gray-500 dark:text-gray-400">
    Medium
  </h2>
  <h2
    class="text-xl font-semibold text-left normal-case tracking-normal text-gray-500 dark:text-gray-400">
    Semibold
  </h2>
  <h2
    class="text-xl font-bold text-left normal-case tracking-normal text-gray-500 dark:text-gray-400">
    Bold
  </h2>
  <h2
    class="text-xl font-extrabold text-left normal-case tracking-normal text-gray-500 dark:text-gray-400">
    Extrabold
  </h2>
</div>
---
import { Heading } from '@/components/ui/heading';

---

<Heading weight="light">Light</Heading>
<Heading weight="normal">Normal</Heading>
<Heading weight="medium">Medium</Heading>
<Heading weight="semibold">Semibold</Heading>
<Heading weight="bold">Bold</Heading>
<Heading weight="extrabold">Extrabold</Heading>
---
// Imports
import { type HTMLAttributes } from "astro/types";
import { tv, type VariantProps } from "@utils/custom-tv";
import { twMerge } from "tailwind-merge";
import {
  type ColorName,
  colorPalette,
  getHardTextClass,
  getOutlinedTextClass,
} from "@utils/colorUtils";

// PropTypes for documentation
export const propTypes = {
  as: {
    type: ["h1", "h2", "h3", "h4", "h5", "h6", "p", "span", "div"],
    description: "The HTML element to render the heading as",
    default: "h2",
  },
  size: {
    type: ["xs", "sm", "base", "lg", "xl", "2xl", "3xl", "4xl", "5xl"],
    description: "The size of the heading",
    default: "xl",
  },
  weight: {
    type: ["light", "normal", "medium", "semibold", "bold", "extrabold"],
    description: "The font weight of the heading",
    default: "bold",
  },
  color: {
    type: Object.keys(colorPalette),
    description: "The color of the heading",
    default: "gray",
  },
  align: {
    type: ["left", "center", "right"],
    description: "The text alignment of the heading",
    default: "left",
  },
  transform: {
    type: ["normal", "uppercase", "lowercase", "capitalize"],
    description: "The text transform of the heading",
    default: "normal",
  },
  tracking: {
    type: ["tighter", "tight", "normal", "wide", "wider", "widest"],
    description: "The letter spacing of the heading",
    default: "normal",
  },
  class: {
    type: "string",
    description: "Additional CSS classes to apply to the heading",
  },
};

// Types and Interfaces
type HeadingVariants = VariantProps<typeof headingStyles>;

interface Props
  extends HTMLAttributes<
      "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p" | "span" | "div"
    >,
    HeadingVariants {
  as?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p" | "span" | "div";
  size?: "xs" | "sm" | "base" | "lg" | "xl" | "2xl" | "3xl" | "4xl" | "5xl";
  weight?: "light" | "normal" | "medium" | "semibold" | "bold" | "extrabold";
  color?: ColorName;
  align?: "left" | "center" | "right";
  transform?: "normal" | "uppercase" | "lowercase" | "capitalize";
  tracking?: "tighter" | "tight" | "normal" | "wide" | "wider" | "widest";
  class?: string;
}

// Component Logic
const {
  as: Element = "h2",
  size = "xl",
  weight = "bold",
  color = "gray",
  align = "left",
  transform = "normal",
  tracking = "normal",
  class: className = "",
  ...rest
} = Astro.props as Props;

// Styles
const headingStyles = tv({
  base: "leading-tight",
  variants: {
    size: {
      xs: "text-xs",
      sm: "text-sm",
      base: "text-base",
      lg: "text-lg",
      xl: "text-xl",
      "2xl": "text-2xl",
      "3xl": "text-3xl",
      "4xl": "text-4xl",
      "5xl": "text-5xl",
    },
    weight: {
      light: "font-light",
      normal: "font-normal",
      medium: "font-medium",
      semibold: "font-semibold",
      bold: "font-bold",
      extrabold: "font-extrabold",
    },
    align: {
      left: "text-left",
      center: "text-center",
      right: "text-right",
    },
    transform: {
      normal: "normal-case",
      uppercase: "uppercase",
      lowercase: "lowercase",
      capitalize: "capitalize",
    },
    tracking: {
      tighter: "tracking-tighter",
      tight: "tracking-tight",
      normal: "tracking-normal",
      wide: "tracking-wide",
      wider: "tracking-wider",
      widest: "tracking-widest",
    },
  },
  defaultVariants: {
    size: "xl",
    weight: "bold",
    align: "left",
    transform: "normal",
    tracking: "normal",
  },
});

const colorClass = getOutlinedTextClass(color, { includeHover: false });
---

<Element
  class={twMerge(
    headingStyles({ size, weight, align, transform, tracking }),
    colorClass,
    className,
  )}
  {...rest}
>
  <slot />
</Element>

Heading Colors

Headings with different color options.

 
<div class="space-y-2">
  <h2
    class="text-xl font-bold text-left normal-case tracking-normal text-blue-500 dark:text-blue-400">
    Blue Heading
  </h2>
  <h2
    class="text-xl font-bold text-left normal-case tracking-normal text-red-500 dark:text-red-400">
    Red Heading
  </h2>
  <h2
    class="text-xl font-bold text-left normal-case tracking-normal text-green-500 dark:text-green-400">
    Green Heading
  </h2>
  <h2
    class="text-xl font-bold text-left normal-case tracking-normal text-yellow-500 dark:text-yellow-400">
    Yellow Heading
  </h2>
  <h2
    class="text-xl font-bold text-left normal-case tracking-normal text-purple-500 dark:text-purple-400">
    Purple Heading
  </h2>
  <h2
    class="text-xl font-bold text-left normal-case tracking-normal text-pink-500 dark:text-pink-400">
    Pink Heading
  </h2>
</div>
---
import { Heading } from '@/components/ui/heading';

---

<Heading color="blue">Blue Heading</Heading>
<Heading color="red">Red Heading</Heading>
<Heading color="green">Green Heading</Heading>
<Heading color="yellow">Yellow Heading</Heading>
<Heading color="purple">Purple Heading</Heading>
<Heading color="pink">Pink Heading</Heading>
---
// Imports
import { type HTMLAttributes } from "astro/types";
import { tv, type VariantProps } from "@utils/custom-tv";
import { twMerge } from "tailwind-merge";
import {
  type ColorName,
  colorPalette,
  getHardTextClass,
  getOutlinedTextClass,
} from "@utils/colorUtils";

// PropTypes for documentation
export const propTypes = {
  as: {
    type: ["h1", "h2", "h3", "h4", "h5", "h6", "p", "span", "div"],
    description: "The HTML element to render the heading as",
    default: "h2",
  },
  size: {
    type: ["xs", "sm", "base", "lg", "xl", "2xl", "3xl", "4xl", "5xl"],
    description: "The size of the heading",
    default: "xl",
  },
  weight: {
    type: ["light", "normal", "medium", "semibold", "bold", "extrabold"],
    description: "The font weight of the heading",
    default: "bold",
  },
  color: {
    type: Object.keys(colorPalette),
    description: "The color of the heading",
    default: "gray",
  },
  align: {
    type: ["left", "center", "right"],
    description: "The text alignment of the heading",
    default: "left",
  },
  transform: {
    type: ["normal", "uppercase", "lowercase", "capitalize"],
    description: "The text transform of the heading",
    default: "normal",
  },
  tracking: {
    type: ["tighter", "tight", "normal", "wide", "wider", "widest"],
    description: "The letter spacing of the heading",
    default: "normal",
  },
  class: {
    type: "string",
    description: "Additional CSS classes to apply to the heading",
  },
};

// Types and Interfaces
type HeadingVariants = VariantProps<typeof headingStyles>;

interface Props
  extends HTMLAttributes<
      "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p" | "span" | "div"
    >,
    HeadingVariants {
  as?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p" | "span" | "div";
  size?: "xs" | "sm" | "base" | "lg" | "xl" | "2xl" | "3xl" | "4xl" | "5xl";
  weight?: "light" | "normal" | "medium" | "semibold" | "bold" | "extrabold";
  color?: ColorName;
  align?: "left" | "center" | "right";
  transform?: "normal" | "uppercase" | "lowercase" | "capitalize";
  tracking?: "tighter" | "tight" | "normal" | "wide" | "wider" | "widest";
  class?: string;
}

// Component Logic
const {
  as: Element = "h2",
  size = "xl",
  weight = "bold",
  color = "gray",
  align = "left",
  transform = "normal",
  tracking = "normal",
  class: className = "",
  ...rest
} = Astro.props as Props;

// Styles
const headingStyles = tv({
  base: "leading-tight",
  variants: {
    size: {
      xs: "text-xs",
      sm: "text-sm",
      base: "text-base",
      lg: "text-lg",
      xl: "text-xl",
      "2xl": "text-2xl",
      "3xl": "text-3xl",
      "4xl": "text-4xl",
      "5xl": "text-5xl",
    },
    weight: {
      light: "font-light",
      normal: "font-normal",
      medium: "font-medium",
      semibold: "font-semibold",
      bold: "font-bold",
      extrabold: "font-extrabold",
    },
    align: {
      left: "text-left",
      center: "text-center",
      right: "text-right",
    },
    transform: {
      normal: "normal-case",
      uppercase: "uppercase",
      lowercase: "lowercase",
      capitalize: "capitalize",
    },
    tracking: {
      tighter: "tracking-tighter",
      tight: "tracking-tight",
      normal: "tracking-normal",
      wide: "tracking-wide",
      wider: "tracking-wider",
      widest: "tracking-widest",
    },
  },
  defaultVariants: {
    size: "xl",
    weight: "bold",
    align: "left",
    transform: "normal",
    tracking: "normal",
  },
});

const colorClass = getOutlinedTextClass(color, { includeHover: false });
---

<Element
  class={twMerge(
    headingStyles({ size, weight, align, transform, tracking }),
    colorClass,
    className,
  )}
  {...rest}
>
  <slot />
</Element>

Heading Alignment

Headings with different text alignments.

 
<div class="space-y-2">
  <h2
    class="text-xl font-bold text-left normal-case tracking-normal text-gray-500 dark:text-gray-400">
    Left Aligned
  </h2>
  <h2
    class="text-xl font-bold text-center normal-case tracking-normal text-gray-500 dark:text-gray-400">
    Center Aligned
  </h2>
  <h2
    class="text-xl font-bold text-right normal-case tracking-normal text-gray-500 dark:text-gray-400">
    Right Aligned
  </h2>
</div>
---
import { Heading } from '@/components/ui/heading';

---

<Heading align="left">Left Aligned</Heading>
<Heading align="center">Center Aligned</Heading>
<Heading align="right">Right Aligned</Heading>
---
// Imports
import { type HTMLAttributes } from "astro/types";
import { tv, type VariantProps } from "@utils/custom-tv";
import { twMerge } from "tailwind-merge";
import {
  type ColorName,
  colorPalette,
  getHardTextClass,
  getOutlinedTextClass,
} from "@utils/colorUtils";

// PropTypes for documentation
export const propTypes = {
  as: {
    type: ["h1", "h2", "h3", "h4", "h5", "h6", "p", "span", "div"],
    description: "The HTML element to render the heading as",
    default: "h2",
  },
  size: {
    type: ["xs", "sm", "base", "lg", "xl", "2xl", "3xl", "4xl", "5xl"],
    description: "The size of the heading",
    default: "xl",
  },
  weight: {
    type: ["light", "normal", "medium", "semibold", "bold", "extrabold"],
    description: "The font weight of the heading",
    default: "bold",
  },
  color: {
    type: Object.keys(colorPalette),
    description: "The color of the heading",
    default: "gray",
  },
  align: {
    type: ["left", "center", "right"],
    description: "The text alignment of the heading",
    default: "left",
  },
  transform: {
    type: ["normal", "uppercase", "lowercase", "capitalize"],
    description: "The text transform of the heading",
    default: "normal",
  },
  tracking: {
    type: ["tighter", "tight", "normal", "wide", "wider", "widest"],
    description: "The letter spacing of the heading",
    default: "normal",
  },
  class: {
    type: "string",
    description: "Additional CSS classes to apply to the heading",
  },
};

// Types and Interfaces
type HeadingVariants = VariantProps<typeof headingStyles>;

interface Props
  extends HTMLAttributes<
      "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p" | "span" | "div"
    >,
    HeadingVariants {
  as?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p" | "span" | "div";
  size?: "xs" | "sm" | "base" | "lg" | "xl" | "2xl" | "3xl" | "4xl" | "5xl";
  weight?: "light" | "normal" | "medium" | "semibold" | "bold" | "extrabold";
  color?: ColorName;
  align?: "left" | "center" | "right";
  transform?: "normal" | "uppercase" | "lowercase" | "capitalize";
  tracking?: "tighter" | "tight" | "normal" | "wide" | "wider" | "widest";
  class?: string;
}

// Component Logic
const {
  as: Element = "h2",
  size = "xl",
  weight = "bold",
  color = "gray",
  align = "left",
  transform = "normal",
  tracking = "normal",
  class: className = "",
  ...rest
} = Astro.props as Props;

// Styles
const headingStyles = tv({
  base: "leading-tight",
  variants: {
    size: {
      xs: "text-xs",
      sm: "text-sm",
      base: "text-base",
      lg: "text-lg",
      xl: "text-xl",
      "2xl": "text-2xl",
      "3xl": "text-3xl",
      "4xl": "text-4xl",
      "5xl": "text-5xl",
    },
    weight: {
      light: "font-light",
      normal: "font-normal",
      medium: "font-medium",
      semibold: "font-semibold",
      bold: "font-bold",
      extrabold: "font-extrabold",
    },
    align: {
      left: "text-left",
      center: "text-center",
      right: "text-right",
    },
    transform: {
      normal: "normal-case",
      uppercase: "uppercase",
      lowercase: "lowercase",
      capitalize: "capitalize",
    },
    tracking: {
      tighter: "tracking-tighter",
      tight: "tracking-tight",
      normal: "tracking-normal",
      wide: "tracking-wide",
      wider: "tracking-wider",
      widest: "tracking-widest",
    },
  },
  defaultVariants: {
    size: "xl",
    weight: "bold",
    align: "left",
    transform: "normal",
    tracking: "normal",
  },
});

const colorClass = getOutlinedTextClass(color, { includeHover: false });
---

<Element
  class={twMerge(
    headingStyles({ size, weight, align, transform, tracking }),
    colorClass,
    className,
  )}
  {...rest}
>
  <slot />
</Element>

Heading Text Transforms

Headings with different text transformations.

 
<div class="space-y-2">
  <h2
    class="text-xl font-bold text-left normal-case tracking-normal text-gray-500 dark:text-gray-400">
    Normal Text
  </h2>
  <h2
    class="text-xl font-bold text-left uppercase tracking-normal text-gray-500 dark:text-gray-400">
    Uppercase Text
  </h2>
  <h2
    class="text-xl font-bold text-left lowercase tracking-normal text-gray-500 dark:text-gray-400">
    Lowercase Text
  </h2>
  <h2
    class="text-xl font-bold text-left capitalize tracking-normal text-gray-500 dark:text-gray-400">
    Capitalized Text
  </h2>
</div>
---
import { Heading } from '@/components/ui/heading';

---

<Heading transform="normal">Normal Text</Heading>
<Heading transform="uppercase">Uppercase Text</Heading>
<Heading transform="lowercase">Lowercase Text</Heading>
<Heading transform="capitalize">Capitalized Text</Heading>
---
// Imports
import { type HTMLAttributes } from "astro/types";
import { tv, type VariantProps } from "@utils/custom-tv";
import { twMerge } from "tailwind-merge";
import {
  type ColorName,
  colorPalette,
  getHardTextClass,
  getOutlinedTextClass,
} from "@utils/colorUtils";

// PropTypes for documentation
export const propTypes = {
  as: {
    type: ["h1", "h2", "h3", "h4", "h5", "h6", "p", "span", "div"],
    description: "The HTML element to render the heading as",
    default: "h2",
  },
  size: {
    type: ["xs", "sm", "base", "lg", "xl", "2xl", "3xl", "4xl", "5xl"],
    description: "The size of the heading",
    default: "xl",
  },
  weight: {
    type: ["light", "normal", "medium", "semibold", "bold", "extrabold"],
    description: "The font weight of the heading",
    default: "bold",
  },
  color: {
    type: Object.keys(colorPalette),
    description: "The color of the heading",
    default: "gray",
  },
  align: {
    type: ["left", "center", "right"],
    description: "The text alignment of the heading",
    default: "left",
  },
  transform: {
    type: ["normal", "uppercase", "lowercase", "capitalize"],
    description: "The text transform of the heading",
    default: "normal",
  },
  tracking: {
    type: ["tighter", "tight", "normal", "wide", "wider", "widest"],
    description: "The letter spacing of the heading",
    default: "normal",
  },
  class: {
    type: "string",
    description: "Additional CSS classes to apply to the heading",
  },
};

// Types and Interfaces
type HeadingVariants = VariantProps<typeof headingStyles>;

interface Props
  extends HTMLAttributes<
      "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p" | "span" | "div"
    >,
    HeadingVariants {
  as?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p" | "span" | "div";
  size?: "xs" | "sm" | "base" | "lg" | "xl" | "2xl" | "3xl" | "4xl" | "5xl";
  weight?: "light" | "normal" | "medium" | "semibold" | "bold" | "extrabold";
  color?: ColorName;
  align?: "left" | "center" | "right";
  transform?: "normal" | "uppercase" | "lowercase" | "capitalize";
  tracking?: "tighter" | "tight" | "normal" | "wide" | "wider" | "widest";
  class?: string;
}

// Component Logic
const {
  as: Element = "h2",
  size = "xl",
  weight = "bold",
  color = "gray",
  align = "left",
  transform = "normal",
  tracking = "normal",
  class: className = "",
  ...rest
} = Astro.props as Props;

// Styles
const headingStyles = tv({
  base: "leading-tight",
  variants: {
    size: {
      xs: "text-xs",
      sm: "text-sm",
      base: "text-base",
      lg: "text-lg",
      xl: "text-xl",
      "2xl": "text-2xl",
      "3xl": "text-3xl",
      "4xl": "text-4xl",
      "5xl": "text-5xl",
    },
    weight: {
      light: "font-light",
      normal: "font-normal",
      medium: "font-medium",
      semibold: "font-semibold",
      bold: "font-bold",
      extrabold: "font-extrabold",
    },
    align: {
      left: "text-left",
      center: "text-center",
      right: "text-right",
    },
    transform: {
      normal: "normal-case",
      uppercase: "uppercase",
      lowercase: "lowercase",
      capitalize: "capitalize",
    },
    tracking: {
      tighter: "tracking-tighter",
      tight: "tracking-tight",
      normal: "tracking-normal",
      wide: "tracking-wide",
      wider: "tracking-wider",
      widest: "tracking-widest",
    },
  },
  defaultVariants: {
    size: "xl",
    weight: "bold",
    align: "left",
    transform: "normal",
    tracking: "normal",
  },
});

const colorClass = getOutlinedTextClass(color, { includeHover: false });
---

<Element
  class={twMerge(
    headingStyles({ size, weight, align, transform, tracking }),
    colorClass,
    className,
  )}
  {...rest}
>
  <slot />
</Element>

Heading Letter Spacing

Headings with different letter spacing options.

 
<div class="space-y-2">
  <h2
    class="text-xl font-bold text-left normal-case tracking-tighter text-gray-500 dark:text-gray-400">
    Tighter Spacing
  </h2>
  <h2
    class="text-xl font-bold text-left normal-case tracking-tight text-gray-500 dark:text-gray-400">
    Tight Spacing
  </h2>
  <h2
    class="text-xl font-bold text-left normal-case tracking-normal text-gray-500 dark:text-gray-400">
    Normal Spacing
  </h2>
  <h2
    class="text-xl font-bold text-left normal-case tracking-wide text-gray-500 dark:text-gray-400">
    Wide Spacing
  </h2>
  <h2
    class="text-xl font-bold text-left normal-case tracking-wider text-gray-500 dark:text-gray-400">
    Wider Spacing
  </h2>
  <h2
    class="text-xl font-bold text-left normal-case tracking-widest text-gray-500 dark:text-gray-400">
    Widest Spacing
  </h2>
</div>
---
import { Heading } from '@/components/ui/heading';

---

<Heading tracking="tighter">Tighter Spacing</Heading>
<Heading tracking="tight">Tight Spacing</Heading>
<Heading tracking="normal">Normal Spacing</Heading>
<Heading tracking="wide">Wide Spacing</Heading>
<Heading tracking="wider">Wider Spacing</Heading>
<Heading tracking="widest">Widest Spacing</Heading>
---
// Imports
import { type HTMLAttributes } from "astro/types";
import { tv, type VariantProps } from "@utils/custom-tv";
import { twMerge } from "tailwind-merge";
import {
  type ColorName,
  colorPalette,
  getHardTextClass,
  getOutlinedTextClass,
} from "@utils/colorUtils";

// PropTypes for documentation
export const propTypes = {
  as: {
    type: ["h1", "h2", "h3", "h4", "h5", "h6", "p", "span", "div"],
    description: "The HTML element to render the heading as",
    default: "h2",
  },
  size: {
    type: ["xs", "sm", "base", "lg", "xl", "2xl", "3xl", "4xl", "5xl"],
    description: "The size of the heading",
    default: "xl",
  },
  weight: {
    type: ["light", "normal", "medium", "semibold", "bold", "extrabold"],
    description: "The font weight of the heading",
    default: "bold",
  },
  color: {
    type: Object.keys(colorPalette),
    description: "The color of the heading",
    default: "gray",
  },
  align: {
    type: ["left", "center", "right"],
    description: "The text alignment of the heading",
    default: "left",
  },
  transform: {
    type: ["normal", "uppercase", "lowercase", "capitalize"],
    description: "The text transform of the heading",
    default: "normal",
  },
  tracking: {
    type: ["tighter", "tight", "normal", "wide", "wider", "widest"],
    description: "The letter spacing of the heading",
    default: "normal",
  },
  class: {
    type: "string",
    description: "Additional CSS classes to apply to the heading",
  },
};

// Types and Interfaces
type HeadingVariants = VariantProps<typeof headingStyles>;

interface Props
  extends HTMLAttributes<
      "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p" | "span" | "div"
    >,
    HeadingVariants {
  as?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p" | "span" | "div";
  size?: "xs" | "sm" | "base" | "lg" | "xl" | "2xl" | "3xl" | "4xl" | "5xl";
  weight?: "light" | "normal" | "medium" | "semibold" | "bold" | "extrabold";
  color?: ColorName;
  align?: "left" | "center" | "right";
  transform?: "normal" | "uppercase" | "lowercase" | "capitalize";
  tracking?: "tighter" | "tight" | "normal" | "wide" | "wider" | "widest";
  class?: string;
}

// Component Logic
const {
  as: Element = "h2",
  size = "xl",
  weight = "bold",
  color = "gray",
  align = "left",
  transform = "normal",
  tracking = "normal",
  class: className = "",
  ...rest
} = Astro.props as Props;

// Styles
const headingStyles = tv({
  base: "leading-tight",
  variants: {
    size: {
      xs: "text-xs",
      sm: "text-sm",
      base: "text-base",
      lg: "text-lg",
      xl: "text-xl",
      "2xl": "text-2xl",
      "3xl": "text-3xl",
      "4xl": "text-4xl",
      "5xl": "text-5xl",
    },
    weight: {
      light: "font-light",
      normal: "font-normal",
      medium: "font-medium",
      semibold: "font-semibold",
      bold: "font-bold",
      extrabold: "font-extrabold",
    },
    align: {
      left: "text-left",
      center: "text-center",
      right: "text-right",
    },
    transform: {
      normal: "normal-case",
      uppercase: "uppercase",
      lowercase: "lowercase",
      capitalize: "capitalize",
    },
    tracking: {
      tighter: "tracking-tighter",
      tight: "tracking-tight",
      normal: "tracking-normal",
      wide: "tracking-wide",
      wider: "tracking-wider",
      widest: "tracking-widest",
    },
  },
  defaultVariants: {
    size: "xl",
    weight: "bold",
    align: "left",
    transform: "normal",
    tracking: "normal",
  },
});

const colorClass = getOutlinedTextClass(color, { includeHover: false });
---

<Element
  class={twMerge(
    headingStyles({ size, weight, align, transform, tracking }),
    colorClass,
    className,
  )}
  {...rest}
>
  <slot />
</Element>

Custom Styled Heading

A heading with custom CSS classes applied.

 
<h2
  class="text-left normal-case tracking-normal dark:text-gray-400 text-4xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-purple-400 to-pink-600">
  <p>Custom Gradient Heading</p>
</h2>
---
import { Heading } from '@/components/ui/heading';

---

<Heading class="text-4xl font-bold text-gradient bg-clip-text text-transparent bg-gradient-to-r from-purple-400 to-pink-600">
Custom Gradient Heading
</Heading>
---
// Imports
import { type HTMLAttributes } from "astro/types";
import { tv, type VariantProps } from "@utils/custom-tv";
import { twMerge } from "tailwind-merge";
import {
  type ColorName,
  colorPalette,
  getHardTextClass,
  getOutlinedTextClass,
} from "@utils/colorUtils";

// PropTypes for documentation
export const propTypes = {
  as: {
    type: ["h1", "h2", "h3", "h4", "h5", "h6", "p", "span", "div"],
    description: "The HTML element to render the heading as",
    default: "h2",
  },
  size: {
    type: ["xs", "sm", "base", "lg", "xl", "2xl", "3xl", "4xl", "5xl"],
    description: "The size of the heading",
    default: "xl",
  },
  weight: {
    type: ["light", "normal", "medium", "semibold", "bold", "extrabold"],
    description: "The font weight of the heading",
    default: "bold",
  },
  color: {
    type: Object.keys(colorPalette),
    description: "The color of the heading",
    default: "gray",
  },
  align: {
    type: ["left", "center", "right"],
    description: "The text alignment of the heading",
    default: "left",
  },
  transform: {
    type: ["normal", "uppercase", "lowercase", "capitalize"],
    description: "The text transform of the heading",
    default: "normal",
  },
  tracking: {
    type: ["tighter", "tight", "normal", "wide", "wider", "widest"],
    description: "The letter spacing of the heading",
    default: "normal",
  },
  class: {
    type: "string",
    description: "Additional CSS classes to apply to the heading",
  },
};

// Types and Interfaces
type HeadingVariants = VariantProps<typeof headingStyles>;

interface Props
  extends HTMLAttributes<
      "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p" | "span" | "div"
    >,
    HeadingVariants {
  as?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p" | "span" | "div";
  size?: "xs" | "sm" | "base" | "lg" | "xl" | "2xl" | "3xl" | "4xl" | "5xl";
  weight?: "light" | "normal" | "medium" | "semibold" | "bold" | "extrabold";
  color?: ColorName;
  align?: "left" | "center" | "right";
  transform?: "normal" | "uppercase" | "lowercase" | "capitalize";
  tracking?: "tighter" | "tight" | "normal" | "wide" | "wider" | "widest";
  class?: string;
}

// Component Logic
const {
  as: Element = "h2",
  size = "xl",
  weight = "bold",
  color = "gray",
  align = "left",
  transform = "normal",
  tracking = "normal",
  class: className = "",
  ...rest
} = Astro.props as Props;

// Styles
const headingStyles = tv({
  base: "leading-tight",
  variants: {
    size: {
      xs: "text-xs",
      sm: "text-sm",
      base: "text-base",
      lg: "text-lg",
      xl: "text-xl",
      "2xl": "text-2xl",
      "3xl": "text-3xl",
      "4xl": "text-4xl",
      "5xl": "text-5xl",
    },
    weight: {
      light: "font-light",
      normal: "font-normal",
      medium: "font-medium",
      semibold: "font-semibold",
      bold: "font-bold",
      extrabold: "font-extrabold",
    },
    align: {
      left: "text-left",
      center: "text-center",
      right: "text-right",
    },
    transform: {
      normal: "normal-case",
      uppercase: "uppercase",
      lowercase: "lowercase",
      capitalize: "capitalize",
    },
    tracking: {
      tighter: "tracking-tighter",
      tight: "tracking-tight",
      normal: "tracking-normal",
      wide: "tracking-wide",
      wider: "tracking-wider",
      widest: "tracking-widest",
    },
  },
  defaultVariants: {
    size: "xl",
    weight: "bold",
    align: "left",
    transform: "normal",
    tracking: "normal",
  },
});

const colorClass = getOutlinedTextClass(color, { includeHover: false });
---

<Element
  class={twMerge(
    headingStyles({ size, weight, align, transform, tracking }),
    colorClass,
    className,
  )}
  {...rest}
>
  <slot />
</Element>

Component Properties

Property Type Default Description
as h1 | h2 | h3 | h4 | h5 | h6 | p | span | div "h2" The HTML element to render the heading as
size xs | sm | base | lg | xl | 2xl | 3xl | 4xl | 5xl "xl" The size of the heading
weight light | normal | medium | semibold | bold | extrabold "bold" The font weight of the heading
color white | slate | gray | zinc | neutral | stone | red | orange | amber | yellow | lime | green | emerald | teal | cyan | sky | blue | indigo | violet | purple | fuchsia | pink | rose "gray" The color of the heading
align left | center | right "left" The text alignment of the heading
transform normal | uppercase | lowercase | capitalize "normal" The text transform of the heading
tracking tighter | tight | normal | wide | wider | widest "normal" The letter spacing of the heading
class string - Additional CSS classes to apply to the heading

Usage Notes

Semantic HTML

When using the Heading component, consider the semantic structure of your content:

  • Use <Heading as="h1"> for the main title of a page.
  • Use <Heading as="h2"> for section headings.
  • Use <Heading as="h3"> to <Heading as="h6"> for subsection headings.
  • Use <Heading as="p"> or <Heading as="span"> for styled text that isn’t a heading.

Accessibility

  • Ensure that headings are used in a hierarchical order (h1, then h2, then h3, etc.) to maintain a logical structure for screen readers.
  • When using custom colors, make sure there’s sufficient contrast with the background for readability.

Responsive Design

Consider using different sizes for headings on different screen sizes: