Recipe Header

A page header for article, recipe, or guide screens. Renders a large serif title with an optional eyebrow kicker above it, a short description, a freeform row of meta chips, and an optional trailing actions slot. Pure CSS — no motion, no client state.

Bread · 04

Sourdough, no fuss

A 24-hour loaf with three folds, one bake, and no special equipment beyond a Dutch oven.

45 min2 loavesIntermediateveganmake-ahead
Customize
Layout
comfortable
start
Slots

Installation

npx shadcn@latest add https://craftbits.dev/r/recipe-header.json

Usage

RecipeHeader is a Radix-style compound. The root paints the header bar; RecipeHeader.Meta is a single chip in the meta row; RecipeHeader.Tag is a pill-shaped tag for taxonomy values.

import { RecipeHeader, ClockIcon, ServingsIcon } from "@craft-bits/core";
 
<RecipeHeader
  title="Sourdough, no fuss"
  eyebrow="Bread · 04"
  description="A 24-hour loaf with three folds and one bake."
  meta={
    <>
      <RecipeHeader.Meta icon={<ClockIcon className="h-4 w-4" />}>
        45 min
      </RecipeHeader.Meta>
      <RecipeHeader.Meta icon={<ServingsIcon className="h-4 w-4" />}>
        2 loaves
      </RecipeHeader.Meta>
      <RecipeHeader.Meta>Intermediate</RecipeHeader.Meta>
      <RecipeHeader.Tag>vegan</RecipeHeader.Tag>
    </>
  }
/>

Add a trailing actions slot — primary actions, share buttons, bookmarks, etc.:

<RecipeHeader
  title="Quicksort, visualised"
  eyebrow="Algorithms"
  description="Watch the partition step shuffle in real time."
  actions={<button>Bookmark</button>}
/>

Skip the eyebrow, description, or meta row by omitting the corresponding prop — every part is optional except title.

Understanding the component

  1. Compound parts. RecipeHeader is the bar; RecipeHeader.Meta is one chip; RecipeHeader.Tag is a pill tag. Composing the meta row rather than passing an array of items means each chip owns its own icon and formatting.
  2. Title is a heading-1. Every page header is a top-level landmark — assistive tech announces the title as the document heading.
  3. Eyebrow above, description below. The eyebrow is an uppercase mono kicker — short, label-like. The description is a body paragraph in muted ink, set with text-wrap: pretty so line endings break gracefully.
  4. Meta row is freeform. Drop any nodes you want — RecipeHeader.Meta chips, plain text, custom badges. The row is flex-wrap with consistent gaps; it stays single-line on wide screens and stacks on narrow.
  5. Tabular numerals on meta. Each RecipeHeader.Meta enables font-variant-numeric: tabular-nums so numeric values align column-cleanly when stacked.
  6. Actions slot pins right. When actions is present, the inner column switches to items-start justify-between so the actions ride the top-right corner.
  7. Density variants. compact, comfortable, and spacious tune the vertical padding for dense dashboards vs. magazine-style spreads.
  8. Data hooks. The root carries data-cb-recipe-header, data-density, and data-align so consumers can target the header from a wrapping layout without re-deriving props.

Variants

Bare — title only

<RecipeHeader title="Quick read" />

With actions

<RecipeHeader
  title="API reference"
  eyebrow="v3.2"
  actions={<button>Edit on GitHub</button>}
/>

Centered, spacious

<RecipeHeader
  align="center"
  density="spacious"
  title="Welcome"
  description="The brief, the bar, the bits."
/>

Compact (dashboard-style)

<RecipeHeader
  density="compact"
  title="Run 0149"
  eyebrow="Experiments"
  meta={<RecipeHeader.Meta>12s ago</RecipeHeader.Meta>}
/>

Props

RecipeHeader

PropTypeDefaultDescription
titleReactNodeRequired. Primary heading.
eyebrowReactNodeSmall kicker label above the title.
descriptionReactNodeShort paragraph below the title.
metaReactNodeSlot for a freeform row of meta chips.
actionsReactNodeTrailing slot — usually primary actions.
density'compact' | 'comfortable' | 'spacious''comfortable'Vertical padding scale.
align'start' | 'center''start'Text alignment of the title column.
innerClassNamestringOverride classes on the centered inner column.
classNamestringMerged onto the rendered header via cn().
...restHTMLAttributes<HTMLElement>Any other header attribute.

RecipeHeader.Meta

PropTypeDefaultDescription
iconReactNodeOptional leading 16px icon.
classNamestringMerged onto the rendered span via cn().
...restHTMLAttributes<HTMLSpanElement>Any other span attribute.

RecipeHeader.Tag

PropTypeDefaultDescription
tone'accent' | 'neutral' | 'muted''accent'Pill color tone.
classNamestringMerged onto the rendered span via cn().
...restHTMLAttributes<HTMLSpanElement>Any other span attribute.

Accessibility

  • The root renders a header landmark and the title is a heading-1 — screen readers announce the page heading correctly.
  • ClockIcon and ServingsIcon default to aria-hidden="true"; the surrounding RecipeHeader.Meta text supplies the accessible name.
  • Color contrast: the title uses --cb-fg, the description uses --cb-fg-muted, the meta chips use --cb-fg-muted / --cb-fg-subtle. All pass WCAG AA on every default surface.
  • No motion is applied. Reduced-motion respect is the responsibility of any custom action button passed into the actions slot.
  • The title uses text-wrap: balance so multi-line headings break evenly; the description uses text-wrap: pretty to avoid orphan words.

Credits

  • Extracted from: terminal-dreams (src/components/cookbook/RecipeHeader.tsx). The original was tightly coupled to a CookbookRecipe shape (recipe.meta.totalTime, recipe.meta.servings, recipe.meta.difficulty, recipe.meta.cuisine, recipe.meta.tags) and rendered a sibling BreadcrumbBar inside the same fragment. craft-bits strips every cookbook-specific concern and keeps the underlying intent — a page header for an article-style screen with title, eyebrow, description, meta chips, and actions. The compound Meta / Tag shape is new; the original inlined every piece.