Skip to content

ExpressiveCSS

Browse documentation

Panes

Material 3 canonical layouts, from the HTML.

View as Markdown

A .panes (or .pane-layout, or .list-detail) is the container. Two or three .pane children are the surfaces. The component has no JavaScript, so the HTML structure sets the layout.

Tokens follow the M3 canonical layouts. Coplanar is the default, with full-bleed surfaces and a 1dp outline-variant divider. Compact widths add 16dp inline margins. Every wider layout uses 24dp margins and 24dp spacers. Give the container a height. It is height: 100%.

The split is at 840px, measured against the nearest ancestor with inline-size or size containment. Without one, viewport media rules apply. To adapt a nested layout, put container-type: inline-size on the containing column. Below the split, one pane shows at a time. Google recommends stacking supporting content below primary content on compact and medium layouts. This component's one-active-pane behavior is a framework adaptation.

Semantic child names are aliases of .pane: .list-pane, .detail-pane, .primary-pane, .supporting-pane. Mark the visible pane on compact windows with active. For optional content that should overlay the page, use a side sheet, not a pane.

List-detail

Use .list-detail or a plain .panes for the default layout, a 360dp list pane beside a flexible detail pane. When people pick a row, the detail pane shows that item. Pair the list pane with a list.

<div style="container-type: inline-size">
<div class="list-detail">
  <section class="list-pane">
    <header>
      <h2>Inbox</h2>
    </header>
    <ul class="list">…</ul>
  </section>
  <section class="detail-pane">
    <header>
      <h2>Subject</h2>
    </header>
    <div>…</div>
  </section>
</div>
</div>

Compact

At Compact widths (below 600px), the layout has 16dp inline margins. Medium, Expanded, Large, and Extra-large widths use 24dp inline margins and 24dp spacers. On Compact and Medium widths (below 840px), every pane is hidden except the active one. If none is active, the first pane shows.

AutoInit() does not start panes, so toggle active yourself when the user picks a row or taps back. At Expanded widths both panes show regardless of active, so the same markup works at every size.

The illustration below always shows one pane so you can see the swap. Change the width of #pane-column in developer tools to test the list-detail demo above without resizing the viewport. Its nearest containing column controls the split.

list.querySelectorAll('a').forEach((row) => {
  row.addEventListener('click', (event) => {
    event.preventDefault();
    detailPane.classList.add('active');
    listPane.classList.remove('active');
  });
});

back.addEventListener('click', () => {
  detailPane.classList.remove('active');
  listPane.classList.add('active');
});

Supporting pane

Add supporting, or use .supporting-pane-layout, to put a flexible primary pane next to a 360dp supporting pane on the trailing edge. Use it when the extra content is secondary and should stay in view beside the main content, such as a related list, an inspector or an outline.

Add start (or left) to dock the supporting pane on the leading edge instead.

Article

The primary pane holds the focus of the screen. Supporting content sits beside it on expanded windows and hides on compact ones until you mark it active.

A supporting pane is in the page flow. A side sheet overlays the page and can be dismissed.

Contents

<div class="panes supporting">
  <section class="primary-pane">…</section>
  <section class="supporting-pane">…</section>
</div>

<div class="panes supporting start">…</div>

Equal

Add equal to split two panes 1fr / 1fr. Use it when the two surfaces have the same weight, such as two documents, a diff, or an editor and a preview.

Draft

Write in this pane. Both columns grow equally once the applicable container or viewport is at least 840px wide.

Preview

The preview stays in lockstep with the draft. Neither pane is the list; neither is supporting.

<div class="panes equal">
  <section class="pane">…</section>
  <section class="pane">…</section>
</div>

Three-pane

Add three-pane for a list, a main pane and a supporting pane. It needs 1200px of container width, or of viewport width without a query container. The columns are a 360dp list, a flexible middle and a 360dp supporting pane. Between 840px and 1200px the default two-column list-detail grid still applies, so the third pane wraps. Below 840px it uses the same single-pane active pattern as the others.

Mail

Brunch this weekend?

I will be in your neighborhood doing errands this weekend. Want to grab brunch on Saturday?

Details

  • Ali Connors

    From

  • Sat, 10:00

    When

<div class="panes three-pane">
  <section class="list-pane">…</section>
  <section class="detail-pane">…</section>
  <section class="supporting-pane">…</section>
</div>

Resizing

Write a <button class="drag-handle"> between the two panes of a list-detail or supporting layout to let people resize them at 840px and up. The handle replaces the spacer and the divider, and resizes the list or supporting pane.

People can drag the handle. Left or Right moves it 16px (Shift for 64px), and Home or End moves it to the narrowest or widest. Clicking it, or pressing Enter, steps through narrow, middle and wide. The click is the single-pointer alternative to dragging. A resized pane stays at least 240px wide, and the other pane keeps --md-comp-pane-primary-min-width.

The handle writes the new width to --md-comp-pane-list-width or --md-comp-pane-supporting-width on the layout. Save that value to restore it later. The root bundle starts the PaneResize behavior on load. With the modular entry, call PaneResize.Init(). Equal and three-pane layouts do not resize.

From script, Expressive.PaneResize.width(handle) returns the resizable pane's current width in pixels, or null when the handle is not in a layout that resizes. Expressive.PaneResize.setWidth(handle, width) sets it within the same bounds as dragging. Use it to restore a saved width.

<div class="panes list-detail">
  <div class="list-pane">…</div>
  <button type="button" class="drag-handle" aria-label="Resize list"></button>
  <div class="detail-pane">…</div>
</div>

Appearance

Coplanar is the default. The panes share one surface, with a 1dp divider on expanded windows. Add separated (or floating) to inset the panes 24dp, round them to 16dp, and fill each with surface-container-low.

On a separated pane, add elevated for elevation 1. Add outlined to switch the fill to surface and draw a 1dp outline.

Outlined

Surface, 1dp outline-variant stroke, no elevation.

Elevated

Surface-container-low at elevation 1.

<div class="panes equal separated">
  <section class="pane outlined">…</section>
  <section class="pane elevated">…</section>
</div>

A pane

Each pane is a column. A child <header> (or .pane-header) is the 64dp top bar: icon-only buttons, then a title-large heading. A child <main>, .pane-content, .pane-body, or <div> is the scrolling body. A last-child <footer>, .pane-footer, or <nav> is the 56dp action bar.

A direct .list fills the pane with no extra padding, so list rows can run edge to edge under the header.

<section class="pane">
  <header>
    <button type="button" aria-label="Back">
      <span class="material-symbols" aria-hidden="true">arrow_back</span>
    </button>
    <h2>Headline</h2>
    <button type="button" aria-label="More">
      <span class="material-symbols" aria-hidden="true">more_vert</span>
    </button>
  </header>
  <div>…</div>
  <footer>
    <button type="button" class="text">Action</button>
  </footer>
</section>

Tokens

Token Default
--md-comp-pane-margin 16px Compact; 24px Medium and wider
--md-comp-pane-gap24px
--md-comp-pane-divider-coloroutline-variant
--md-comp-pane-container-colorsurface
--md-comp-pane-container-shape0px (16px separated)
--md-comp-pane-list-width360px
--md-comp-pane-supporting-width360px

No matching pages

  • Source color

    The seed every generated ramp derives from. Pick one and browse the docs — the whole theme follows. Error does not: it is a fixed hue.

    Default source: Mist Iris.

    ExpressiveCSS presets: Lagoon, Harbor, Breeze, Cobalt, Iris.

    Bold

    Soft

    Mist

    Glow