Skip to content

Code snippet tabs

Embed code in several languages behind tabs, with instructions and copy.

Status
Stable
Category
Content
Adoption
Not used yet
import { CodeSnippetTabs } from "@oration/canon/components/code-snippet-tabs";
packages/canon/src/components/code-snippet-tabs.tsx

Add the web-call widget

Suppliers call the Where is my payment agent from the Cedarline supplier portal.

Public keys are safe in page source. They only work on allowed websites.
<!-- Paste before </body> -->
<script src="https://cdn.oration.app/widget.js" async></script>
<oration-widget
  agent-id="agt_where_is_my_payment"
  public-key="pk_live_cdl_7Rw2mKq9XhT4bN8e"
  mode="voice"
  position="bottom-right"
></oration-widget>
import { CodeSnippetTabs } from "@oration/canon/components/code-snippet-tabs";import * as React from "react";export function Hero() {    const key = "pk_live_cdl_7Rw2mKq9XhT4bN8e";    return (        <div className="flex w-full max-w-xl flex-col gap-3">            <div>                <h2 className="text-sm font-semibold text-foreground">                    Add the web-call widget                </h2>                <p className="mt-1 text-13 text-muted-foreground">                    Suppliers call the Where is my payment agent from the                    Cedarline supplier portal.                </p>            </div>            <CodeSnippetTabs                label="Widget embed code"                instructions="Public keys are safe in page source. They only work on allowed websites."                snippets={[                    {                        id: "html",                        label: "HTML",                        language: "html",                        code: `<!-- Paste before </body> --><script src="https://cdn.oration.app/widget.js" async></script><oration-widget  agent-id="agt_where_is_my_payment"  public-key="${key}"  mode="voice"  position="bottom-right"></oration-widget>`,                    },                    {                        id: "react",                        label: "React",                        language: "tsx",                        code: `import { OrationWidget } from "@oration/widget-react";export function SupplierSupportWidget({ vendorId }: { vendorId: string }) {  return (    <OrationWidget      agentId="agt_where_is_my_payment"      publicKey="${key}"      mode="voice"      dynamicVariables={{ vendor_id: vendorId }}    />  );}`,                    },                    {                        id: "ios",                        label: "iOS",                        language: "swift",                        code: `import OrationSDKOration.configure(publicKey: "${key}")Oration.startWebCall(agent: "agt_where_is_my_payment")`,                    },                ]}            />        </div>    );}

Usage#

Code snippet tabs show one example in several languages or platforms, one tab each, with a copy button for the visible snippet and optional setup instructions above. It is how Oration hands over the web-call widget embed (HTML, React, iOS) and API calls (cURL, Node, Python) in Settings and the agent widget config. It looks like Code block with a line tab strip in the title bar. The mistake is using it for one snippet, which adds a tab strip with nothing to switch.

When to use

  • For install or embed code in several platforms: the web-call widget as HTML, React and iOS.
  • For the same API call in several languages: cURL, Node, Python.
  • When setup needs one line of instructions above the code, such as which key to paste.
  • For a multi-step guide where each step's snippet follows one language choice, with a shared controlled value.

When not to use

  • For a single snippet. Use a titled code block. Use Code block
  • For one key or URL on a line. Use Copy row
  • For switching between whole panels of content. Use Tabs
  • For a payload people read rather than copy and run. Use Code block

The Machine Mono Rule

Code and the strings inside instructions (ORATION_API_KEY, YOUR_PUBLIC_KEY) are Geist Mono. Tab labels are language names in Geist Sans: cURL, Node, iOS.

The Scroll Edge Rule

When the tab strip overflows, it scrolls sideways and fades its clipped side over 2rem through useOverflowFade, instead of wrapping.

Anatomy#

Replace the key with a live key.
curl https://api.oration.app/v1/invoices \
  -H "Authorization: Bearer $KEY"
  1. Instructions. Optional 13px Slate Meta line above the block. code inside it renders in Geist Mono and ink.
  2. Tab list. A line tab strip in the 36px title bar, 12px medium labels, scrolling with an edge fade when it overflows.
  3. Active tab. Ink label with a 2px ink underline sitting on the bar's bottom hairline.
  4. Copy button. A 24px ghost Copy button for the visible snippet, named Copy {label} snippet.
  5. Code panel. 12px Geist Mono on Row Mist with 12px padding, scrolling sideways on long lines.

Examples#

An API call

The same request in cURL, Node and Python, with instructions naming the environment variable in InlineCode.

Set ORATION_API_KEY to a key from Settings, API keys. The response lists the 20 newest open invoices.
curl "https://api.oration.app/v1/invoices?status=open&limit=20" \
  -H "Authorization: Bearer $ORATION_API_KEY"
