Skip to main content
Copy for LLM

Code Block

A multi-line code block with monospace rendering, optional filename header, copy-to-clipboard button, and language attribute for future syntax highlighting. For inline code inside running text, use <x-wirekit::code>.

Live Sandbox

This is a hydrated playground for the component. Toggle "Live preview" on the block below to swap the static HTML render for a real Livewire instance — every prop in the component's sandbox schema becomes an editable form field inside the iframe, so you can try different prop combinations live without writing any local code.

Sandbox
<x-wirekit::button>Save</x-wirekit::button>

Basic Usage

Wrap any code snippet in <x-wirekit::code-block>. The body is rendered verbatim inside a <pre><code> pair using the --font-wk-mono font token.

Plain code block
composer require pushery/wirekit
php artisan wirekit:install
The same block in both color modes
Light
Dark

A code block is one of the few places where the dark rendering is not the light one with the colors swapped: the syntax palette is its own set of tokens. Side by side is the only way to read both at once, and the page's own mode does not decide what you see here.

With Filename

Pass filename="…" to render a subtle header bar above the code. Useful for showing file paths or command-line contexts.

Code block with filename
routes/web.php
Route::get('/', function () {
    return view('welcome');
});

With Copy Button

Set :copy="true" to add a copy-to-clipboard button in the header bar. The button uses navigator.clipboard.writeText() and shows a checkmark for two seconds after a successful copy. The button is always accessible via keyboard and exposes aria-label="Copy to clipboard".

Copyable code block
Install
composer require pushery/wirekit

Wrapping long lines

A long line scrolls inside the block by default, and the block takes keyboard focus so the hidden part can be reached. Set wrap to break the lines instead: nothing scrolls then, and the block is no tab stop of its own, which saves a keyboard reader one press per block on a page of examples.

On paper every block breaks its lines, wrap or not, and so does any other <pre> on the page: a sheet cannot scroll, and a line that ran past its edge would not print.

A block that wraps its lines
Terminal
php artisan vendor:publish --provider="Pushery\WireKit\WireKitServiceProvider" --tag=wirekit-assets --force

With Language

Pass language="…" to emit a data-language="..." attribute on the <code> element. The component itself does not ship a syntax highlighter — the attribute is a hook for developer-side highlighters (Prism, Shiki, highlight.js) to pick up.

Code block with language
app/Providers/AppServiceProvider.php
use Pushery\WireKit\WireKit;

public function boot(): void
{
    WireKit::scope('marketing', [
        'button' => ['classes' => ['base' => 'rounded-full']],
    ]);
}

Blade syntax highlighting

highlight.js does not ship a built-in blade language module. Its built-in php-template language covers most Laravel-Blade idioms (@directive, {{ }}, {!! !!}, mixed HTML+PHP) — but it delegates HTML to hljs's xml mode, whose tag-name matcher is [A-Za-z0-9_.-]+ and explicitly does NOT accept :. Under a php-template alias, a <x-wirekit::button> tag tokenizes as <x-wirekit: + bare : + orphan :button> — visibly broken on every WireKit component example.

The fix is a tiny custom grammar that extends hljs's XML-tag rule with :: support. Drop this in the same module where you call hljs.registerLanguage(...). WireKit ships no highlighter, so install the one you want first:

# 1. highlight.js is a developer-side choice — WireKit only emits the
#    data-language="…" hook attribute the highlighter targets.
npm install highlight.js
// 1. Load highlight.js + the languages you use.
import hljs from 'highlight.js/lib/core';
import xml from 'highlight.js/lib/languages/xml';
import php from 'highlight.js/lib/languages/php';

hljs.registerLanguage('xml', xml);
hljs.registerLanguage('php', php);

// 2. Register a custom `blade` grammar that handles `::` in tag names.
//    Covers @directives, {{ }} echoes, {!! !!} raw, and HTML/component
//    tags with the WireKit namespace separator.
hljs.registerLanguage('blade', function (hljs) {
    const BLADE_COMMENT = { className: 'comment', begin: /\{\{--/, end: /--\}\}/ };
    const BLADE_RAW     = { className: 'template-variable', begin: /\{!!/, end: /!!\}/ };
    const BLADE_ECHO    = { className: 'template-variable', begin: /\{\{/,  end: /\}\}/  };
    const BLADE_DIR     = { className: 'keyword',           begin: /@[a-zA-Z]+/ };
    const TAG = {
        className: 'tag',
        begin: /<\/?/, end: /\/?>/,
        contains: [
            // The `::` is the load-bearing change vs. stock xml/php-template —
            // it lets `<x-wirekit::button>` tokenize as one tag name.
            { className: 'name',   begin: /[\w.::-]+/, relevance: 0 },
            { className: 'attr',   begin: /[\w-]+/ },
            { className: 'string', begin: /"/, end: /"/ },
            { className: 'string', begin: /'/, end: /'/ },
        ],
    };
    return {
        name: 'Blade',
        case_insensitive: true,
        contains: [BLADE_COMMENT, BLADE_RAW, BLADE_ECHO, BLADE_DIR, TAG],
    };
});

