Exclusifbêta
Code Block <mv-code-block>
Enveloppe n’importe quel <pre><code>, brut ou déjà coloré, avec un nom de fichier, une étiquette de langage, un bouton de copie, des numéros de ligne, des lignes surlignées et de diff, le repli et un bouton de retour à la ligne, et copie ce dont le lecteur a vraiment besoin : les invites du shell, la sortie des commandes et les marqueurs de diff sont exclus, et les numéros de ligne comme les marqueurs ne peuvent pas non plus être sélectionnés à la main.
| Catégorie | Affichage de données |
|---|---|
| Type | Web Component (<mv-code-block>) |
| Statut | bêta |
| Installe aussi | copy-button |
| Keywords | exclusive, light, code, snippet, pre, copy, clipboard, syntax, line-numbers, diff, docs, terminal |
When to use
- Docs, a blog post or a changelog show code snippets that readers copy into their editor or terminal
- Install steps mix shell prompts and output, and only the commands should land in the clipboard
- A snippet must point at specific lines or show what changed between two versions
- A long file excerpt should fold to its first lines with a way to show the rest
Avoid when
- Only a single value, key or command needs a copy button, without a code frame → use Copy Button instead
- The code must be editable or runnable in the page; this block only displays it
- A short inline keyboard shortcut or key name sits inside a sentence → use Kbd instead
Installation
node scripts/add.mjs code-block --out ./src/marvelousAgent IA avec le serveur MCP de Marvelous UI : install_components({ slugs: ["code-block"], target_dir: "<absolute path>/src/marvelous", framework: "react" }).
Fichiers copiés (dépendances comprises) : tokens/tokens.css, core/base.css, core/dom.js, core/element.js, core/motion.js, core/observe.js, components/code-block/code-block.js, components/code-block/code-block.css, components/copy-button/copy-button.js, components/copy-button/copy-button.css.
Utilisation
Démarrage rapide, le balisage minimal qui fonctionne :
<mv-code-block filename="install.sh"><pre><code class="language-bash">$ npm install @acme/ui</code></pre></mv-code-block>Balisage de référence : partez de celui-ci et personnalisez-le avec les attributs, data-* et les variables CSS :
<div id="mv-code-block-demo" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,340px),1fr));gap:1rem;width:100%;max-width:1040px;margin-inline:auto;align-items:start">
<mv-code-block filename="src/lib/format-price.ts" line-numbers highlight="6-7" collapse="10">
<pre><code class="language-ts"><span class="tk-c">/**
* Format a price in the shopper's own locale.
* Amounts are stored in cents to avoid rounding drift.
*/</span>
<span class="tk-k">export function</span> <span class="tk-f">formatPrice</span>(cents: <span class="tk-k">number</span>, currency = <span class="tk-s">"USD"</span>, locale = <span class="tk-s">"en-US"</span>): <span class="tk-k">string</span> {
<span class="tk-k">const</span> amount = cents / <span class="tk-n">100</span>;
<span class="tk-k">return new</span> Intl.<span class="tk-f">NumberFormat</span>(locale, { style: <span class="tk-s">"currency"</span>, currency }).<span class="tk-f">format</span>(amount);
}
<span class="tk-k">export const</span> examples = [
<span class="tk-f">formatPrice</span>(<span class="tk-n">129900</span>), <span class="tk-c">// "$1,299.00"</span>
<span class="tk-f">formatPrice</span>(<span class="tk-n">4550</span>, <span class="tk-s">"EUR"</span>, <span class="tk-s">"de-DE"</span>), <span class="tk-c">// "45,50 €"</span>
<span class="tk-f">formatPrice</span>(<span class="tk-n">2400</span>, <span class="tk-s">"GBP"</span>, <span class="tk-s">"en-GB"</span>), <span class="tk-c">// "£24.00"</span>
<span class="tk-f">formatPrice</span>(<span class="tk-n">149900</span>, <span class="tk-s">"INR"</span>, <span class="tk-s">"hi-IN"</span>), <span class="tk-c">// "₹1,499.00"</span>
];
</code></pre>
</mv-code-block>
<div style="display:grid;gap:1rem">
<mv-code-block>
<pre><code class="language-bash">$ npm install @acme/ui
added 42 packages in 3s
$ npx acme init --template dashboard \
--currency USD
✔ Created acme.config.json</code></pre>
</mv-code-block>
<mv-code-block filename="checkout.js" language="diff-js">
<pre><code>const total = items.reduce((sum, item) => sum + item.price, 0);
-const label = "$" + total.toFixed(2);
+const label = new Intl.NumberFormat("en-US", {
+ style: "currency",
+ currency: "USD",
+}).format(total);</code></pre>
</mv-code-block>
<mv-code-block>
<pre><code>npm install @acme/ui</code></pre>
</mv-code-block>
</div>
</div>
<style>
/* Stand-in for your own highlighter's theme: mv-code-block keeps its markup as is. */
#mv-code-block-demo .tk-c { color: var(--mv-fg-subtle); font-style: italic; }
#mv-code-block-demo .tk-k { color: var(--mv-accent); }
#mv-code-block-demo .tk-f { color: var(--mv-info); }
#mv-code-block-demo .tk-s { color: var(--mv-success); }
#mv-code-block-demo .tk-n { color: var(--mv-warning); }
</style>API
Attributes
| Name | Type | Default | Description |
|---|---|---|---|
filename | string | File name shown in the header (and used to name the copy button and the scroll region). Shown before upgrade too, with no layout shift. | |
language | string | Language label (e.g. ts, bash). Defaults to the language-* / lang-* class of the <code> or <pre>, as written by Markdown renderers and highlighters. Shell languages (bash, sh, zsh, console, powershell…) enable prompt handling; diff (a patch) or diff-<lang> (code with + / - line markers) enable diff lines. Not named lang on purpose: lang is the human-language attribute screen readers rely on. | |
highlight | string | Lines to highlight, e.g. "2,5-7" (numbers as displayed, so they follow line-numbers). | |
collapse | number | Folds the snippet to its first N lines with a "Show all N lines" button. Never folds when that would hide fewer than 3 lines. | |
copy | auto | clean | raw | none | auto | What the copy button copies. clean: drops shell prompts, command output (when prompts are present), removed diff lines and diff markers. raw: the exact source text. none: no copy button. auto: clean for shell languages and diff-<lang>, raw otherwise (a diff patch is copied as a patch). |
line-numbers | boolean | number | Shows line numbers; a value sets the first number (e.g. line-numbers="42" for an excerpt). | |
data-wrap | boolean | Wrapped lines. Set by the wrap toggle; can be set initially. The toggle only appears when a line overflows (or while wrapped). | |
data-folded / data-numbers / data-bare / data-ready | boolean | Set by the component (for styling). data-bare: no file name and no language, so the copy button floats in the corner. |
Properties
| Name | Type | Description |
|---|---|---|
text | string | The text the copy button copies (read-only). |
wrap | boolean | Line wrapping on or off. |
expanded | boolean | Whether a folded snippet is currently unfolded. |
strings | object | Translatable texts: { copy: "Copy", copied: "Copied!", copyFailed: "Copy failed", wrap: "Wrap lines", showAll: "Show all {count} lines", showLess: "Show fewer lines", code: "Code" }. showAll may also be an object of plural forms ({ one, few, many, other }), picked with Intl.PluralRules; {count} is formatted with Intl.NumberFormat in the page's lang. |
Methods
| Name | Description |
|---|---|
toggle(force?) | Folds or unfolds the snippet (no argument: toggles). |
Events
| Name | Description |
|---|---|
mv-toggle | The snippet was folded or unfolded; detail = { expanded }. |
mv-copy | Bubbles from the inner copy button; detail = { text } (the cleaned text). |
mv-copy-error | Bubbles from the inner copy button when the clipboard refuses; detail = { error }. |
Content structure
| Name | Description |
|---|---|
pre | A child <pre>, with or without a <code> inside, plain or highlighted by any highlighter (build time or runtime). Its markup is kept: tokens that span several lines are split per line, classes intact. If a highlighter rewrites the code later, the lines are rebuilt. |
CSS classes
| Name | Description |
|---|---|
mv-code-block-bar / -title / -name / -lang / -actions / -wrap / -copy | Generated header parts. |
mv-code-block-line | One span per line; data-highlight, data-kind="add | del | meta | output" set as needed. |
mv-code-block-mark | Diff marker or shell prompt: kept in the text, but aria-hidden and not selectable. |
mv-code-block-fold / -fold-button | Fold footer and its button. |
CSS variables
| Name | Default | Description |
|---|---|---|
--mv-code-block-bg | var(--mv-bg-subtle) | Block background. |
--mv-code-block-bar-bg | mix of --mv-bg-muted and the block background | Header background. |
--mv-code-block-highlight | var(--mv-accent) | Highlighted line bar (tint at 11%). |
--mv-code-block-add | var(--mv-success) | Added lines. |
--mv-code-block-del | var(--mv-danger) | Removed lines. |
--mv-code-block-padding | var(--mv-space-4) | Inline padding of lines and header. |
--mv-code-block-radius | var(--mv-radius-lg) | Corner radius. |
--mv-code-block-font-size | 0.8125rem | Code size. |
Accessibility
Without JavaScript the block is a readable, styled <pre> (file name included). Line numbers are CSS generated content with an empty alternative (content: counter() / ""), so they are never selected, copied or read aloud. Prompts and diff markers stay in the text for highlighters but sit in aria-hidden, user-select: none spans; added and removed lines carry role="insertion" / "deletion" and highlighted lines role="mark", so the change is announced in the reader's own language, not by color alone (the + / - glyphs and an inline-start bar are also shown). The copy button is a real <button> described by the file name, and its result is announced once by a polite status. The wrap toggle is an aria-pressed button shown only when a line overflows; the scroller becomes focusable (a named group) only when it actually scrolls, so keyboard users can scroll it. The fold button has aria-expanded and aria-controls; folded lines use hidden="until-found", so find-in-page still reaches them and unfolds the snippet, and collapsing keeps the button in view. The language is never put in the lang attribute. Print shows every line, wrapped. Forced colors: system borders and Highlight bars. No motion beyond a chevron turn.