Typography

Semantic text primitives: headings, paragraph, lead, muted, blockquote, inline code, and list.

Report a bug

Preview

Switch between light and dark to inspect the embedded Storybook preview.

Installation

pnpm dlx shadcn@latest add https://ui.vllnt.ai/r/typography.json

Storybook

Explore all variants, controls, and accessibility checks in the interactive Storybook playground.

View in Storybook

4 stories available:

Code

import { cva, type VariantProps } from "class-variance-authority"; import { cn } from "../../lib/utils"; /** * Shared typographic scale for the semantic text primitives. Exposed so * consumers can compose the same styles onto bespoke elements. */ export const typographyVariants = cva("", { defaultVariants: { variant: "p", }, variants: { variant: { blockquote: "mt-6 border-l-2 border-border pl-6 italic text-foreground", h1: "scroll-m-20 text-4xl font-extrabold tracking-tight text-balance lg:text-5xl", h2: "scroll-m-20 border-b border-border pb-2 text-3xl font-semibold tracking-tight first:mt-0", h3: "scroll-m-20 text-2xl font-semibold tracking-tight", h4: "scroll-m-20 text-xl font-semibold tracking-tight", inlineCode: "relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-sm font-semibold text-foreground", lead: "text-xl text-muted-foreground", list: "my-6 ml-6 list-disc [&>li]:mt-2", muted: "text-sm text-muted-foreground", p: "leading-7 [&:not(:first-child)]:mt-6", }, }, }); /** Variant key accepted by {@link typographyVariants}. */ export type TypographyVariant = NonNullable< VariantProps<typeof typographyVariants>["variant"] >; /** Props for a heading-level typographic primitive. */ export type HeadingProps = React.HTMLAttributes<HTMLHeadingElement>; /** Props for a paragraph-level typographic primitive. */ export type ParagraphProps = React.HTMLAttributes<HTMLParagraphElement>; /** Props for the blockquote primitive. */ export type BlockquoteProps = React.BlockquoteHTMLAttributes<HTMLQuoteElement>; /** Props for the inline code primitive. */ export type InlineCodeProps = React.HTMLAttributes<HTMLElement>; /** Props for the unordered list primitive. */ export type ListProps = React.HTMLAttributes<HTMLUListElement>; /** * Top-level page heading. * @example * <H1>Page title</H1> */ const H1 = ({ children, className, ref, ...props }: HeadingProps & { ref?: React.Ref<HTMLHeadingElement> }) => ( <h1 className={cn(typographyVariants({ variant: "h1" }), className)} ref={ref} {...props} > {children} </h1> ); H1.displayName = "H1"; /** Section heading. */ const H2 = ({ children, className, ref, ...props }: HeadingProps & { ref?: React.Ref<HTMLHeadingElement> }) => ( <h2 className={cn(typographyVariants({ variant: "h2" }), className)} ref={ref} {...props} > {children} </h2> ); H2.displayName = "H2"; /** Subsection heading. */ const H3 = ({ children, className, ref, ...props }: HeadingProps & { ref?: React.Ref<HTMLHeadingElement> }) => ( <h3 className={cn(typographyVariants({ variant: "h3" }), className)} ref={ref} {...props} > {children} </h3> ); H3.displayName = "H3"; /** Minor heading. */ const H4 = ({ children, className, ref, ...props }: HeadingProps & { ref?: React.Ref<HTMLHeadingElement> }) => ( <h4 className={cn(typographyVariants({ variant: "h4" }), className)} ref={ref} {...props} > {children} </h4> ); H4.displayName = "H4"; /** Body paragraph with sensible vertical rhythm. */ const P = ({ className, ref, ...props }: ParagraphProps & { ref?: React.Ref<HTMLParagraphElement> }) => ( <p className={cn(typographyVariants({ variant: "p" }), className)} ref={ref} {...props} /> ); P.displayName = "P"; /** Emphasised introductory paragraph. */ const Lead = ({ className, ref, ...props }: ParagraphProps & { ref?: React.Ref<HTMLParagraphElement> }) => ( <p className={cn(typographyVariants({ variant: "lead" }), className)} ref={ref} {...props} /> ); Lead.displayName = "Lead"; /** De-emphasised secondary text. */ const Muted = ({ className, ref, ...props }: ParagraphProps & { ref?: React.Ref<HTMLParagraphElement> }) => ( <p className={cn(typographyVariants({ variant: "muted" }), className)} ref={ref} {...props} /> ); Muted.displayName = "Muted"; /** Quoted passage with a leading rule. */ const Blockquote = ({ className, ref, ...props }: BlockquoteProps & { ref?: React.Ref<HTMLQuoteElement> }) => ( <blockquote className={cn(typographyVariants({ variant: "blockquote" }), className)} ref={ref} {...props} /> ); Blockquote.displayName = "Blockquote"; /** Inline monospaced code span. */ const InlineCode = ({ className, ref, ...props }: InlineCodeProps & { ref?: React.Ref<HTMLElement> }) => ( <code className={cn(typographyVariants({ variant: "inlineCode" }), className)} ref={ref} {...props} /> ); InlineCode.displayName = "InlineCode"; /** Bulleted list with indentation and item spacing. */ const List = ({ className, ref, ...props }: ListProps & { ref?: React.Ref<HTMLUListElement> }) => ( <ul className={cn(typographyVariants({ variant: "list" }), className)} ref={ref} {...props} /> ); List.displayName = "List"; export { Blockquote, H1, H2, H3, H4, InlineCode, Lead, List, Muted, P };

Dependencies

  • @vllnt/ui@^0.2.1