Appearance
App shell
Three components frame a dashboard: SAppBar is the bar along the top, SSidebar is the navigation column that collapses to a rail and turns into a drawer on narrow screens, and SPage is the content area that sits below the bar and beside the column instead of underneath them.
They are a frame, not a layout engine. Rows, columns, gutters and breakpoint visibility stay with the grid utilities and .s-container; the shell only decides what the page has to reserve. The three are siblings inside the app container — the .s-root--app wrapper described in Isolation:
vue
<template>
<div class="s-root s-root--app">
<SAppBar>
<template #prepend>
<SButton
variant="ghost"
icon-only
icon="menu"
aria-label="Open navigation"
@click="open = true"
/>
</template>
Dashboard
</SAppBar>
<SSidebar
v-model:open="open"
v-model:collapsed="collapsed"
aria-label="Main"
>
…
</SSidebar>
<SPage>…</SPage>
</div>
</template>Order matters: the page reserves space with CSS that reads the bar and the column as preceding siblings. Nothing is measured in JavaScript — the bar publishes --s-app-bar-height, the sidebar publishes --s-sidebar-width and --s-sidebar-collapsed-width, and the page turns them into padding. A custom size is mirrored onto the wrapper element, so the neighbours inherit it.
Landmarks
The bar renders role="banner", the sidebar wraps its navigation in role="navigation" and the page renders role="main", all on plain div elements rather than header/nav/main: host stylesheets target those tags, and library markup stays out of their reach. Assistive technology sees the same regions either way.
A document has one banner and one main, so a second bar or a second page area sets :landmark="false". Give every sidebar an aria-label — a page with more than one navigation region needs the names to tell them apart.
A full shell
The bar sticks to the top and keeps its place in the flow, so the page needs no offset for it. The column is pinned beside the page. Every example below lives in a wrapper with a transform, which makes that wrapper the containing block for the pinned elements — otherwise they would escape into the documentation page.
The collapsed rail
v-model:collapsed narrows the column to collapsedWidth, and the page follows. A navigation built from SList needs nothing else: in the rail a row drops its text and its side padding, so the icon is centred and keeps the whole column as its hit target. For markup of your own, .s-sidebar__label marks the text that goes away, and anything that needs more than a hidden label reads the collapsed slot prop. The column never scrolls sideways, however wide the content is. The toggle's accessible name comes from the locale dictionary (expandSidebar / collapseSidebar); replace it per instance with expandLabel and collapseLabel, or everywhere through i18n.
side="end" puts the column on the trailing edge — the right side in a left-to-right document and the left one in RTL.
The drawer breakpoint
Below breakpoint the column becomes a drawer opened with v-model:open — the same content, a Drawer around it, focus trapped inside. The breakpoint is a name from the grid scale: sm, md (the default), lg or xl. The server renders the column and swaps to the drawer once the media query has been read in the browser, so nothing branches on a measured window width.
The example below asks for breakpoint="xl", so the narrow rendering shows on a wide screen too.
A page with a container
container wraps the content in the .s-container utility — centred, with the readable maximum of the current breakpoint and the container's own side padding. maxWidth caps it further, with or without the container, and :padded="false" hands the padding back to the content.
position="fixed" takes the bar out of the flow; the page then reserves its height, so a scrolling page passes under a bar that stays put. elevate-on-scroll gives that bar a shadow as soon as the window moves.
API
SAppBar
Props
| Name | Type | Default | Description |
|---|---|---|---|
position | | "sticky"
| "static"
| "fixed" | "sticky" | How the bar is anchored: static scrolls away with the page, sticky stays at the top of
the viewport while keeping its place in the flow, fixed leaves the flow entirely (the page
then reserves the height by itself). |
height | string | number | undefined | Bar height: a CSS length or a number of pixels. The value is published as
--s-app-bar-height on the shell, so the page and the sidebar offset by it without measuring
anything. 3.5rem by default. |
bordered | boolean | true | Hairline along the bottom edge. |
elevate-on-scroll | boolean | false | Raises the bar with a shadow once the window is scrolled — the Material behaviour for a bar
that overlaps content. The elevation prop wins when it is set, giving a constant shadow. |
color | string | undefined | Surface color: a name from the palette (primary/teal/teal-10).
Replaces the default surface. |
text-color | string | undefined | Content color on the fill: a name from the palette. White by default (set it
for light color values). |
flat | boolean | undefined | Removes the shadow. Overridden by the elevation prop when it is set. |
elevation | 0 | 3 | 1 | 2 | 4 | 5 | undefined | Shadow level 0–5 (scale); 0 means no shadow. Overrides flat. |
landmark | boolean | true | Marks the bar as the page's banner landmark. Turn it off for a bar that is not the
document's own header — a bar inside a panel, or a second bar on the same page. |
Slots
| Name | Description |
|---|---|
prepend | Leading area: a menu button, a logo, a back arrow. |
default | Centre of the bar: the title, a search field, tabs. |
append | Trailing area: actions, a notifications button, an avatar. |
SSidebar
Props
| Name | Type | Default | Description |
|---|---|---|---|
side | "start" | "end" | "start" | Edge the sidebar sits on, in logical terms: start is the left side in a left-to-right
document and the right one in RTL. |
width | string | number | undefined | Width of the expanded column: a CSS length or a number of pixels. 16rem by default. The
value is published as --s-sidebar-width on the shell, so the page reserves the same width
without measuring anything. |
collapsed-width | string | number | undefined | Width of the collapsed rail: a CSS length or a number of pixels. 4rem by default, published
as --s-sidebar-collapsed-width. |
breakpoint | "md" | "sm" | "lg" | "xl" | "md" | Breakpoint at which the column appears: below it the sidebar is a drawer opened through
v-model:open. A name from the grid scale — sm (600px), md (1024px), lg (1440px) or
xl (1920px). |
bordered | boolean | true | Hairline between the column and the page. |
collapsible | boolean | true | Shows the button that collapses the column to a rail of icons. Turning it off leaves
v-model:collapsed under the application's own control. |
aria-label | string | undefined | Accessible name of the navigation landmark. Set it whenever a page holds more than one navigation region, so screen readers can tell them apart. |
expand-label | string | undefined | Accessible name of the toggle while the rail is collapsed (taken from the locale dictionary by default). |
collapse-label | string | undefined | Accessible name of the toggle while the column is expanded (taken from the locale dictionary by default). |
open | boolean | false | Whether the drawer is open below the breakpoint. Two-way binding via v-model:open. |
collapsed | boolean | false | Whether the wide-screen column is collapsed to a rail of icons. Bound via v-model:collapsed. |
Events
| Name | Signature | Description |
|---|---|---|
update:open | (value: boolean) => void | Emitted when open changes. Used for two-way binding (v-model:open). |
update:collapsed | (value: boolean) => void | Emitted when collapsed changes. Used for two-way binding (v-model:collapsed). |
Slots
| Name | Description |
|---|---|
header | Top of the sidebar: a logo, a workspace switcher. |
default | Navigation content. Receives the rail state so items can hide their labels. |
footer | Bottom of the sidebar: a user card, secondary links. |
toggle | Replaces the collapse button; receives the rail state and the toggle handler. |
SPage
Props
| Name | Type | Default | Description |
|---|---|---|---|
padded | boolean | true | Inner padding around the content. |
container | boolean | false | Wraps the content in the .s-container utility: centred and limited to the readable width of
the current breakpoint. |
max-width | string | number | undefined | Maximum content width: a CSS length or a number of pixels. The content stays centred, with or
without container. |
landmark | boolean | true | Marks the area as the page's main landmark. Turn it off where the page is not the
document's main content — a preview inside a panel, or a second page area. |
Slots
| Name | Description |
|---|---|
default | Page content. |