Typography

Typography provides Heading and Text primitives aligned to the design token contract (text-h*, text-p-*, text-caption, text-mono).

When to use

  • Render semantic headings and body text with consistent scale.
  • Keep docs/app content aligned with typography tokens across themes.
  • Use convenience shortcuts (Heading.H2, Text.PSm, Text.Caption, etc.) for common content structures.

Components

  • Heading
    • base: Heading with as='h1' ... 'h6'
    • shortcuts: Heading.H1 through Heading.H6
  • Text
    • base: Text with variant='body' | 'body-lg' | 'body-sm' | 'mini' | 'caption' | 'mono'
    • shortcuts: Text.P, Text.PLg, Text.PSm, Text.PMini, Text.Caption, Text.Mono

Behavior notes

  • Heading applies semantic heading tags and heading token classes.
  • Text defaults to paragraph semantics for body-like variants.
  • caption and mono variants render as inline text by default.
  • Override element tags with as when semantic context requires it.

Example

<div className='space-y-2'>
  <Heading.H2>Usage analytics</Heading.H2>
  <Text.PSm className='text-muted-foreground'>
    Last updated 5 minutes ago.
  </Text.PSm>
  <Text.Caption>Experimental</Text.Caption>
</div>

Preview

heading 1

heading 2

heading 3

heading 4

heading 5
heading 6
monospaced

body large

body large

body large medium

body large bold

paragraph regular

paragraph medium

paragraph bold

paragraph small

paragraph small medium

paragraph small bold

paragraph mini

Caption

Usage

import { Heading, Text } from '@branditdev/ui';

export function TypographyDemo() {
  return (
    <div className='flex flex-col gap-8'>
      <Heading.H1>heading 1</Heading.H1>
      {/* <Heading as='h1'>heading 1</Heading> */}
      <Heading.H2>heading 2</Heading.H2>
      {/* <Heading as='h2'>heading 2</Heading> */}
      <Heading.H3>heading 3</Heading.H3>
      {/* <Heading as='h3'>heading 3</Heading> */}
      <Heading.H4>heading 4</Heading.H4>
      {/* <Heading as='h4'>heading 4</Heading> */}
      <Heading.H5>heading 5</Heading.H5>
      {/* <Heading as='h5'>heading 5</Heading> */}
      <Heading.H6>heading 6</Heading.H6>
      {/* <Heading as='h6'>heading 6</Heading> */}

      <Text.Mono>monospaced</Text.Mono>
      {/* <Text variant='mono'>monospaced</Text> */}

      <Text.PLg>body large</Text.PLg>
      {/* <Text variant='body-lg'>body large</Text> */}
      <Text.PLg>body large</Text.PLg>
      <Text.PLg className='font-medium'>body large medium</Text.PLg>
      <Text.PLg className='font-bold'>body large bold</Text.PLg>

      <Text.P>paragraph regular</Text.P>
      {/* <Text variant='body'>paragraph regular</Text> */}
      <Text.P className='font-medium'>paragraph medium</Text.P>
      <Text.P className='font-bold'>paragraph bold</Text.P>

      <Text.PSm>paragraph small</Text.PSm>
      {/* <Text variant='body-sm'>paragraph small</Text> */}
      <Text.PSm className='font-medium'>paragraph small medium</Text.PSm>
      <Text.PSm className='font-bold'>paragraph small bold</Text.PSm>

      <Text.PMini>paragraph mini</Text.PMini>
      {/* <Text variant='mini'>paragraph mini</Text> */}

      <Text.Caption>Caption</Text.Caption>
      {/* <Text variant='caption'>Caption</Text> */}
    </div>
  );
}

API

const Heading: (props) => Element & object

Defined in: components/typography/typography.tsx:141

Compound heading API with base primitive and convenience heading levels.

Includes Heading.H1 through Heading.H6 shortcuts.

Type Declaration

H1()

H1: (props) => Element

Parameters

props

DetailedHTMLProps<HTMLAttributes<HTMLHeadingElement>>

Returns

Element

H2()

H2: (props) => Element

Parameters

props

DetailedHTMLProps<HTMLAttributes<HTMLHeadingElement>>

Returns

