Acronis UIKit
Components

Section

A titled band that groups cards — or a table — on a page.

Usage

import { Section, SectionHeader, SectionContent } from '@acronis-platform/ui-react';

Section is a compound component: a page-level band that labels a run of content and separates it from the next one. Where Card owns a single surface and CardSection divides a card's body, Section sits above both.

The root owns two props. variant picks the content layout — one full-width column (column1), a 70/30 split (column2-70-30), a three-column card grid (grid3), or a flush table (table) — and is published to the header and content parts through context, so you set it in exactly one place. hasBottomBorder draws the divider that separates one band from the next.

SectionHeader carries the title (20px medium), an optional description, an optional toggle switch, inline extras, and end-aligned actions. All parts are optional — there is no hasHeader flag, you simply omit SectionHeader — and every part is polymorphic via Base UI's useRender (the render prop). Its children slot takes anything extra the row needs between the title and the actions; that's also where a custom heading goes.

There is no --ui-section-* token tier: the title, description, and divider resolve to the shared semantic tokens (--ui-text-on-surface-primary, --ui-text-on-surface-secondary, --ui-border-on-surface-divider).

Examples

A row of cards under one title:

<Section variant="grid3">
  <SectionHeader title="Recent activity" />
  <SectionContent>
    <Card>…</Card>
    <Card>…</Card>
    <Card>…</Card>
  </SectionContent>
</Section>

A 70/30 split — children take the wide column, secondaryContent the narrow one:

<Section variant="column2-70-30">
  <SectionHeader
    title="Storage"
    description="Usage across every connected location."
    hasDescription
  />
  <SectionContent secondaryContent={<QuotaCard />}>
    <LocationsCard />
  </SectionContent>
</Section>

A full-bleed table. The table variant drops the root's padding entirely so rows reach the page edges; the header re-applies the horizontal inset so the title still lines up with the first column:

<Section variant="table">
  <SectionHeader
    title="Workloads"
    actions={
      <ButtonIcon aria-label="More actions">
        <EllipsisIcon size={24} />
      </ButtonIcon>
    }
  />
  <SectionContent>
    <WorkloadsTable />
  </SectionContent>
</Section>

A section with a toggle switch in the header:

<Section>
  <SectionHeader
    title="Email notifications"
    description="Send a digest whenever a plan fails."
    hasDescription
    isSwitchable
    defaultSwitchChecked
    switchLabel="Toggle email notifications"
  />
  <SectionContent>…</SectionContent>
</Section>

Stacking sections

A section reserves no bottom padding unless hasBottomBorder is set — the next band already opens with its own top inset, so reserving it would double the gap. Set hasBottomBorder on every section but the last when you want visible separators:

<Section hasBottomBorder>…</Section>
<Section variant="grid3" hasBottomBorder>…</Section>
<Section>…</Section>

The table variant stays flush either way: hasBottomBorder adds the divider without reintroducing padding.

Collapsible

Compose Section with AccordionContainer (the shared disclosure primitive), exactly as Card does: wrap SectionContent in AccordionContainer.Content and set SectionHeader's isCollapsible to render the trigger. isCollapsible only has an effect inside a collapsible AccordionContainer; it's a no-op otherwise. The header stays visible in both states.

import { AccordionContainer, Section, SectionContent, SectionHeader } from '@acronis-platform/ui-react';

<Section variant="grid3">
  <AccordionContainer collapsible defaultOpen>
    <SectionHeader
      title="Protection"
      description="3 plans applied to 24 workloads."
      hasDescription
      isCollapsible
      collapseLabel="Toggle protection section"
    />
    <AccordionContainer.Content>
      <SectionContent>
        <Card>…</Card>
        <Card>…</Card>
        <Card>…</Card>
      </SectionContent>
    </AccordionContainer.Content>
  </AccordionContainer>
</Section>

Accessibility

The root renders a real <section>, which only becomes a region landmark once it has an accessible name — add aria-label / aria-labelledby when the band is genuinely navigable content, and leave it off otherwise so the landmark list stays useful.

collapseLabel and switchLabel are the only strings the component renders on its own; override them per section when a page stacks several collapsible or switchable bands.

Supplying your own heading

The title prop renders a <p>, not a heading. A section's place in the document outline depends on the page around it, so guessing a level (h2? h3?) would produce a broken hierarchy in most compositions.

When the band really is a document section, put your own heading in SectionHeader's children slot instead: omit title so you don't render two titles, match the title's text scale yourself, and point the root's aria-labelledby at your heading.

<Section aria-labelledby="protection-heading">
  <SectionHeader actions={<Button variant="secondary">Manage</Button>}>
    <h2
      id="protection-heading"
      className="min-w-0 flex-1 truncate text-xl leading-6 font-medium text-[var(--ui-text-on-surface-primary)]"
    >
      Protection
    </h2>
  </SectionHeader>
  <SectionContent>…</SectionContent>
</Section>

Three things to know about this recipe:

  • Use children, not render. render replaces the entire header row, so a heading passed that way would wrap the switch, description, extras, actions, and collapse trigger inside the heading element.
  • There is no exported class for the title's scale — copy text-xl leading-6 font-medium text-[var(--ui-text-on-surface-primary)] (plus truncate if long titles should clip rather than wrap) so your heading matches a title visually. <h2> also carries a browser default size, weight, and margin; those utilities override the first two and ui-react's Tailwind preflight zeroes the margin.
  • Add min-w-0 flex-1 if you also use actions. The header row is a plain flex row; the wrapper around title / description / extras is what normally claims its free space, and it isn't rendered when you omit all three. Without a flex-grow child of your own, the heading and actions sit side by side at the inline start instead of splitting across the row.

children render after that wrapper and before actions, so a heading in the slot lands at the inline start of the row — the same place title would have.

API Reference

Section

Prop

Type

SectionHeader

Prop

Type

SectionContent

Prop

Type

Edit on GitHub

On this page