import { InlineCode } from "@oration/canon/components/code-block";import { CodeSnippetTabs } from "@oration/canon/components/code-snippet-tabs";export function Api() {    return (        <CodeSnippetTabs            label="List open invoices"            className="w-full max-w-xl"            instructions={                <span>                    Set <InlineCode>ORATION_API_KEY</InlineCode> to a key from                    Settings, API keys. The response lists the 20 newest open                    invoices.                </span>            }            snippets={[                {                    id: "curl",                    label: "cURL",                    language: "bash",                    code: `curl "https://api.oration.app/v1/invoices?status=open&limit=20" \\  -H "Authorization: Bearer $ORATION_API_KEY"`,                },                {                    id: "node",                    label: "Node",                    language: "javascript",                    code: `const res = await fetch(  "https://api.oration.app/v1/invoices?status=open&limit=20",  { headers: { Authorization: \`Bearer \${process.env.ORATION_API_KEY}\` } },);console.log(await res.json());`,                },                {                    id: "python",                    label: "Python",                    language: "python",                    code: `import os, requestsres = requests.get(    "https://api.oration.app/v1/invoices",    params={"status": "open", "limit": 20},    headers={"Authorization": f"Bearer {os.environ['ORATION_API_KEY']}"},)print(res.json())`,                },            ]}        />    );}

One language across steps

Two blocks share a controlled value, so picking Python in one step switches the other too.

1. Install

npm install @oration/sdk

2. Start a W-9 call

await oration.calls.create({
  agent: "agt_w9_collection",
  to: "+13125550148",
  variables: { vendor_id: "ven_halcyon" },
});
import { CodeSnippetTabs } from "@oration/canon/components/code-snippet-tabs";import { toast } from "@oration/canon/components/toast";import * as React from "react";export function SharedLanguage() {    const [language, setLanguage] = React.useState("node");    const install = [        {            id: "node",            label: "Node",            language: "bash",            code: "npm install @oration/sdk",        },        {            id: "python",            label: "Python",            language: "bash",            code: "pip install oration",        },    ];    const call = [        {            id: "node",            label: "Node",            language: "javascript",            code: `await oration.calls.create({  agent: "agt_w9_collection",  to: "+13125550148",  variables: { vendor_id: "ven_halcyon" },});`,        },        {            id: "python",            label: "Python",            language: "python",            code: `oration.calls.create(    agent="agt_w9_collection",    to="+13125550148",    variables={"vendor_id": "ven_halcyon"},)`,        },    ];    return (        <div className="flex w-full max-w-xl flex-col gap-4">            <div className="flex flex-col gap-2">                <p className="text-13 font-medium text-foreground">                    1. Install                </p>                <CodeSnippetTabs                    label="Install the SDK"                    snippets={install}                    value={language}                    onValueChange={setLanguage}                />            </div>            <div className="flex flex-col gap-2">                <p className="text-13 font-medium text-foreground">                    2. Start a W-9 call                </p>                <CodeSnippetTabs                    label="Start an outbound call"                    snippets={call}                    value={language}                    onValueChange={(id) => {                        setLanguage(id);                        toast.add({                            title:                                id === "python"                                    ? "Showing Python"                                    : "Showing Node",                            description: "Both steps switched language.",                        });                    }}                />            </div>        </div>    );}

With a placeholder to replace

When there's no live key yet, the snippet carries YOUR_PUBLIC_KEY and the instructions tint it Caution Amber.

Replace YOUR_PUBLIC_KEY with an active live key. You don't have one yet.
<script src="https://cdn.oration.app/widget.js" async></script>
<script>
  window.Oration = { key: "YOUR_PUBLIC_KEY", agent: "agt_where_is_my_payment" };
