Code snippet tabs
Embed code in several languages behind tabs, with instructions and copy.
Add the web-call widget
Suppliers call the Where is my payment agent from the Cedarline supplier portal.
<!-- 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
ORATION_API_KEY, YOUR_PUBLIC_KEY) are Geist Mono. Tab labels are language names in Geist Sans: cURL, Node, iOS.The Scroll Edge Rule
useOverflowFade, instead of wrapping.Anatomy#
curl https://api.oration.app/v1/invoices \
-H "Authorization: Bearer $KEY"- Instructions. Optional 13px Slate Meta line above the block.
codeinside it renders in Geist Mono and ink. - Tab list. A line tab strip in the 36px title bar, 12px medium labels, scrolling with an edge fade when it overflows.
- Active tab. Ink label with a 2px ink underline sitting on the bar's bottom hairline.
- Copy button. A 24px ghost Copy button for the visible snippet, named Copy {label} snippet.
- 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.
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())`, }, ]} /> );}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.
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#
| State | Treatment |
|---|---|
| Tab rest | Slate Meta label, no underline. |
| Tab hover | The label turns ink over 150ms. |
| Tab focus visible | Ink label and an inset 2px Focus Indigo ring at 50%. |
| Tab active | Ink label and the 2px underline, which fades in over 150ms. |
| Panel focus visible | The code panel is focusable and draws an inset 2px ring at 50%. |
| Copied | The copy icon swaps to a green check and the button reads Copied for 1.6s. |
| Empty | With no snippets the component renders nothing. |
Behavior#
- Uncontrolled by default, starting on
defaultValueor the first snippet. PassvalueandonValueChangeto 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.
languageis written to the<code>element asdata-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"curl https://api.oration.app/v1/me \
-H "Authorization: Bearer $KEY"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
labelthat names the example (Widget embed code), not the default Code examples.
Accessibility#
- The strip is a Base UI
tablistoftabs witharia-selected, and each panel is atabpanel. Name the list withlabel. - 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.
| Keys | Action |
|---|---|
| Tab | Moves to the selected tab, then the copy button, then the panel. |
| ←→ | Moves focus between tabs, wrapping at the ends. |
| EnterSpace | Selects the focused tab. |
Design tokens#
| Token | Used for |
|---|---|
--surface | Row Mist behind the bar and code |
shadow-border | The block's hairline lift |
--border | Bar bottom hairline |
--foreground | Active label and the 2px underline |
--muted-foreground | Inactive labels and instructions |
--ring | Inset 2px focus ring at 50% |
--radius-lg | 10px corners |
API reference#
CodeSnippetTabs
Tabbed code with a copy button. Also exports the CodeSnippet type. Takes only the props below.
| Prop | Type | Default | Description |
|---|---|---|---|
snippetsRequired | { id: string; label: string; language: string; code: string }[] | No default | One entry per tab, in order. |
instructions | React.ReactNode | No default | Setup notes above the block. |
value | string | No default | The selected snippet id. Controlled. |
defaultValue | string | No default | The first selected id. Defaults to the first snippet. |
onValueChange | (id: string) => void | No default | Called when a tab is selected. |
label | string | "Code examples" | Accessible name for the tab list. |
className | string | No default | Merged 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.