Tabs
{tab=} attribute on adjacent fences or tables makes a tab set; add a group and it becomes linkable, synchronized and remembered.Tabs put equivalent alternatives side by side: package managers, distributions, YAML / TOML / JSON, an environment variable versus a configuration key. Ordered steps and unrelated content do not belong in tabs — the reader sees only one panel at a time.
The native form is a tab attribute on adjacent blocks. Reach for the
tabs/tab shortcode only when the panels hold running text: several
paragraphs, lists, callouts. Both forms share one runtime, one DOM and the same
keyboard behaviour.
Shortest form
Write two fences carrying tab back to back, separated by a blank line only.
The server emits two titled code blocks with no panel hidden; after the page loads, the runtime regroups adjacent blocks of the same kind into a tab set. On GitHub, in print, and with JavaScript off, the reader sees two complete blocks one after the other.
Groups: links, sync and memory
Write group on the first block only and the set gains a public URL hash
#<group>-<value>, in-page synchronization and browser persistence. Every block
in a group must carry value.
value is the machine value (^[a-z0-9][a-z0-9_-]*$), tab is the human
label; the two are independent. The pnpm panel above answers to
#pkgmgr-pnpm, and visiting this page with that hash selects it.
Groups move together
The set below reuses group="pkgmgr". Switch the package manager above and this
one follows; switch it here and the one above follows. The choice is written to
localStorage under the key td-tabs:v1:pkgmgr and still applies to
same-group tabs on other pages.
This set has no yarn panel. When a value is missing, that set simply stays
where it is; a set is never left with nothing selected. The initial selection is
decided in this order: URL hash, stored value, the shortcode’s default or the
first block, the first tab. Opening the page with a hash switches the set
without overwriting a preference the reader already stored.
Tables can be tabs too
The same attributes on a table’s attribute line group adjacent tables into a tab set.
| Parameter | Default |
|---|---|
shared_buffers |
25% RAM |
max_connections |
100 |
| Parameter | Default |
|---|---|
shared_buffers |
128MB |
max_connections |
100 |
Fences and tables are two block kinds and never merge into one set even when adjacent: a tab set is all fences or all tables. To mix them, use the shortcode form below.
A label and a filename together
A fence can carry both tab and title: the label goes in the tab bar, the
filename title bar stays inside the panel.
A lone block is just a titled block
A block needs a neighbour of the same kind to become a tab set. On its own it keeps its title rather than becoming a tab bar with one tab.
Only blank lines may sit between blocks. Three things break a set: running text
in between (a paragraph, a heading or a list all count); an HTML comment in
between, of which <!-- prettier-ignore-end --> is the common one; a later
block writing its own group, since only the first block of a set may carry it.
Tabs around running text
When a panel holds paragraphs, lists, callouts, or several blocks, use the
tabs/tab shortcode. The body is full Markdown.
The repository ships .github/workflows/; a push to main builds and publishes.
baseURL has to be the repository’s Pages address.
Connect the repository in the Cloudflare dashboard; the build command is:
default names the initially selected panel; it must equal a child’s value
and it requires group. Without group, value is forbidden and the theme
generates tab1, tab2 and so on — such a set switches locally and touches
neither the URL nor storage. The shortcode form is stricter than the attribute
form: a mistake is reported at build time instead of in the browser.
Output
| Output | Shape |
|---|---|
| HTML | <div class="td-tabs"> with role="tablist" buttons and panels; every panel is visible until the runtime takes over |
| Consecutive titled static sections, no tab bar | |
| Markdown | The fence form keeps the source fence, {tab=} included; the shortcode form emits **Label** plus the body |
| RSS | Same as print — stacked titled sections |
Only a page that uses tabs loads tabs.js; print, Markdown and RSS never do.
Parameter reference
Attributes on a fence info line or a table attribute line:
The tabs shortcode:
The tab shortcode:
Behavioural contract: in a group the panel ID is <group>-<value>; when the
same group name appears a second time on one page, later sets get a -2,
-3 suffix and the deep-link target stays the first set. Ungrouped sets get
theme-generated IDs. The storage key is td-tabs:v1:<group>. A click or a key
press updates the hash with replaceState and writes storage; arriving with a
hash only switches. Left and right arrows (RTL-aware) plus Home/End move and
activate, and focus stays on the tab.
Limits
- Build failures: in the attribute form,
valuewithoutgroup,grouporvaluewithouttab,tabtogether with the numbering attributenum; in the shortcode form, a duplicatevaluein one set, atabswith notabchild, running text between children, or adefaultthat matches no child. - Grouping mistakes in the attribute form do not stop the build; they leave a
warning in the browser console. A missing
valuedropsgroupfrom the whole set, which degrades to a locally switching tab set with no hash, sync or persistence. A duplicatevalueskips the set entirely and those blocks stay titled blocks. - Fences and tables never merge into one set. To mix prose with code, use the shortcode form.
- Tabs are not a disclosure. To fold away long output use
> [!DETAILS](see Callouts). - A
groupname is shared site-wide: a reader who picks pnpm on page A gets pnpm in the same group on page B. That is the point — and it meansgroupnames should mean something, not betabs1.
Related
- Code blocks — the rest of the fence attributes (title, copy, line numbers, folding)
- Tables — the rest of the table attribute line
- Callouts — for folding rather than juxtaposing
- Steps — tabs inside a procedure