// 3. Highlight all code blocks on the page.
hljs.highlightAll();

Without that grammar a <code class="language-blade"> block falls through to highlight.js's no-highlight fallback (you'll see a console warning Could not find the language 'blade') — the block still renders as plain monospace text, it just doesn't get colored tokens.

Do not alias blade to php-template:

// Don't do this — looks tempting, breaks WireKit components.
hljs.registerAliases('blade', { languageName: 'php-template' });

php-template covers Blade directives and interpolations but its underlying XML mode treats : as a tag-name boundary. Every x-wirekit::* example then renders with broken tokens — the component prefix splits off and the rest of the line reads as orphan text. The 30-line custom grammar above is the smallest fix that keeps WireKit's :: namespace separator intact.

Props

Prop Type Default Description
language string|null null Emitted as data-language on the <code> tag — hook for developer syntax highlighters.
label string|null null Names the scrollable code region, and the switch that makes it a screen-reader landmark. Without it the block stays keyboard-reachable and simply is not a landmark — see Accessibility.
filename string|null null Text shown in the header bar. When set, the header bar is rendered even without copy.
copy bool false Show a copy-to-clipboard button in the header bar.
wrap bool false Break long lines instead of scrolling them. The block is then no tab stop of its own — see Wrapping long lines.
scope string|null null Named personalization scope for block-level class overrides.

Events

A copy dispatches wirekit:copied on the block once the write has succeeded or failed. The event bubbles, and event.detail.succeeded says which, so a page can count copies without reading anything inside the block. Every copy dispatches it, a second one with the same result as well.

document.addEventListener('wirekit:copied', (event) => {
    // event.target is the block that was copied, or the clipboard button that copied.
    countCopy(event.target, event.detail.succeeded);
});

Accessibility

  • An aria-label or aria-labelledby of yours names the code and makes it a region, as label does
  • The copy button is a real <button> element and reachable via Tab
  • The icon-only button exposes aria-label="Copy to clipboard" and updates to "Copied to clipboard" after a successful copy
  • An aria-live="polite" status region announces "Code copied to clipboard" to screen readers on success (WCAG 2.2 SC 4.1.3)
  • The success state swap is a visual-only change — the button is never removed from the tab order
  • The underlying <pre> block is horizontally scrollable on overflow, with a visible focus outline when reached by keyboard
  • The <code> carries tabindex="0" in every configuration, so a keyboard user can reach text that scrolled out of view (WCAG 2.1.1)

The landmark is opt-in — name the block to get one

A named region is a landmark, and landmarks have to be distinguishable. A name derived from the language would read well on a page with one or two blocks and badly on a page with twenty of the same language: the name meant to tell them apart would make them identical, and an accessibility audit reports that as landmark-unique.

So an unnamed block is not a landmark at all. It keeps tabindex="0" and its focus ring, which is what keyboard reachability actually requires; twenty landmarks with one name would not be a way to find anything either.

Pass label when the block IS worth navigating to, and name it after what it contains rather than after what it is:

{{-- 1. "Change 4471" locates the block; "php code" only repeats what the page already is. --}}
{{-- 2. One name per instance — the rotor lists them all, in order. --}}
@foreach ($changes as $change)
    <x-wirekit::code-block language="php" :label="__('Change :id', ['id' => $change->id])">
        {{ $change->diff }}
    </x-wirekit::code-block>
@endforeach

An empty or whitespace-only label counts as no name — a role="region" with an empty accessible name is not exposed as a landmark anyway, so the block omits the role rather than shipping one nobody can identify. An interpolated value over a record with no title lands here.

Keyboard Interaction

The code region itself is presentational and takes no keyboard input. The copy button is a real control and behaves like one.

Key Action
Tab Move focus to the copy button, when copyable is set
Enter / Space Copy the code to the clipboard

Design Tokens

Token Used for
--font-wk-mono Monospace font family
--color-wk-bg-muted Code block background
--color-wk-border Wrapper + header-bar border
--color-wk-text Code color
--color-wk-text-muted Filename and copy-button color
--color-wk-success Copy-success checkmark color
--radius-wk-md Wrapper border radius
--text-wk-sm Code font size
--text-wk-xs Filename font size
--space-wk-md Code padding
--space-wk-xs Header-bar vertical padding

See Also

  • Code — inline monospace for running text
  • Customization — WireKit::scope() for block-level class overrides

Updated in WireKit v2.66.0 (2026-10-13)

Was this page helpful?

Thank you for your feedback!

Voting requires cookies or local storage. What we store