svelte-code-tabs Svelte Themes

Svelte Code Tabs

svelte-code-tabs

VitePress-style code group tabs and container directives for mdsvex / SvelteKit.

Turn this markdown:

::: code-group

```typescript [TypeScript]
const x: number = 1;
```

```python [Python]
x: int = 1
```

:::

Into tabbed code blocks — click TypeScript or Python to switch.

Install

npm install svelte-code-tabs

Setup

1. Add the preprocessor to svelte.config.js

import { containerPreprocess } from 'svelte-code-tabs';
import { mdsvex } from 'mdsvex';

const config = {
  extensions: ['.svelte', '.md'],
  preprocess: [
    containerPreprocess(),  // must come BEFORE mdsvex
    mdsvex({ extensions: ['.md'] })
  ]
};

2. Import the CSS

// In your root layout or app.css
import 'svelte-code-tabs/style.css';

Or copy the CSS variables into your own stylesheet for customization.

3. Upgrade code groups client-side

<script>
  import { onMount } from 'svelte';
  import { afterNavigate } from '$app/navigation';
  import { browser } from '$app/environment';
  import { upgradeCodeGroups } from 'svelte-code-tabs';

  let contentEl = $state(null);

  function init() {
    if (contentEl) upgradeCodeGroups(contentEl);
  }

  onMount(init);
  if (browser) afterNavigate(init);
</script>

<main bind:this={contentEl}>
  <slot />
</main>

Supported Syntax

Code Groups (tabbed code blocks)

::: code-group

```typescript [TypeScript]
const x = 1;
```

```python [Python]
x = 1
```

:::

The [Label] after the language sets the tab name. Without it, the language name is used.

Callout Blocks

::: tip Optional Title
Content here.
:::

::: warning
This is a warning.
:::

::: info
Informational note.
:::

Customization

CSS Variables

Override these in your stylesheet:

:root {
  --cg-border: #e2e2e3;
  --cg-bg: #fff;
  --cg-bg-soft: #f9f9f9;
  --cg-color-active: #111;
  --cg-color-hover: #333;
  --cg-color-muted: #888;
  --cg-font-mono: ui-monospace, Menlo, Monaco, Consolas, monospace;
}

Preprocessor Options

containerPreprocess({
  labelClass: 'cg-label',    // CSS class for hidden label spans
  calloutClass: 'callout'    // CSS class prefix for callout divs
})

Client-side Options

upgradeCodeGroups(container, {
  wrapperClass: 'cg-wrapper',
  tabBarClass: 'cg-tabs',
  tabClass: 'cg-tab',
  activeClass: 'active',
  labelClass: 'cg-label',
  languageLabels: {
    typescript: 'TypeScript',
    python: 'Python',
    // ... add your own
  }
});

How It Works

  1. Preprocessor (build time): Runs before mdsvex. Strips ::: directive lines, extracts [Label] from code fences into hidden <span> elements, converts callout directives to styled <div>s.

  2. mdsvex (build time): Processes the markdown normally — code fences get syntax highlighting, curly braces get escaped, everything works.

  3. Client-side (runtime): upgradeCodeGroups() finds consecutive <pre> elements, reads their labels from the hidden spans, and wraps them in a tabbed interface.

Why not a remark plugin?

mdsvex strips the [Label] portion from code fence info strings before passing to remark or the highlighter. A Svelte preprocessor runs on the raw markdown string before any parsing, preserving full control.

Why not wrap in Svelte components during preprocessing?

Wrapping code fences in <Component> tags causes mdsvex to treat the code as raw HTML rather than markdown. Curly braces in code examples ({ name: 'x' }) get parsed as Svelte template expressions and break compilation.

License

MIT

Top categories

Loading Svelte Themes