Code snippets
Text a reader copies exactly: one shiki-highlighted block with copy, expand, line numbers, and collapse, plus a tabbed form. Three sheet types, no size axis.
Code snippets
Text a reader copies exactly: one shiki-highlighted block with copy, expand, line numbers, and collapse, plus a tabbed form. Three sheet types, no size axis.
Demo
The sheet’s three types on one select — horizontal, vertical, vertical with tabs. There is no type prop: each is the prop combination the snippet shows.
The modern toggle starts on each type’s own default
modern defaults to !showLineNumbers, so picking a type seats the toggle on what that type already derives — false for horizontal, true for vertical — and the snippet emits the prop only when you flip it against the grain, which is the off-sheet combination the guidelines warn about. The copied code carries the sample as a template literal, because code is a string and a one-element snippet has nowhere else to put it.Contextual demo
A snippet is read next to what it documents. The preview puts the three types in an API reference and a settings card.
This page is also a consumer
language="tsx" with modern={false} and line numbers, rendered by _components/playground.tsx at line 268. The gray blocks on the Code tab are not: CodeBlock in _components/prose.tsx is a plain pre and code with no highlighting and no copy button, read from that file. One live consumer in this repo, then, rendered on every component page that has a playground.The family
One block, four tabs parts reachable off it, and the highlight function it calls. All six come from one module.
| Component | Description | Import from |
|---|---|---|
| CodeSnippet | The block: highlights a string on the client or renders pre-highlighted nodes, with a copy button, an optional expand button, an optional line-number gutter, and an optional collapse. | @/components/application/code-snippet/code-snippet |
| CodeSnippet.Tabs | The tabbed root. Owns the selection and the outer tray, forces every block inside it into the modern style, and shares a ref so the header’s copy button can find the active panel. | @/components/application/code-snippet/code-snippet |
| CodeSnippet.TabList | The tab bar with the header copy button at its end. The button copies the text of whichever panel is showing. | @/components/application/code-snippet/code-snippet |
| CodeSnippet.Tab | One label. Quaternary at rest, primary when selected or hovered, with a 2px focus ring 2px outside. | @/components/application/code-snippet/code-snippet |
| CodeSnippet.TabPanel | The content for one tab, matched to it by id, with the 8px inset the tray needs around a block. | @/components/application/code-snippet/code-snippet |
| highlight | The async function the block calls itself: shiki’s codeToHast with the kit’s two themes and color replacements, returned as React nodes. Exported from a “use client” module, so a server component gets a client reference it cannot call; the server path the children prop describes needs the same codeToHast call in a server module of your own. | @/components/application/code-snippet/code-snippet |
Built from
Nine pieces make up the block; four have their own docs page.
Component | Where it’s used here |
|---|---|
| ButtonUtility | The action buttons, xs tertiary with a tooltip that doubles as the accessible name: Copy on every block and in the tabs header, Expand when onExpand is given. |
| Tooltip | Reached through ButtonUtility’s tooltip prop, which sets the button’s aria-label to the same string, so the name is there whether or not the tooltip opens. |
| Button | The Show more and Show less button, sm secondary. |
| @untitledui/icons | Copy01 at rest, Check for 2 seconds after a copy, and Maximize01 on the Expand button since 2026-08-23. |
The variant space
The sheet’s three types at its one size, all live: horizontal, vertical, and vertical with tabs.
Line numbers pick the container
isModern is isInsideTabs || (modern ?? !showLineNumbers). The horizontal type is a ring-1 ring-secondary ring-inset block on bg-primary with no shadow; the vertical type is a shadow-lg block on bg-primary_alt with a ring-secondary_alt ring, inside a rounded-[20px] p-2 tray on bg-secondary_alt with shadow-xs. Inside tabs the block skips its own tray and is forced modern, but showLineNumbers still defaults to true there, which is why the install panels above pass it false. Without the gutter the content gets p-5; with it, the gutter’s own 20px above the first line and below the last do the same job.Collapsing and expanding
Two affordances, two triggers: maxHeight collapses the block in place; onExpand adds a button that hands off to you.
What the collapse is made of
max-height from the prop and, while collapsed, overflow-y-hidden; an effect compares its scrollHeight to maxHeight − 10 and nothing collapses under that. Over it, an absolutely positioned 160px overlay paints the fade and holds a Button sm secondary 24px up from the bottom, which reads as 32px from the tray’s outer edge on the vertical type. Pressing it sets max-height: none and turns the overlay into a 68px sticky strip with Show less: the sheet draws no Show less, so that state is the kit’s own. The fade mixes the block’s background token through color-mix over 16 stops since 2026-08-23; before that it was hard-coded white and a gray-950 this theme never uses, so it never blended in dark mode.Languages
Four of the kit’s union. Shiki’s web bundle grammar; github themes with five values swapped for DrumKit tokens.
Five replacements per theme, and what they leave alone
highlight: github’s default text becomes text-primary, constants and support utility-blue-600, keywords and storage utility-pink-600, entities utility-brand-600, and strings text-primary, in both themes. Everything else keeps github’s own value: comments stay #6a737d, tag names stay green, and variables stay orange, which is why the tsx sample above has two colors no DrumKit token names. The sheet is inconsistent on comments itself, teal on one symbol and slate on the other, so that row stays recorded on the Code tab rather than fixed.Client-side, with a server path
highlight in a useLayoutEffect, so the first paint is nodes ?? code ?? <p>Loading...</p>: the raw string in the block’s monospace, without the gutter, which appears when the async pass lands. “Loading...” itself only shows when neither prop is given. To skip the client pass, build the nodes on the server and pass them as children; they are the initial state, and if code is also given the client pass replaces them anyway. Not through highlight itself, though: the module is "use client", so a server component gets a client reference it cannot call, and the server pass needs the same codeToHast call in a server module of your own. The Code tab has the example.States
Copied, hovered, and focused on the buttons; selected, hovered, and focused on the tabs.
Selection is color-only, on the sheet as in the kit
CodeSnippetTab: the label is text-quaternary and turns text-primary when isSelected || isHovered, with no fill and no rule, which is also what the sheet’s six tab symbols draw. The focus ring is outline-2 in outline-focus-ring at outline-offset-2, and focus alone does not recolor an unselected tab. aria-selected carries the state for assistive tech; a sighted keyboard user reads it from color. The buttons’ own states are ButtonUtility’s: a bg-primary_hover fill on hover and the same 2px ring on focus.