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, notrender.renderreplaces 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)](plustruncateif long titles should clip rather than wrap) so your heading matches atitlevisually.<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-1if you also useactions. The header row is a plain flex row; the wrapper aroundtitle/description/extrasis 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 andactionssit 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