Skip to contentAsheeUIAsheeUI

Section

A full-width band that owns the vertical rhythm and the background behind a group of a page's content.

A section is the band a page is divided into: a full-width strip that claims vertical room around its content, paints one of three backgrounds behind it, and optionally holds the content to the framework's column. It is how a page states "this is one part of me, and the next part starts here".

It owns the rhythm and the surface, and it deliberately does not own the width unless asked: Container is the width on its own, and Section composes either way round, a section can contain a container, or a page can place a container around several sections.

Usage

import { Section, Typography, VStack } from "@asheeui/web";

<Section spacing="xl" background="muted" contained>
  <VStack gap="sm">
    <Typography role="heading-xl" as="h2">
      Everything a campaign needs
    </Typography>
    <Typography role="body-lg" tone="muted">
      Segment, schedule and measure from one place.
    </Typography>
  </VStack>
</Section>;

The children are laid out by whatever you put inside, the band supplies room around them, not a gap between them. That is the division of labour: spacing is the band's rhythm, and the rhythm inside the band is a Stack's.

The band's rhythm

spacing is the vertical padding the band claims above its first child and below its last, and it is the only vertical space a section adds. A band with spacing="2xl" separates two parts of a page that are not otherwise related; "md" tightens a page that already has headings doing the separating.

Pick a token and the band grows around its content without moving it in the reading order. The scale is the shared one, so a section's rhythm and a stack's gap are the same seven words, see Layout and spacing.

Backgrounds and separators

Three treatments, and one separator. none leaves the page background showing through, which is the right answer for the band a page opens with; muted draws a subdued surface, so a long page reads as several parts; tinted is a faint accent for the one band that should be noticed, a pricing table, a closing call to action.

divider draws a rule above the band, which is what keeps two bands of the same treatment from running together. It is a border on the top edge of the band that asks for it, so the boundary is drawn once rather than twice by the two bands either side of it.

The three treatments are shared vocabulary rather than a web idea: @asheeui/core declares SectionBackground, so a hero, a call to action and a section all name one type. muted paints the theme's secondary at half strength and tinted its primary at five percent, so an application that recolours those tokens changes every band that asks for them at once.

Contained, or the page's column

contained says whether the band wraps its children in a Container, and containerSize says which width. It is off by default, so a section is a band and nothing else until you ask for a column, which is what allows both compositions: a Container inside a full-bleed Section, or several full-bleed Sections inside one Container in PageContent.

The band's content and its background are independent: a contained section paints its background the full width of the window and holds only the content to the column, which is the shape a coloured band with a centred headline takes. Turning contained off keeps the children where they are in the document and hands the width decision to the parent.

as decides the element, and the default is a <section>, which is correct while the band carries a heading. A band that is purely a rhythm and a surface should render as="div", so the page does not gain an unnamed region.

The section kit

A band, a heading and a row of actions ship together. SectionHeading is the eyebrow, title and description block the framework's own bands present, ActionGroup renders configured actions through the public Button, and SkipLink is the first focusable element of a page. They are public through the @asheeui/web/section-kit subpath rather than the package entry point, so they are imported from there:

import { Section, VStack } from "@asheeui/web";
import { ActionGroup, SectionHeading } from "@asheeui/web/section-kit";

<Section contained background="muted" spacing="xl" aria-labelledby="workflow-heading">
  <VStack gap="lg">
    <SectionHeading
      eyebrow="Workflow"
      title="Everything a campaign needs"
      description="Segment, schedule and measure from one place."
      titleId="workflow-heading"
      size="lg"
      titleAs="h2"
    />
    <ActionGroup
      primaryAction={{ label: "Start free", href: "/signup" }}
      secondaryAction={{ label: "Read the docs", href: "/docs" }}
    />
  </VStack>
</Section>;

titleId exists so the band can name itself with aria-labelledby; the heading's level is the caller's, because a size chooses a typography role rather than a place in the outline.

SkipLink is the other half of that arrangement, the way into the content past repeated navigation. It takes the id of the region it leads to, which is expected to be focusable, so following the link moves the reader into the band rather than only scrolling to it:

import { SkipLink } from "@asheeui/web/section-kit";

<SkipLink targetId="campaigns" label="Skip to campaigns" />;

<Section id="campaigns" tabIndex={-1} aria-labelledby="campaigns-heading">
  …
</Section>;

Props

All five of the section's options are registered by the component, spacing: "lg", background: "none", contained: false, containerSize: "lg" and divider: false, so components.section in the application configuration restates the band once and every section follows. Nothing here follows defaultVariant or defaultRadius: a band has no variant and no corners, and the three backgrounds are the framework's own vocabulary.

The heading block is instance-only, it has no configuration section, because a band states its own heading rather than inheriting one. size resolves the typography role: md is a section title, lg and xl are the two sizes a page's leading statement takes.

A configured action is a description rather than a React element: label is the only required field, href makes it a link and omitting it makes it a button, and the remaining fields map onto Button's own props. The primary action renders solid and the secondary bordered unless the action states a variant of its own, and children is where an action that needs a handler goes, because a configured action describes a destination.

targetId and label are both required: a skip link without a destination does nothing, and one without wording has no accessible name. The classes that hide it until it is focused are exported as SKIP_LINK_CLASS if you need to place it yourself.

Accessibility

  • <section> is announced as a region only when it has an accessible name. Give the band a heading and point aria-labelledby at it, SectionHeading's titleId is there for exactly that, or render as="div" so the page does not gain an unnamed region.
  • The band itself renders no heading and sets no level: a <section> is not an <h2>. The heading block picks both together from size, and its default element is the one the role implies, md is an h2, while lg and xl are an h1. State titleAs when the band is not the first statement of the page.
  • ActionGroup renders each configured action through Button, so every action keeps its own accessible name, its own activation and the framework's focus ring. The group stacks on a narrow screen and becomes a row from the sm breakpoint without changing the DOM order.
  • The background and the divider are decoration. Nothing is conveyed by a treatment alone, so a band whose meaning depends on being noticed still needs a heading that says so.
  • SkipLink is visually hidden until it receives focus, and it is the first focusable element of the page. Its target must be focusable (tabIndex={-1}), or following the link scrolls without moving the reader.

The spacing scale, the width tokens and as are shared by every layout; see Layout and spacing.