The `codeTabs` feature groups related code blocks into an interactive tab strip. You wrap consecutive fenced blocks in a `:::code-group` directive, and zfb emits a `<CodeGroup>` element that the framework renders as tabs.

:::note[Included in the standard preset]

`zudoDoc()` enables Code Tabs. No separate configuration field is necessary.

:::

## Configuration

```ts title="zfb.config.ts"
import { defineConfig } from "zfb/config";
import { zudoDoc } from "@takazudo/zudo-doc/config";

export default defineConfig(
  zudoDoc({
    siteName: "My Docs",
  }),
);
```

## Authoring syntax

Wrap two or more fenced code blocks in a `:::code-group` directive. Each block's tab label is derived from its `title="…"` attribute:

````md
:::code-group

```ts title="index.ts"
export const greeting: string = "hello";
```

```js title="index.js"
export const greeting = "hello";
```

:::
````

:::warning[The `:::code-group` directive is the trigger — not bare consecutive fences]

Two adjacent fenced code blocks on their own do **not** become tabs; they render as two independent code blocks. The tab grouping is triggered only by the explicit `:::code-group` directive wrapping the fences.

:::

## Tab label resolution

Each tab's label follows this priority order:

1. The `title="…"` attribute on the fence
2. The language id (e.g. `ts`, `js`)
3. The fallback string `"tab"` when neither is present

## Live demo

The directive above renders as:

:::code-group

```ts title="index.ts"
export const greeting: string = "hello";
```

```js title="index.js"
export const greeting = "hello";
```

:::

## Emitted output

The `:::code-group` directive produces a `<CodeGroup>` JSX element with a `tabs` prop holding the resolved label array, and one `<pre data-lang="…">` child per fence:

```jsx
<CodeGroup tabs={["index.ts", "index.js"]}>
  <pre data-lang="ts">export const greeting: string = "hello";</pre>
  <pre data-lang="js">export const greeting = "hello";</pre>
</CodeGroup>
```

zudo-doc registers its package-owned `<CodeGroup>` implementation by default, mapping the `tabs` array and `<pre data-lang>` children onto the existing `<Tabs>` / `<TabItem>` UI. You can override it through [`mdxExtras`](https://zudo-doc.takazudomodular.com/docs/guides/custom-components#global-registration-mdxextras). The code inside each `<pre>` is raw text (the Rust pipeline does not run syntax highlighting inside `:::code-group` fences).

## Syncing tabs with `name`

The optional `name` attribute links all `:::code-group` blocks on the page that share the same name. When a user clicks a tab in one group, every other group with the same name switches to the matching tab automatically — if a tab with that label exists.

````md
:::code-group{name="package-manager"}

```sh title="npm"
npm install my-lib
```

```sh title="pnpm"
pnpm add my-lib
```

:::

:::code-group{name="package-manager"}

```sh title="npm"
npm run dev
```

```sh title="pnpm"
pnpm dev
```

:::
````

The chosen tab is also written to `localStorage` under the key `tabs-group-{name}`. On the next page load, every group with that name restores the stored tab (falling back to the default tab if the stored label is not present in the group).

Internally, `name` is forwarded by zfb as a prop to the `<CodeGroup>` component, which passes it as `groupId` to the underlying `<Tabs>` component. The `TabsInit` script reads `data-group-id` from the rendered DOM, syncs all same-`groupId` containers on click, and reads/writes `localStorage` for cross-visit persistence.

## Notes

- Empty `:::code-group` containers (no fences) are left unchanged as plain text.
- Nested `:::code-group` directives are not supported.