Skip to content
Facade UI
Colour mode
GitHub (opens in a new tab)
uiatom

Section

The outer band: landmark element, vertical rhythm token, and the aria-labelledby wiring.

Preview

Open full width (opens in a new tab)
Preview width

Installation

Package manager
pnpm dlx shadcn@latest add https://facadeui.dev/r/section.json

Pulls in types, utils. The CLI installs them for you.

Usage

The exact source of the preview above.

demos/section.tsx
import { Container } from "@registry/ui/container"
import { Section } from "@registry/ui/section"
import { SectionHeader } from "@registry/ui/section-header"

export function Demo() {
  return (
    <Section
      spacing="sm"
      labelledBy="facade-a-named-band"
      className="bg-muted/40 rounded-xl"
    >
      <Container size="md">
        <SectionHeader
          eyebrow="Structure"
          title="A named band"
          description="Section pairs aria-labelledby with the id SectionHeader puts on the heading, so it becomes a real landmark instead of an unnamed region."
        />
      </Container>
    </Section>
  )
}

Source

What the CLI copies into your project, byte for byte.

components/ui/section.tsx
/**
 * Section — the outer band every Facade section renders.
 *
 * Centralises the three things that are easy to get wrong and tedious to repeat:
 * the landmark element, the vertical rhythm token, and the `aria-labelledby`
 * link to the section's own heading.
 *
 * a11y: a `<section>` only becomes a landmark once it has an accessible name, so
 * this always pairs `aria-labelledby` with the id `SectionHeader` puts on the
 * heading. An unnamed band should be rendered `as="div"` instead, which keeps it
 * out of the landmark list rather than adding a nameless region.
 *
 * Dependencies: react, @/lib/types, @/lib/utils.
 */

import type { ComponentPropsWithoutRef, ElementType, ReactNode } from "react"

import type { SectionBaseProps } from "@/lib/types"
import { cn } from "@/lib/utils"

export interface SectionProps
  extends
    Omit<ComponentPropsWithoutRef<"section">, "children">,
    Pick<SectionBaseProps, "as" | "spacing"> {
  /** Id of the heading that names this section. Omit only when `as="div"`. */
  labelledBy?: string
  children?: ReactNode
}

const spacings = {
  none: "",
  sm: "py-section-sm",
  md: "py-section",
  lg: "py-section-lg",
} as const

export function Section({
  as = "section",
  spacing = "md",
  labelledBy,
  className,
  children,
  ...props
}: SectionProps) {
  const Component = as as ElementType
  const nameable = as === "section" || as === "article" || as === "aside"

  return (
    <Component
      aria-labelledby={nameable ? labelledBy : undefined}
      className={cn("relative w-full", spacings[spacing], className)}
      {...props}
    >
      {children}
    </Component>
  )
}

Props

SectionProps

Extends Omit<ComponentPropsWithoutRef<"section">, "children">, Pick<SectionBaseProps, "as" | "spacing">.

Props for SectionProps
PropTypeDefault
labelledById of the heading that names this section. Omit only when `as="div"`.string
childrenReactNode