Hexpunk
InstallConceptsElementsProsehp-cellhp-codehp-copyhp-hexhp-latexhp-visually-hiddenhp-buttonhp-checkboxhp-formhp-labelhp-radiohp-selecthp-sliderhp-togglehp-toggle-grouphp-badgehp-taghp-progresshp-spinnerhp-backgroundhp-clusterhp-collapsiblehp-demohp-gridhp-scroll-areahp-separatorhp-sidebarhp-toolbarhp-avatarhp-iconhp-pixelhp-linkhp-menubarhp-navigation-menuhp-tabshp-alert-dialoghp-context-menuhp-dialoghp-dropdown-menuhp-hover-cardhp-popoverhp-tooltiphp-bannerhp-toasthp-tetherhp-module-handlehp-unfold-listhp-unfold-overlayhp-unfold-pagehp-bondhp-link-nodePaletteAnimationsReleases

hp-code

Monospaced code-block primitive with line numbers and per-line hover highlighting. Syntax highlighting is opt-in: hp-code ships no bundled tokeniser. A consumer that wants colours installs the tokeniser of their choice (highlight.js, Shiki, Prism, …) and registers an adapter once at startup via HpCode.setHighlighter(...). This showcase has Shiki registered globally — omit the language attribute (or pass an unsupported one) to see the un-tokenised fallback.

The code blocks on this page are tokenised by Shiki — full docs and the available theme catalogue at shiki.style. Examples API Intent Changelog function hexGrid(rows, cols) { return Array.from({ length: rows }, (_, r) => Array.from({ length: cols }, (_, c) => ({ r, c })), ); } const grid = hexGrid(4, 6); console.log(grid); <body> <hp-code> function hexGrid(rows, cols) { return Array.from({ length: rows }, (_, r) => Array.from({ length: cols }, (_, c) => ({ r, c })), ); } </hp-code> </body> function hexGrid(rows, cols) { return Array.from({ length: rows }, (_, r) => Array.from({ length: cols }, (_, c) => ({ r, c })), ); } const grid = hexGrid(4, 6); console.log(grid); <body> <hp-code language="javascript"> function hexGrid(rows, cols) { return Array.from({ length: rows }, (_, r) => Array.from({ length: cols }, (_, c) => ({ r, c })), ); } </hp-code> </body> .hex { clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%); background: var(--hp-primary); } <body> <hp-code no-line-numbers language="css"> .hex { clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%); background: var(--hp-primary); } </hp-code> </body>

Install

bun add @hexpunk/core lit
import "@hexpunk/core";

Properties

Property Attribute Type Default Description
language language string "" Language hint passed to the tokeniser (e.g. "typescript").
noLineNumbers no-line-numbers boolean false Hide the line-number gutter.

Slots

Slot Description
(default) Raw code text (whitespace preserved after a dedent pass)

CSS Custom Properties

Property Default Description
--hp-code-background Override the block background

CSS Parts

Part Description
pre The internal <pre> element
code The internal <code> element

Adding syntax highlighting

hp-code ships no tokeniser to keep @hexpunk/core dependency-free. Pick a library, install it in your project, and register it once at startup. Every connected <hp-code> re-tokenises automatically when a new highlighter is set.

highlight.js

Smallest of the popular options, batteries-included for ~190 languages, and hp-code's default theme is built around its .hljs-* class names — so you get the hexpunk palette with zero extra CSS.

npm install highlight.js import hljs from "highlight.js/lib/core"; import typescript from "highlight.js/lib/languages/typescript"; import css from "highlight.js/lib/languages/css"; import { HpCode } from "@hexpunk/core"; hljs.registerLanguage("typescript", typescript); hljs.registerLanguage("css", css); HpCode.setHighlighter((code, language) => { if (!language || !hljs.getLanguage(language)) { return null; } return hljs.highlight(code, { language }).value; });

Shiki

Uses TextMate grammars + VS Code themes for the most faithful output. Heavier and async — you'll likely want to override hp-code's default theme rules since Shiki emits inline-styled spans, not class names. Docs at shiki.style.

npm install shiki import { codeToHtml } from "shiki"; import { HpCode } from "@hexpunk/core"; HpCode.setHighlighter(async (code, language) => { if (!language) return null; try { const html = await codeToHtml(code, { lang: language, theme: "night-owl" }); return html .replace(/^<pre[^>]*><code[^>]*>/, "") .replace(/<\/code><\/pre>$/, ""); } catch { return null; } });

Prism

npm install prismjs import Prism from "prismjs"; import "prismjs/components/prism-typescript"; import { HpCode } from "@hexpunk/core"; HpCode.setHighlighter((code, language) => { const grammar = Prism.languages[language]; if (!grammar) return null; return Prism.highlight(code, grammar, language); });

Prism emits .token.keyword-style classes. Add your own theme rules targeting those — hp-code only ships .hljs-* by default.

When to use

Use for any code snippet shown to the reader: docs, examples, blog posts. The line gutter and hover row make scanning long snippets easier. Pass language matching whatever tokeniser is registered. Without a tokeniser or a recognised language, the attribute is just a hint — text still renders as plain monospace. Stuff inline code into <hp-code>. For a one-word inline mention, plain <code> in body text is correct; <hp-code> is a block-level snippet.

Accessibility

Line numbers are wrapped in aria-hidden="true" so they don't pollute screen-reader output. The hover highlight is decorative — line content is the primary signal.

  • 0.1.0-beta 2026-05-24

    Added

    • Showcase: home page redesign — hero (<h1>Hexpunk</h1> + tagline) + Setup section (copy-paste <head> snippet in <hp-code language="html"> with an <hp-copy> button positioned in the top-right corner; the snippet documents the eventual esm.sh CDN surface: two stylesheet links, an importmap, a module script) + Get Started section (5-hex <hp-cluster layout="rosette">: Install centre + Concepts top + Components middle-left + Palette middle-right + GitHub bottom). Drops the old "Spatial primitives" + "Quick links" prose sections.

    Changed

    • Showcase code panels — JSX → plain HTML. All 116 <hp-code slot="code" language="jsx"> panels across 51 pages converted to language="html" showing element markup directly. JSX import "@hexpunk/core"; export default () => (…) wrapper dropped — copy-pasteable HTML matches what Lit-native consumers actually paste, and the convention every comparable Lit design system (Shoelace / Web Awesome / Adobe Spectrum / Carbon Web Components) uses. Edge cases (hp-copy single-arrow shape, hp-latex comment-between-import, hp-code const-string pattern) needed manual rewrites; hp-icon's Lucide-mirror inline-import documentation moved into a new Intent-tab section. Bulk transformation tool added at tools/convert-jsx-panels.ts for any future bulk format changes. Full-doctype standalone-runnable template wrap deferred to a post-publish PR (CDN URLs would 404 today since @hexpunk/core isn't on npm yet).
esc