Headings
Versatile heading components with various styles, sizes, and customization options. Supports different HTML tags, sizes, weights, colors, and text transformations. previews<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>
<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>
<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>
<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>
<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>
<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>
<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>
<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>
<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: