Code block

Source code with syntax colors, a title, a copy action, line numbers, diff lines and a collapsible height.

<tp-code-block>
Code block
Default density Compact density Restart from the initial state Show the copyable source
import type { Invoice } from './types'; /** Fetches the unpaid invoices for one workspace. */ export async function unpaidInvoices(id: string): Promise<Invoice[]> { const url = `/api/workspaces/${id}/invoices?status=open`; const response = await fetch(url); if (!response.ok) throw new Error(`HTTP ${response.status}`); return response.json(); }

Usage

The source comes from the element’s text content, dedented and trimmed, or from the code property. In markup, escape < and & as &lt; and &amp;, or set code from script. label adds a header with the title and language; the title slot replaces it with your own content. Text content is read when the element connects and whenever it changes.

Custom title

Title slot
Default density Compact density Restart from the initial state Show the copyable source
Deploy to production Requires approval npx tweakpad deploy --env production --confirm

Content in the title slot replaces the label text in the header, for example to add a Badge. Slotted elements are not part of the source, so the copied code stays exactly the command.

Line numbers and highlights

line-numbers adds a gutter that stays put while long lines scroll. highlight-lines takes lines and ranges such as 2,5-7 to draw attention to the part that matters.

Highlighted lines
Default density Compact density Restart from the initial state Show the copyable source
<form action="/signup" method="post"> <tp-field label="Work email"> <tp-input name="email" type="email" required></tp-input> </tp-field> <tp-button type="submit">Create account</tp-button> </form>

Diffs

added-lines and removed-lines mark lines with a gutter marker, a tint and visually hidden text, so the change is clear without relying on color.

Diff
Default density Compact density Restart from the initial state Show the copyable source
export function subtotal(items) { let sum = 0; for (const item of items) sum += item.total; return sum; return items.reduce((sum, item) => sum + item.total, 0); }

Collapsible

collapsible clips long files to collapsed-lines (12 by default) and adds an Expand control. Use it for reference files where the first lines tell most of the story.

Collapsible
Default density Compact density Restart from the initial state Show the copyable source
{ "compilerOptions": { "target": "ES2022", "module": "ESNext", "moduleResolution": "bundler", "lib": ["ES2022", "DOM", "DOM.Iterable"], "strict": true, "noUncheckedIndexedAccess": true, "experimentalDecorators": true, "useDefineForClassFields": false, "declaration": true, "sourceMap": true, "outDir": "dist", "skipLibCheck": true }, "include": ["src"], "exclude": ["**/*.test.ts"] }

Without a header

Without label the copy action floats in the top corner. Set copyable="false" to remove it, and wrap to wrap long lines instead of scrolling.

Shell
Default density Compact density Restart from the initial state Show the copyable source
# Install the library and its peer dependency npm install @tweakpad/ui lit

Languages and theming

The built-in highlighter covers TypeScript, JavaScript, HTML, CSS, JSON, Bash and diffs; other languages render as plain text. Syntax colors are --tp-syntax-* tokens, so setting them on an ancestor restyles every block inside it.

