Askr documentation
UI & Componentslimited

Advanced Layout

Advanced Layout: anatomy, keyboard behavior, state, and theming in Askr.

Example

import { Block, Grid } from '@askrjs/themes/components';

<Grid columns={{ base: 1, md: 3 }} gap="lg">
  <Block style={{ gridColumn: 'span 2' }}><ProjectTimeline /></Block>
  <Block><ProjectSummary /></Block>
</Grid>

Purpose

This page extends Layout Primitives into more involved shells — persistent sidebars, nested content regions, and multi-panel apps — using the same `Block`-based primitives plus `Sidebar` and its parts. As with Layout Primitives, there's no single component to reach for; it's a set of composable pieces and a recommended way to combine them.

Install and import

`Sidebar`, `SidebarContent`, `SidebarHeader`, `SidebarFooter`, `SidebarGroup`, `SidebarGroupLabel`, `SidebarGroupContent`, `SidebarMenu`, `SidebarMenuItem`, `SidebarMenuButton`, `SidebarMenuAction`, `SidebarMenuBadge`, `SidebarRail`, `SidebarScope`, `SidebarInset`, and `SidebarTrigger` all come from `@askrjs/themes/sidebar` (also available through the `@askrjs/themes/components` barrel). Combine them with `Block`, `Header`, and `Main` from that same `@askrjs/themes/components` barrel for the surrounding shell — there is no separate `@askrjs/themes/core` subpath; that name doesn't exist in the package.

Live examples

A standard admin layout is `SidebarScope` wrapping a `Sidebar` (with `SidebarHeader`, `SidebarContent` full of `SidebarGroup`/`SidebarMenu` entries, and `SidebarFooter`) alongside a `SidebarInset` holding the routed page content, with `SidebarTrigger` somewhere in the header to toggle it. Set `collapsible="icon"` on `Sidebar` when you want it to shrink to an icon rail instead of disappearing entirely on collapse.

Anatomy

`Sidebar` itself renders as an `aside` and accepts `side` (`'left' | 'right'`), `variant` (`'sidebar' | 'floating' | 'inset'`), and `collapsible` (`'offcanvas' | 'icon' | 'none'`). Inside it, `SidebarGroup` clusters related `SidebarMenuItem`s under an optional `SidebarGroupLabel`, and `SidebarMenuButton`/`SidebarMenuAction` are the actual interactive rows and trailing controls within each item.

State model

`SidebarScope` is a pure styling/CSS-boundary wrapper — it renders a plain part with no state of its own. It does **not** coordinate open/closed or rail-expanded state across `Sidebar` and `SidebarTrigger`; there's no built-in mechanism tracking that. You own collapse state yourself (a `state()` cell, driven by clicking `SidebarTrigger`, controlling whatever prop or class toggles the collapsed styling) the same way you would with any other composable primitive here — don't assume wrapping things in `SidebarScope` gets you that behavior for free. Individual `SidebarMenuButton`s and `SidebarMenuAction`s take an `active?: boolean` prop for marking the current route, which is a value you compute from your router, not something the component derives on its own.

Keyboard and accessibility

`SidebarMenuButton` and `SidebarTrigger` accept `tooltip` and `tooltipSide` props, which matter most in the collapsed icon-rail state where the label text is hidden — always set a `tooltip` so icon-only buttons still communicate their purpose to sighted users and, indirectly, screen reader users relying on the accessible name. As with any nav structure, keep `SidebarMenuButton` rendered as real buttons or links rather than divs with click handlers, so keyboard and screen reader navigation work without extra effort.

Styling and tokens

The `variant` prop's three values — `sidebar`, `floating`, `inset` — control fairly different visual treatments (attached, elevated-and-detached, or content-inset), and switching between them is a token-driven style change rather than a structural one. `SidebarMenuButton`'s `size` (`'default' | 'sm' | 'lg'`) and `variant` (`'default' | 'outline'`) follow the same density and variant conventions as `Button`, so a sidebar built from these components looks native next to the rest of the theme rather than like a separate design language.

API

`SidebarProps` omits `as`, `width`, and `shrink` from the general `BlockElementProps<'aside'>` shape and re-adds typed versions of `width` and `shrink`, plus `collapsible`, `side`, and `variant`. `SidebarButtonProps` (used by `SidebarMenuButton` and `SidebarMenuAction`) adds `active`, `asChild`, `size`, `tooltip`, `tooltipSide`, and `variant` on top of native button attributes. The remaining `Sidebar*` parts (`SidebarContent`, `SidebarHeader`, `SidebarFooter`, `SidebarGroup`, etc.) take `SidebarPartProps` — just `as`, `asChild`, and native `div` attributes.

Edge cases

A `collapsible="none"` sidebar ignores `SidebarTrigger` entirely from a visual standpoint, so don't render a trigger for a sidebar that isn't meant to collapse — it'll be misleading UI. Deeply nested `SidebarGroup`s inside `SidebarGroup`s aren't part of the documented shape; keep groups to one level and use `SidebarMenu`/`SidebarMenuItem` for the actual hierarchy of links within a group.

Layout Primitives covers the simpler header-and-main shell this page builds on top of. NavigationMenu and Navbar are worth comparing for top-nav-driven apps that don't need a persistent side panel. Item and ItemGroup are a reasonable alternative to `SidebarMenu` when you need list rows that aren't specifically navigation.