Card 1
A short description for card 1.
An editorial section block. A small uppercase eyebrow sits above a heading, an optional description softens into the body, and the main content slot fills the rest of the section. When an aside is provided, the body splits into a 2-column grid on wider viewports so the aside rides alongside the content. The component owns the section frame only — eyebrow / heading scale and the vertical rhythm between regions — so it composes with any card, grid, prose, or callout block.
A short description for card 1.
A short description for card 2.
npx shadcn@latest add https://craftbits.dev/r/craft-section.jsonimport { CraftSection } from "@craft-bits/core";
<CraftSection
eyebrow="Featured"
heading="What we shipped this week"
description="A pair of editorial entries with a small sidekick lane."
>
<FeaturedGrid />
</CraftSection>Add an aside and the body splits into a 2-column grid above md:
<CraftSection
eyebrow="Chapter 02"
heading="On the editorial measure"
aside={<TableOfContents />}
content={<Prose>{essay}</Prose>}
/>Drop every slot except content for a pure-body editorial frame:
<CraftSection content={<RecipeCardGrid recipes={recipes} />} />var(--cb-font-mono) (with a system-monospace fallback) at 0.6875rem with tracking-[0.15em] so it reads as a kicker, not a heading.headingLevel picks h2 (default), h3, or h4 so the section can nest inside another sectioning element without breaking the heading outline. Supply headingId if you need to mirror it on a custom outer container's aria-labelledby.md. When aside is present the body becomes a grid-cols-1 on narrow viewports and grid-cols-[minmax(0,1fr)_auto] from md upward; asidePosition flips the visual order without changing the DOM order.gap is a single CSS variable. The chosen gap value (sm / md / lg) lands on --cb-section-gap and powers both the header → body separation and the content ↔ aside gutter in one place.content wins over children. Either slot fills the body; if both are present content takes precedence so consumers can pass a structured slot without losing the JSX child shorthand for the simple case.--cb-fg, --cb-fg-muted, --cb-bg-elevated, --cb-border-muted) flow in from the surrounding scope.Bare editorial frame — eyebrow + heading + content, nothing else:
<CraftSection eyebrow="~/craft" heading="this guy cooks">
<FeaturedGrid />
</CraftSection>Centered hero-style section:
<CraftSection
eyebrow="Showcase"
heading="What's new"
description="Three editorial blocks, centered."
align="center"
gap="lg"
>
<ShowcaseGrid />
</CraftSection>Sidebar-on-the-left layout:
<CraftSection
eyebrow="Reference"
heading="API"
asidePosition="left"
aside={<TableOfContents />}
content={<PropsTable />}
/>Nested section under an h1:
<CraftSection
eyebrow="Section"
heading="Recently shipped"
headingLevel="h3"
content={<RecipeGrid />}
/>| Prop | Type | Default | Description |
|---|---|---|---|
eyebrow | ReactNode | — | Small uppercase kicker rendered above the heading. |
heading | ReactNode | — | Main heading content. Rendered inside the element picked by headingLevel. |
description | ReactNode | — | Optional supporting copy between heading and body. |
content | ReactNode | — | Main body slot. Takes precedence over children when both are present. |
children | ReactNode | — | Shorthand body slot. Used when content is omitted. |
aside | ReactNode | — | Optional aside that rides alongside content above md. |
asidePosition | 'right' | 'left' | 'right' | Which side the aside renders on for md and up. |
headingLevel | 'h2' | 'h3' | 'h4' | 'h2' | Heading element rendered for heading. |
headingId | string | — | Optional explicit id forwarded to the heading and to the section's aria-labelledby. |
gap | 'sm' | 'md' | 'lg' | 'md' | Vertical rhythm between header and body (and the content ↔ aside gutter). |
align | 'start' | 'center' | 'start' | Text alignment for the header region. |
className | string | — | Merged onto the section root via cn(). |
<section> landmark. When heading and headingId are both supplied, the section is labelled by the heading via aria-labelledby so assistive tech announces the region with its title.--cb-fg, --cb-fg-muted) meets WCAG AA against --cb-bg and --cb-bg-elevated in the default light and dark themes.prefers-reduced-motion fallback is required.h1 per page — bump headingLevel to h3 or h4 when nesting inside another sectioning region.terminal-dreams (src/components/retro/CraftSection.tsx). The source was a hardcoded ~/craft block with two inline SVG-illustrated cards linking to /cookbook and /playground, themed via terminal-dreams' --color-accent / --color-surface / --font-mono variables. craft-bits keeps the editorial structure (eyebrow kicker + heading over a body) but reshapes the API: the eyebrow, heading, description, content, and aside become slots; the retro mono treatment falls back to the consumer's --cb-font-mono token; the hardcoded card pair is removed so the section composes with any grid, card, or prose primitive.