CSS
Default density Compact density Restart from the initial state Show the copyable source
:root { --tp-radius: 0.75rem; --tp-syntax-keyword: light-dark(#6d28d9, #c4b5fd); } @media (width <= 40rem) { .workspace { grid-template-columns: 1fr; } }
Token theme
Default density Compact density Restart from the initial state Show the copyable source
// Verify the signature before trusting the payload. export async function handleWebhook(request: Request) { const signature = request.headers.get('x-signature'); if (!signature) return new Response('Missing signature', { status: 401 }); return processEvent(await request.json()); } @@ -12,3 +12,3 @@ -const KEY_TTL = 60 * 60 * 1000; +const KEY_TTL = 5 * 60 * 1000; export const verifier = createVerifier({ ttl: KEY_TTL });

The wrapper above sets four --tp-syntax-* roles, and both blocks inside pick them up, light and dark alike. language="diff" colors a unified patch as written; use added-lines and removed-lines instead when you want to mark changes in highlighted source.

For full grammar coverage, assign a Shiki-based highlighter from @tweakpad/ui/code to the highlighter property. The block renders plain text first and swaps in colors without shifting layout. The library never imports Shiki; you create the highlighter with the languages and themes you need:

import { createHighlighterCore } from 'shiki/core'; import { createJavaScriptRegexEngine } from 'shiki/engine/javascript'; import { createShikiHighlighter } from '@tweakpad/ui/code'; const shiki = await createHighlighterCore({ engine: createJavaScriptRegexEngine(), themes: [ import('@shikijs/themes/github-light-default'), import('@shikijs/themes/github-dark-default'), ], langs: [import('@shikijs/langs/typescript'), import('@shikijs/langs/html')], }); document.querySelector('tp-code-block').highlighter = createShikiHighlighter(shiki, { themes: { light: 'github-light-default', dark: 'github-dark-default' }, });

Languages Shiki has not loaded fall back to the built-in tokenizer. Tokens with explicit colors (Shiki themes) use their light or dark color for the current scheme. Theme colors are the theme’s own: check their contrast on the code surface (for example, github-light-default comments measure about 4.3:1 on the default surface). The built-in --tp-syntax-* defaults keep every role at 4.5:1 or more on the surface and on highlighted and diff lines in both schemes.

A highlighter implements highlight(code, language, { signal }) and returns CodeTokens (lines of { text, scope?, light?, dark?, fontStyle? } tokens) or a promise of them. A newer request aborts signal.

Accessibility

The block is a figure named by its title, or by the language and the code message. The scroll region becomes focusable (and a labelled region) only when it overflows, so keyboard users can scroll it. Code stays left-to-right in right-to-left pages. Line numbers and diff markers are hidden from assistive technology; added and removed lines carry visually hidden text. The Expand control exposes aria-expanded and controls the viewport. Reduced motion removes the height transition.

API Reference

Every element also accepts motion-policy.

Attributes

AttributePropertyTypeDefaultDescriptioncodecodestring | undefinedundefinedThe source, in place of the element's text content; set it from script to avoid escaping markup.languagelanguagestring'plaintext'The language the highlighter colors; one it does not know renders as plain text.labellabelstring''A title such as the file name, shown in a header with the language and the copy action.line-numberslineNumbersbooleanfalseAdds a gutter of line numbers that stays put while long lines scroll.highlight-lineshighlightLinesstring''Lines to emphasize, as numbers and ranges such as 2,5-7.added-linesaddedLinesstring''Lines marked as added, as numbers and ranges.removed-linesremovedLinesstring''Lines marked as removed, as numbers and ranges.collapsiblecollapsiblebooleanfalseClips a long block to collapsed-lines and adds an Expand control.default-expandeddefaultExpandedbooleanfalseStarts a collapsible block expanded.collapsed-linescollapsedLinesnumber12How many lines a collapsed block shows.copyablecopyablebooleantrueShows the copy action; copyable="false" removes it.wrapwrapbooleanfalseWraps long lines instead of scrolling them sideways.

Properties

PropertyTypeDefaultDescriptionhighlighterCodeHighlighter | nullbuiltinHighlighterProduces the syntax tokens: the built-in tokenizer, an adapter from @tweakpad/ui/code, or null for plain text.messagesCodeBlockMessages{}Replacements for the built-in text, such as the copy action's name; missing entries keep the defaults.onExpandedChange((event: TpValueChangeEvent<boolean>) => void) | undefined—Called with each accepted or vetoed expanded proposal.expandedboolean—Controlled expansion of a collapsible block; undefined makes it uncontrolled.sourceread-onlystring—The source text the block shows and copies.codeMessagesread-onlyRequired<CodeBlockMessages>—The text in use: messages over the defaults.

Events

EventTypeDescriptiontp-code-copyCustomEvent<CodeCopyDetail>Cancelable, before copying; detail.text.tp-value-changeTpValueChangeEvent<boolean>Cancelable expanded proposal (trigger-press, imperative-action).

Methods

MethodReturnsDescriptionsetExpanded(value, reason, event?)booleanProposes expanding or collapsing; resolves to whether it was accepted.copy(sourceEvent?)Promise<boolean>Copies the source text through the composed Copy button (which announces and confirms); without a copy action it writes the clipboard directly.

Slots

SlotDescriptiontitleTitle content in the header, in place of label; it is never part of the copied source.

CSS parts

PartDescription::part(code-block)The figure.::part(header)Title row with the language and the copy action.::part(title)The title in the header.::part(language)The language name in the header.::part(copy)The Copy button.::part(viewport)The scrollable pre.::part(line)One source line; data-highlighted, data-inserted or data-deleted.::part(line-number)The line number gutter cell.::part(line-marker)The added or removed marker of a diff line.::part(token)A token span with data-scope.::part(expand)The row with the Expand control of a collapsible block.

Components, widgets and patterns that use this one.