Product
Code card
Code display with language selection, copy, expand and collapse controls, and footer actions.
$ npx context-ds add code-cardUsage
Use it for
Quickstarts and request samples.
Avoid
- Dark code cards in the product
Dependencies: copy-button, select, tooltip, lucide-react
Example
examples/code-card.tsx
"use client";import { useCopy } from "@/components/ui/copy-button";import { Bot, Sparkles } from "lucide-react";import { CodeCard, CodeCardAction } from "@/components/product/code-card";export function Product() { const { copy, hasCopied } = useCopy(); return ( <CodeCard className="w-full max-w-xl" samples={[ { language: "cURL", code: `curl https://api.context.dev/v1/scrape \\\n -H "Authorization: Bearer ctx_live_9f2c41b7e0a84d6c" \\\n -H "Content-Type: application/json" \\\n -d '{\n "url": "https://acme.com",\n "format": "markdown",\n "onlyMainContent": true\n }'`, display: `curl https://api.context.dev/v1/scrape \\\n -H "Authorization: Bearer ctx_live_••••••••" \\\n -H "Content-Type: application/json" \\\n -d '{\n "url": "https://acme.com",\n "format": "markdown",\n "onlyMainContent": true\n }'`, }, { language: "TypeScript (SDK)", code: `import ContextDev from 'context.dev';\n\nconst client = new ContextDev({ apiKey: process.env.CONTEXT_DEV_API_KEY });\nconst page = await client.scrape({ url: 'https://acme.com', format: 'markdown' });\nconsole.log(page.markdown);` }, ]} footer={ <> <span className="hidden @md:inline">Open in</span> <CodeCardAction label="Open in Claude" onClick={() => window.open("https://claude.ai/new", "_blank", "noopener,noreferrer")}> <Sparkles strokeWidth={1.5} /> </CodeCardAction> <CodeCardAction label={hasCopied ? "Copied" : "Copy for your agent"} onClick={() => copy("Use the Context.dev API to scrape https://acme.com and return markdown.")}> <Bot strokeWidth={1.5} /> </CodeCardAction> </> } /> );}export function SingleLanguage() { return <CodeCard className="w-full max-w-xl" defaultExpanded samples={[{ language: "cURL", code: `curl https://api.context.dev/v1/scrape \\\n -H "Authorization: Bearer $CONTEXT_DEV_API_KEY" \\\n -d '{"url": "https://acme.com"}'` }]} />;}Source
components/product/code-card.tsx
"use client";import * as React from "react";import { Check, ChevronDown, Copy } from "lucide-react";import { useCopy } from "@/components/ui/copy-button";import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";import { cn } from "@/lib/utils";// The playground's code card: the call straight on the card in mono with light highlighting (strings brand blue,// keywords and flags navy, numbers a lighter blue), folded to its first five lines until expanded, copy waiting in the// code's corner, and one quiet footer for everything about it: the language chip, the fold, and anything else (an// "Open in" row of agents). Long lines wrap rather than scroll sideways. Not clipped, so the language menu can open// over what's below.const COLLAPSED_LINES = 5;const KEYWORDS = "curl|const|let|await|import|from|return|require|def|func|package|use";const LITERALS = "true|false|True|False|null|None|nil";const TOKEN = new RegExp(`('[^']*'|"[^"]*"|--[a-z-]+|\\b(?:${KEYWORDS})\\b|\\b\\d+\\b|\\b(?:${LITERALS})\\b)`, "g");const KEYWORD = new RegExp(`^(${KEYWORDS})$`);const LITERAL = new RegExp(`^(\\d+|${LITERALS})$`);function tokenClass(token: string) { if (/^['"]/.test(token)) return "text-fg-brand"; if (token.startsWith("--") || KEYWORD.test(token)) return "text-navy-60"; if (LITERAL.test(token)) return "text-blue-70"; return undefined;}/** One language's code. `display` is what the card shows when it differs from what Copy puts on the clipboard (a masked key). */export type CodeSample = { language: string; code: string; display?: string };export function CodeCard({ samples, language: languageProp, onLanguageChange, defaultExpanded = false, onCopy, footer, className,}: { samples: CodeSample[]; /** The shown language, when the page keeps it (so every card on the page follows one pick). */ language?: string; onLanguageChange?: (language: string) => void; /** Whether every line shows at first; otherwise the code starts folded to its first few lines. */ defaultExpanded?: boolean; onCopy?: (sample: CodeSample) => void; /** Sits at the end of the footer, e.g. "Open in" and a row of CodeCardAction buttons. */ footer?: React.ReactNode; className?: string;}) { const [ownLanguage, setOwnLanguage] = React.useState(samples[0]?.language ?? ""); const language = languageProp ?? ownLanguage; const setLanguage = (next: string) => { setOwnLanguage(next); onLanguageChange?.(next); }; const [expanded, setExpanded] = React.useState(defaultExpanded); const { hasCopied, copy } = useCopy(); const sample = samples.find((s) => s.language === language) ?? samples[0]; const lines = (sample?.display ?? sample?.code ?? "").split("\n"); const canExpand = lines.length > COLLAPSED_LINES; const copyLabel = hasCopied ? "Copied" : "Copy code"; return ( <div data-slot="code-card" data-expanded={(expanded && canExpand) || undefined} className={cn("@container flex min-h-0 flex-col rounded-card bg-surface text-fg shadow-card", className)}> <div className="ds-scrollbar relative min-h-0 flex-1 overflow-y-auto"> <pre className={cn("py-4 pr-14 pl-5 font-mono text-caption leading-code-card", !expanded && canExpand && "max-h-[122px] overflow-y-hidden")}> <code> {lines.map((line, i) => ( <span key={i} className="block min-h-[1.65em] whitespace-pre-wrap [overflow-wrap:anywhere]"> {line.split(TOKEN).map((part, j) => ( <span key={j} className={tokenClass(part)}> {part} </span> ))} </span> ))} </code> </pre> {canExpand && !expanded && <div className="pointer-events-none absolute inset-x-0 bottom-0 h-12 bg-linear-to-t from-surface to-transparent" />} <span className="absolute top-2.5 right-2.5 flex items-center"> <Tooltip> <TooltipTrigger asChild> <button type="button" onClick={() => { if (!sample) return; void copy(sample.code); onCopy?.(sample); }} aria-label={copyLabel} className="grid size-8 place-items-center rounded-control text-fg-subtle outline-none transition-[color,background-color,scale] duration-150 ease-out hover:bg-wash-hover hover:text-fg focus-visible:ring-2 focus-visible:ring-focus-ring active:scale-[0.97] motion-reduce:active:scale-100" > {hasCopied ? <Check className="size-3.5 text-success" strokeWidth={1.75} /> : <Copy className="size-3.5" strokeWidth={1.5} />} </button> </TooltipTrigger> <TooltipContent side="top">{copyLabel}</TooltipContent> </Tooltip> </span> </div> <div className="flex h-11 shrink-0 items-center gap-3 border-t border-line-subtle pr-3 pl-2 text-caption whitespace-nowrap text-fg-subtle"> {samples.length > 1 ? ( <Select value={sample?.language} onValueChange={setLanguage}> <SelectTrigger appearance="chip" aria-label="Language"> <SelectValue /> </SelectTrigger> <SelectContent position="popper" sideOffset={16} className="w-auto min-w-40"> {samples.map((s) => ( <SelectItem key={s.language} value={s.language}> {s.language} </SelectItem> ))} </SelectContent> </Select> ) : ( sample && <span className="flex h-7 items-center rounded-sm bg-wash-hover px-2.5 text-fg-muted">{sample.language}</span> )} {canExpand && ( <button type="button" onClick={() => setExpanded((e) => !e)} className="flex items-center gap-1 outline-none transition-colors duration-150 hover:text-fg focus-visible:ring-2 focus-visible:ring-focus-ring"> <ChevronDown className={cn("size-3.5 transition-transform duration-200 ease-out motion-reduce:transition-none", expanded && "rotate-180")} strokeWidth={1.5} /> {expanded ? "Show less" : `Show all ${lines.length} lines`} </button> )} {footer && <span className="ml-auto flex items-center gap-1">{footer}</span>} </div> </div> );}/** A small icon button for the footer (an agent's mark in an "Open in" row), named by its tooltip. */export function CodeCardAction({ label, onClick, className, children }: { label: string; onClick?: () => void; className?: string; children: React.ReactNode }) { return ( <Tooltip> <TooltipTrigger asChild> <button type="button" aria-label={label} onClick={onClick} className={cn("grid size-6 place-items-center rounded-sm text-fg outline-none transition-colors duration-150 hover:bg-wash-hover focus-visible:ring-2 focus-visible:ring-focus-ring [&_svg]:size-3.5", className)} > {children} </button> </TooltipTrigger> <TooltipContent side="top">{label}</TooltipContent> </Tooltip> );}Reference: context-dashboard c0ddf7a src/components/playground/code-card.tsx.