Tabs
Tabs splits parallel content into panels the reader switches between : the same instructions per platform, the same task in two tools. The strip and every panel are rendered on the server and shelldocs.js does the switching, so tabs work on static hosts with no Blazor runtime.
Basic use
Wrap two or more Tab elements. Label is the text in the strip; the tag body is the panel.
<Tabs>
<Tab Label="Blazor Server">Runs on a .NET host with a live connection per visitor.</Tab>
<Tab Label="Static export">Prerendered HTML served from any static host.</Tab>
<Tab Label="Local dev">Hot reload on markdown, Razor and C# edits.</Tab>
</Tabs>Default tab
The first tab is selected unless DefaultValue names another one, by Label or by Value.
<Tabs DefaultValue="linux">
<Tab Label="Windows" Value="windows">winget install Microsoft.DotNet.SDK.10</Tab>
<Tab Label="macOS" Value="macos">brew install --cask dotnet-sdk</Tab>
<Tab Label="Linux" Value="linux">sudo apt-get install -y dotnet-sdk-10.0</Tab>
</Tabs>Sync groups
Give several Tabs the same SyncKey and picking a tab in one switches all of them. The choice is saved in localStorage, so it also applies on other pages and on the next visit.
<Tabs SyncKey="host">
<Tab Label="GitHub Pages">Source: GitHub Actions.</Tab>
<Tab Label="Cloudflare Pages">Build output directory: publish.</Tab>
</Tabs>
<Tabs SyncKey="host">
<Tab Label="GitHub Pages">Custom domain via a CNAME file in the output.</Tab>
<Tab Label="Cloudflare Pages">Custom domain from the dashboard.</Tab>
</Tabs>Use stable keys (os, host, language). The key names the saved preference, so renaming it resets readers' choice. Sync matches tabs by Value, or Label when there is no Value : give tabs that mean the same thing the same value across groups.
Markdown in panels
Written as an inline tag in the page, each Tab takes a markdown body. Leave a blank line between the tags and any fence.
shelldocs dev --port 5200
Watches .md, .razor and .cs files.
shelldocs build --site-url https://docs.example.com
Writes a static site to ./publish.
For panels that are only code, CodeGroup is the same mechanism with a code-block frame.
Keyboard and accessibility
The strip is a role="tablist". Each button is a role="tab" with aria-selected and aria-controls, and each panel is a role="tabpanel" labelled by its tab. Only the selected tab is in the tab order. With a tab focused:
- Arrow Left / Arrow Right select the previous or next tab, wrapping around.
- Home / End jump to the first or last tab.
- Tab moves on to the content after the strip.
Props
Tabs
| Prop | Type | Default | Description |
|---|---|---|---|
DefaultValue |
string? |
first tab |
Label or Value of the tab selected on first render. |
SyncKey |
string? |
— |
Groups with the same key switch together and remember the choice in localStorage. |
Class |
string? |
— |
Extra classes on the outer element. |
ChildContent |
RenderFragment? |
— |
The Tab children. |
Tab
| Prop | Type | Default | Description |
|---|---|---|---|
LabelRequired |
string |
— |
Text shown in the strip. |
Value |
string? |
Label |
Stable identifier used by DefaultValue and sync. |
ChildContent |
RenderFragment? |
— |
Panel content. |
Styling
The parts carry stable classes: .sd-tabs (outer element, with data-tabs-value set to the selected value), .sd-tabs-list (strip), .sd-tabs-trigger (each button, aria-selected="true" when selected) and .sd-tabs-panel.
Name collisions
If your component library also registers a Tabs, <Tabs> renders yours. This one stays available as <DocsTabs> and <DocsTab>.