Section Label

A small editorial kicker for stand-alone sections — uppercase, mono-tracked, with an optional hairline rule that extends to the right edge of its row. Use it to label sections that sit outside any numbered figure system: a journey intro, a "what you built" recap, a single-step row.

The journey
Three ways to see it
Step 1
Step 2

Installation

npx shadcn@latest add https://craftbits.dev/r/section-label.json

Usage

import { SectionLabel } from "@craft-bits/core";
 
<SectionLabel>The journey</SectionLabel>

Swap the tone for an accent or status colour:

<SectionLabel tone="accent">Three ways to see it</SectionLabel>
<SectionLabel tone="success">Verified</SectionLabel>

Drop the hairline for a tight inline render where two labels share a row:

<div className="flex items-center gap-6">
  <SectionLabel decor="none">Step 1</SectionLabel>
  <SectionLabel decor="none">Step 2</SectionLabel>
</div>

Understanding the component

  1. Editorial kicker, not a heading. SectionLabel renders a small uppercase span, not a <h2> / <h3>. Pair it with a heading underneath when you need an a11y landmark; keep it on its own when the section is purely visual.
  2. Tone is semantic. default is the muted kicker tone (--cb-fg-subtle); accent, success, warning, error, muted thread the corresponding cb token colour through the label text. The hairline rule colour stays constant (--cb-border-muted) regardless of tone.
  3. Decor controls the rule. rule (default) draws an aria-hidden hairline that fills the remaining row width; none drops the rule so the label collapses to its natural intrinsic width.
  4. Children accept ReactNode. Compose icon + text, count badge + text, or any inline JSX — children are not forced to a string.
  5. Data hooks. The root carries data-cb-section-label, data-tone, and data-decor so consumers can target nested regions without re-deriving props.

Variants

Default kicker

<SectionLabel>The journey</SectionLabel>

Accent tone

<SectionLabel tone="accent">Three ways to see it</SectionLabel>

Inline pair, no rule

<div className="flex items-center gap-6">
  <SectionLabel decor="none">Step 1</SectionLabel>
  <SectionLabel decor="none">Step 2</SectionLabel>
</div>

Props

PropTypeDefaultDescription
childrenReactNodeLabel content. Rendered uppercase, mono-tracked. Accepts plain text or composed inline JSX.
tone'default' | 'accent' | 'success' | 'warning' | 'error' | 'muted''default'Semantic colour of the label text. The hairline rule colour stays constant.
decor'rule' | 'none''rule'Decorative element rendered after the label. rule draws an aria-hidden hairline; none drops it.
classNamestringMerged onto the rendered <div> via cn().
...restHTMLAttributes<HTMLDivElement>Any other <div> attribute.

Accessibility

  • The decorative hairline is aria-hidden — never announced to assistive tech.
  • The label itself is a plain <span> inside the row. It is not a heading or landmark; pair it with a heading element when the section needs an a11y title.
  • Colour contrast: every tone reads from the cb token system, where each colour clears WCAG AA on the default --cb-bg and --cb-bg-elevated surfaces.
  • No motion. Reduced-motion respect is the responsibility of the surrounding content.

Credits

  • Extracted from: algoflashcards (src/lessons/primitives/chrome/SectionLabel.tsx). The original threaded a hex accentHex prop directly onto an inline style attribute and accepted a binary hairline prop. craft-bits replaces the hex prop with a semantic tone token and renames hairline to decor to leave room for future decorative variants.