Spec version v0.8.0

Sidebar-Only Shell

Purpose

The sidebar-only shell is a simplified application frame that uses a fixed sidebar as the sole chrome element — there is no topbar. The product wordmark and brand identity live in the sidebar header zone, and global utilities (user avatar, settings) relocate to the sidebar footer. This is the default starting point for products with a single navigation tier — focused tools, admin panels, documentation sites, and internal utilities that do not need product area switching, an app switcher, or global search. For products that need a persistent topbar, use the page-shell pattern instead. See the Layout Selection section in design.md for the full decision framework.

Structure

The shell is composed of two regions: a fixed sidebar on the left and a scrollable content panel filling the remaining viewport.

+------------------+----------------------------------------------------+
|                  |                                                     |
|  Sidebar         |   ╭──────────────────────────────────────────╮     |
|  256px wide      |   │  Content Panel (inset rounded panel)     │     |
|  fixed position  |   │  background: --force-color-bg-surface    │     |
|  full height     |   │  border-radius: --force-radius-xl (12px) │     |
|                  |   │  padding: --force-layout-content-padding │     |
|  +-----------+   |   │  overflow-y: auto (sole scroll region)   │     |
|  | Brand /   |   |   │  fills available width                   │     |
|  | Wordmark  |   |   ╰──────────────────────────────────────────╯     |
|  +-----------+   |   ↑ grey chrome (bg-emphasis) shows through        |
|  | Nav Items |   |                                                     |
|  |           |   |                                                     |
|  +-----------+   |                                                     |
+------------------+----------------------------------------------------+

The sidebar occupies the full viewport height from top: 0 to bottom: 0. Its width is --force-layout-sidebar-width (256px). The sidebar background is --force-color-bg-emphasis. It does NOT have a right border — the inset content panel provides visual separation.

The sidebar header zone is fixed-height at --force-layout-header-height (64px) and contains the product wordmark, logo, or brand name. This zone does not scroll. See the sidebar-navigation pattern for the full sidebar structure (sections, items, groups, footer).

The content panel follows the same inset rounded pattern as the page shell. It uses position: fixed with edges: top: var(--force-spacing-2) (inset gap from viewport top), left: calc(var(--force-layout-sidebar-width) + var(--force-spacing-2)) (sidebar width plus gap), right: var(--force-spacing-2), bottom: var(--force-spacing-2). Unlike the page shell where the panel is flush against the topbar, the sidebar-only shell has the inset gap on all four sides — top, left, right, and bottom — because there is no topbar to sit flush against.

The content panel uses overflow-y: auto and is the only scrollable region. The <body> element uses overflow: hidden to prevent page-level scroll.

Its background is --force-color-bg-surface (white) with border-radius: var(--force-radius-xl) (12px). Inner content is padded by --force-layout-content-padding (24px). The panel fills the available width — do NOT cap it with a max-width. Inner columns may opt in to --force-layout-content-max-width (1400px) as a reading-measure constraint when the content type benefits (forms, long-form prose).

Responsive Behavior

At and above --force-breakpoint-lg (992px), the sidebar displays in expanded mode.

Between --force-breakpoint-md (768px) and --force-breakpoint-lg (992px), the sidebar enters shrunk mode at --force-layout-sidebar-collapsed (64px). The content panel expands to fill the freed space. The content panel's left inset adjusts to calc(var(--force-layout-sidebar-collapsed) + var(--force-spacing-2)).

Below --force-breakpoint-md (768px), the sidebar becomes an off-canvas drawer. A hamburger button is fixed in the top-left corner of the viewport. The content panel fills the full viewport width with no inset gaps or rounded corners. When the sidebar drawer is open, it slides in from the left at 288px wide with a backdrop overlay behind it.

Token Usage

Element Token
Sidebar width (expanded) --force-layout-sidebar-width (256px)
Sidebar width (shrunk) --force-layout-sidebar-collapsed (64px)
Sidebar background --force-color-bg-emphasis
Sidebar right border None — the inset content panel provides visual separation
Sidebar header height --force-layout-header-height (64px)
Content panel background --force-color-bg-surface
Content panel border 1px solid --force-color-border-default — reinforces the panel edge
Content panel border radius --force-radius-xl (12px)
Content panel padding --force-layout-content-padding / --force-spacing-6 (24px)
Inner-column reading-measure cap (opt-in, not applied to the panel) --force-layout-content-max-width (1400px)
Content panel inset gap --force-spacing-2 (8px) — all four sides
Body overflow hidden — content panel is the sole scroll container
Mobile sidebar z-index --force-zIndex-modal (500)
Sidebar transition duration --force-duration-normal (250ms)
Sidebar transition easing --force-easing-standard

Accessibility

The sidebar is a <nav> element with aria-label="Site navigation". The content area uses a <main> element with role="main".

A skip navigation link must be the first focusable element in the DOM, targeting the <main> element. It should read "Skip to main content."

Tab order proceeds: skip link, then sidebar items top-to-bottom, then main content.

When the sidebar is in its off-canvas mobile state and closed, its contents must be aria-hidden="true" and all interactive elements must carry tabindex="-1". When opened, focus moves to the first focusable element inside the sidebar. Pressing Escape closes the drawer and returns focus to the hamburger trigger.

The hamburger trigger carries aria-expanded reflecting open/closed state and aria-controls pointing to the sidebar element's id.

Guidance

Use this shell when the product has a single navigation tier and does not need a persistent topbar — typically focused tools, admin panels, documentation sites, or internal utilities. When in doubt between this and the page shell, start here — adding a topbar later is straightforward because the sidebar structure remains unchanged.

If the application later needs product area switching, an app switcher, global search, or a persistent brand/utility strip, migrate to the full page-shell pattern rather than overloading the sidebar with topbar concerns.

In this shell, global utilities that would otherwise live in the topbar (user avatar, settings, account link) are placed in the sidebar footer zone. See the sidebar-navigation pattern for footer zone details.

The brand in the sidebar header should be a link to the application root. Use --force-color-text-primary-brand for brand text, which has appropriate contrast in both light and dark themes.

All other guidance from the page-shell pattern applies: shell owns the content padding, do not use --force-color-bg-emphasis inside the content area, and the content panel fills the available width — only inner columns (forms, long-form prose) opt in to --force-layout-content-max-width.

Composition

The sidebar follows the sidebar-navigation pattern for its internal structure. The content panel is a <main> element. Individual pages within the content area use the same layouts as the page shell: list-page (PageHeader + DataGrid), detail-view (PageHeader + TabList + content), or prose content with appropriate max-width constraints.