Element

H3()

H3: (props) => Element

Parameters

props

DetailedHTMLProps<HTMLAttributes<HTMLHeadingElement>>

Returns

Element

H4()

H4: (props) => Element

Parameters

props

DetailedHTMLProps<HTMLAttributes<HTMLHeadingElement>>

Returns

Element

H5()

H5: (props) => Element

Parameters

props

DetailedHTMLProps<HTMLAttributes<HTMLHeadingElement>>

Returns

Element

H6()

H6: (props) => Element

Parameters

props

DetailedHTMLProps<HTMLAttributes<HTMLHeadingElement>>

Returns

Element

Source

import type { ComponentProps, HTMLAttributes } from 'react';

import { cn } from '@branditdev/ui';

type HeadingTag = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
type TextTag =
  | 'p'
  | 'span'
  | 'pre'
  | 'code'
  | 'ul'
  | 'ol'
  | 'li'
  | 'blockquote';
type TextVariant = 'body' | 'body-lg' | 'body-sm' | 'mini' | 'caption' | 'mono';

const headingClassMap: Record<HeadingTag, string> = {
  h1: 'text-h1',
  h2: 'text-h2',
  h3: 'text-h3',
  h4: 'text-h4',
  h5: 'text-h5',
  h6: 'text-h6',
};

const textClassMap: Record<TextVariant, string> = {
  body: 'text-p-base',
  'body-lg': 'text-p-lg',
  'body-sm': 'text-p-sm',
  mini: 'text-p-mini',
  caption: 'text-caption uppercase',
  mono: 'text-mono font-mono',
};

export type HeadingProps = ComponentProps<'h1'> & {
  as?: HeadingTag;
};

export type TextProps = HTMLAttributes<HTMLElement> & {
  as?: TextTag;
  variant?: TextVariant;
};

function HeadingComponent({
  as = 'h1',
  children,
  className,
  ...props
}: HeadingProps) {
  const Tag = as;

  return (
    <Tag
      className={cn(
        'scroll-m-20 font-heading text-balance',
        headingClassMap[as],
        className
      )}
      {...props}
    >
      {children}
    </Tag>
  );
}

function TextComponent({
  as,
  variant = 'body',
  children,
  className,
  ...props
}: TextProps) {
  const Tag =
    as || (variant === 'caption' || variant === 'mono' ? 'span' : 'p');

  return (
    <Tag
      className={cn('text-balance', textClassMap[variant], className)}
      {...props}
    >
      {children}
    </Tag>
  );
}

const H1 = (props: ComponentProps<'h1'>) => (
  <HeadingComponent as='h1' {...props} />
);
const H2 = (props: ComponentProps<'h2'>) => (
  <HeadingComponent as='h2' {...props} />
);
const H3 = (props: ComponentProps<'h3'>) => (
  <HeadingComponent as='h3' {...props} />
);
const H4 = (props: ComponentProps<'h4'>) => (
  <HeadingComponent as='h4' {...props} />
);
const H5 = (props: ComponentProps<'h5'>) => (
  <HeadingComponent as='h5' {...props} />
);
const H6 = (props: ComponentProps<'h6'>) => (
  <HeadingComponent as='h6' {...props} />
);

const P = (props: ComponentProps<'p'>) => (
  <TextComponent variant='body' {...props} />
);
const PLg = (props: ComponentProps<'p'>) => (
  <TextComponent variant='body-lg' {...props} />
);
const PSm = (props: ComponentProps<'p'>) => (
  <TextComponent variant='body-sm' {...props} />
);
const PMini = (props: ComponentProps<'p'>) => (
  <TextComponent variant='mini' {...props} />
);
const Caption = (props: ComponentProps<'span'>) => (
  <TextComponent as='span' variant='caption' {...props} />
);
const Mono = (props: ComponentProps<'code'>) => (
  <TextComponent as='code' variant='mono' {...props} />
);

export const Heading = Object.assign(HeadingComponent, {
  H1,
  H2,
  H3,
  H4,
  H5,
  H6,
});

export const Text = Object.assign(TextComponent, {
  P,
  PLg,
  PSm,
  PMini,
  Caption,
  Mono,
});