Editorial

Heading

Section heading in the display face, Hanken Grotesk through the --font-display variable, at four sizes that step up with the Section container. Renders an h2 by default; the tag can be swapped.

Free, MIT licenseOpen in Browse
import { mergeProps } from "@base-ui/react/merge-props"
import { useRender } from "@base-ui/react/use-render"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "cn"

const headingVariants = cva(
  "font-display font-normal tracking-tight text-balance",
  {
    variants: {
      /**
       * The type size. Each size steps up once the nearest
       * `@container/section`, such as a `Section`, is at least 42rem wide:
       * `sm` from 32px to 40px, `md` (the default) from 32px to 48px, `lg`
       * from 36px to 72px and `xl` from 64px to 96px. Outside such a
       * container it keeps the smaller size.
       */
      size: {
        sm: "text-[2rem]/10 @2xl/section:text-[2.5rem]/10",
        md: "text-[2rem]/10 @2xl/section:text-5xl/14",
        lg: "text-4xl/10 @2xl/section:text-7xl/20",
        xl: "text-[4rem]/16 @2xl/section:text-8xl/24",
      },
    },
    defaultVariants: { size: "md" },
  }
)

/**
 * A section heading in the display font, rendered as an `<h2>`. Pass
 * `render` to use another heading level. Its lines are balanced when it
 * wraps.
 *
 * `size` picks the scale, and each size steps up once the nearest
 * `@container/section`, such as a `Section`, is at least 42rem wide: `sm`
 * from 32px to 40px, `md` (the default) from 32px to 48px, `lg` from 36px
 * to 72px and `xl` from 64px to 96px. Without that container it keeps the
 * smaller size.
 */
function Heading({
  className,
  size = "md",
  render,
  ...props
}: useRender.ComponentProps<"h2"> & VariantProps<typeof headingVariants>) {
  return useRender({
    defaultTagName: "h2",
    render,
    state: { slot: "heading", size },
    props: mergeProps<"h2">(
      { className: cn(headingVariants({ size }), className) },
      props
    ),
  })
}

export { Heading }

Installation

pnpm dlx shadcn add @01orbital/heading
npx shadcn add @01orbital/heading
yarn shadcn add @01orbital/heading
bunx --bun shadcn add @01orbital/heading

Free under the MIT license. Add the @01orbital registry to components.json once (install guide), then run the command.

It lands at components/editorial/heading.tsx, as source you own and edit.

The example also uses section. Install it too:

pnpm dlx shadcn add @01orbital/section
npx shadcn add @01orbital/section
yarn shadcn add @01orbital/section
bunx --bun shadcn add @01orbital/section

Usage

import { Heading } from "@/components/editorial/heading"
import {
  Section,
  SectionContent,
} from "@/components/editorial/section"

const COPY = {
  text: "A strong start. Room to make it yours.",
}

export default function HeadingDemo() {
  return (
    <Section className="py-0">
      <SectionContent className="px-0 @5xl/section:px-0">
        <Heading>{COPY.text}</Heading>
      </SectionContent>
    </Section>
  )
}

API reference

Prop

Type

It also accepts the props of the Base UI part it wraps, and the HTML attributes of the element it renders.

Dependencies

npm: @base-ui/react, class-variance-authority, cn. The CLI installs them for you.

Registry pieces it installs with it:

  • font-hanken-grotesk