Components › Navigation › App shell › Split view

Split view

beta

The one two-pane layout: aside next to the main content (rail | detail, list | reader, work | discussion); bar above, not resizable.

React lives in the app (SplitView.tsx); stories render its markup 1:1. Beta 4 Oct 2026: stories + usage + a11y done; canon after AD approval. Open: settings (OpravneniRole, SadyOpravneniTab) use SplitView with fill={false} although DS-SPLITVIEW lists Settings as not-SplitView, and pass gap without divider="gap" (no effect).

Used in 32 files · 28 areas · 33× — components/GlobalSearch, components/agent-desk, components/company, pages/AccountingInvoiceDetailPage, pages/AccountingInvoiceEditPage, pages/AsistentPlochaPage …

Keyboard

  • The split adds no controls and no shortcuts; Tab moves through the content of the first pane in the DOM, then the second.
  • DOM order follows what the eye reads first: with the aside on the left the aside comes first; with asidePosition="right" the main pane comes first and the aside (discussion) after it. On the phone the panes stack in the same order, so tab order and visual order never disagree.
  • With fill each pane is its own scroll container. Chromium makes a scroll container without focusable content keyboard-focusable; in other browsers a pane with no focusable content can be scrolled only by pointer.
  • There is no keyboard way to resize the divider because the split is not resizable.

ARIA

  • .sv, .sv-aside and .sv-main are plain <div>s with no role: the split is layout, not a landmark. The landmark and its name belong to the content — the page's <main>, a rail's own heading, a list's label.
  • Do not add role="separator" to the divider; it is a border, not an interactive splitter.
  • Loading content inside a pane carries aria-busy itself (e.g. SkeletonList); the split does not announce anything.

Focus

  • No focus rule and no outline: none in split-view.css; controls inside the panes use the foundation ring from ds.css (outline: 2px solid var(--accent); outline-offset: 1px).
  • .sv-fill > .sv-aside clips horizontal overflow (overflow-x: hidden): a focus ring on a control flush with the pane edge can be cut. .sv-pad keeps controls --sp-5 from the edge, which is why bare panes must use it.
  • The split never moves focus. Selecting a record in the aside does not move focus to the main pane unless the screen does so itself.

Contrast

Computed from packages/ui/src/tokens/tokens.css:

  • The split has no text or fill of its own; panes sit on the page surface, so their text contrast is that of the content.
  • The divider is a --border hairline — decorative, and it is not the only cue: the panes are told apart by their content and headings.

Known limits

  • The divider line is below 3:1 against the surface; it is decorative and the split does not rely on it.
  • In non-Chromium browsers a pane with no focusable content cannot be scrolled from the keyboard.
  • Nothing names the two panes as regions; a screen-reader user relies on the headings inside them.

Source: docs/app-shell/split-view.a11y.md · GitLab