</script>
import { InlineCode } from "@oration/canon/components/code-block";import { CodeSnippetTabs } from "@oration/canon/components/code-snippet-tabs";export function Placeholder() {    return (        <CodeSnippetTabs            label="Install snippets"            className="w-full max-w-xl"            instructions={                <span>                    Replace{" "}                    <InlineCode className="bg-warning/15">                        YOUR_PUBLIC_KEY                    </InlineCode>{" "}                    with an active live key. You don't have one yet.                </span>            }            snippets={[                {                    id: "html",                    label: "HTML",                    language: "html",                    code: `<script src="https://cdn.oration.app/widget.js" async></script><script>  window.Oration = { key: "YOUR_PUBLIC_KEY", agent: "agt_where_is_my_payment" };</script>`,                },            ]}        />    );}

Many languages

The tab strip scrolls sideways and fades its clipped edge over 2rem. Scroll or arrow through it to reach Java.

// Verify the Oration-Signature header in cURL
// with your webhook secret.
import { CodeSnippetTabs } from "@oration/canon/components/code-snippet-tabs";export function Overflow() {    const languages = ["cURL", "Node", "Python", "Ruby", "Go", "PHP", "Java"];    return (        <CodeSnippetTabs            label="Verify a webhook signature"            className="w-full max-w-xs"            snippets={languages.map((name) => ({                id: name.toLowerCase(),                label: name,                language: name.toLowerCase(),                code: `// Verify the Oration-Signature header in ${name}\n// with your webhook secret.`,            }))}        />    );}

States#

States
StateTreatment
Tab restSlate Meta label, no underline.
Tab hoverThe label turns ink over 150ms.
Tab focus visibleInk label and an inset 2px Focus Indigo ring at 50%.
Tab activeInk label and the 2px underline, which fades in over 150ms.
Panel focus visibleThe code panel is focusable and draws an inset 2px ring at 50%.
CopiedThe copy icon swaps to a green check and the button reads Copied for 1.6s.
EmptyWith no snippets the component renders nothing.

Behavior#

  • Uncontrolled by default, starting on defaultValue or the first snippet. Pass value and onValueChange to control it, for example to share one language choice across several blocks.
  • If the stored tab no longer exists in snippets, it falls back to the first snippet.
  • The copy button always copies the visible snippet's code, exactly as written.
  • Built on Base UI Tabs: arrow keys move focus along the strip and wrap at the ends; Enter or Space selects. Inactive panels unmount.
  • language is written to the <code> element as data-language. Nothing highlights it today.
  • Tab switches don't animate beyond the underline's 150ms fade.

Do and don't#

curl https://api.oration.app/v1/me \
  -H "Authorization: Bearer $KEY"
Do. Use tabs when there are at least two languages or platforms to choose between.
curl https://api.oration.app/v1/me \
  -H "Authorization: Bearer $KEY"
Don't. Wrap one snippet in tabs. The strip has nothing to switch and looks like a missing option.

Content#

  • Tab labels are the language or platform's own name: cURL, Node, Python, HTML, React, iOS.
  • Instructions are one sentence of setup, with the thing to replace in code: Set `ORATION_API_KEY` to a key from Settings, API keys.
  • Order tabs by how many people need them, most common first. For the widget that's HTML.
  • Use environment variables or clear placeholders for secrets in every language, never a real key.
  • Give the tab list a label that names the example (Widget embed code), not the default Code examples.

Accessibility#

  • The strip is a Base UI tablist of tabs with aria-selected, and each panel is a tabpanel. Name the list with label.
  • The copy button's name includes the language (Copy Python snippet), so screen reader users know which version they copied.
  • The focus ring on tabs and panels is 2px and inset, drawn inside the title bar, so it isn't clipped by overflow-hidden.
  • The panel takes focus, but the scrolling <pre> inside it doesn't, so arrow keys can't scroll a clipped line. Keep lines short.
  • Instructions are plain text above the block; they aren't linked to the panel. If they matter to the snippet, repeat them in the surrounding copy.
Keyboard interactions
KeysAction
TabMoves to the selected tab, then the copy button, then the panel.
←→Moves focus between tabs, wrapping at the ends.
EnterSpaceSelects the focused tab.

Design tokens#

Design tokens
TokenUsed for
--surfaceRow Mist behind the bar and code
shadow-borderThe block's hairline lift
--borderBar bottom hairline
--foregroundActive label and the 2px underline
--muted-foregroundInactive labels and instructions
--ringInset 2px focus ring at 50%
--radius-lg10px corners

API reference#

CodeSnippetTabs

Tabbed code with a copy button. Also exports the CodeSnippet type. Takes only the props below.

Props of CodeSnippetTabs
PropTypeDefaultDescription
snippetsRequired{ id: string; label: string; language: string; code: string }[]No defaultOne entry per tab, in order.
instructionsReact.ReactNodeNo defaultSetup notes above the block.
valuestringNo defaultThe selected snippet id. Controlled.
defaultValuestringNo defaultThe first selected id. Defaults to the first snippet.
onValueChange(id: string) => voidNo defaultCalled when a tab is selected.
labelstring"Code examples"Accessible name for the tab list.
classNamestringNo defaultMerged onto the outer column, for width.

Known gaps#

Where the implementation and the system disagree today. Follow the system, not the gap.

language is stored as data-language but nothing highlights it, so every language renders as plain mono.

The Channels widget setup passes a single HTML snippet, which renders a one-tab strip.

The Channels widget setup doesn't pass label either, so its tab list is named Code examples.

The web-call widget embed is written three ways across Settings: a <script data-key> tag on the Channels page, a window.Oration config on Public keys and an <oration-widget> element in the agent's widget config.

Like Code block, it draws the hairline lift and has no flat variant for use inside a card.