Kbd
Keyboard keys with a little depth, alone, in a combo or in a list of shortcuts. Type one for real and its keys sink as you press them; the whole combo down lights its line.
View codeCode to read and copy: file name, line numbers, highlighted lines and a monochrome syntax in weight and light. Copy rolls to Copied; a terminal copies its commands only.
With your AI
Say it to your AI. It gets the exact code below, not its best guess, and knows where it goes.
Show my install snippets in a Kodza Code Block.
claude mcp add -t http kodza https://kodza.dev/api/mcp
<figure class="kodza-code">
<figcaption class="kodza-code__head">
<span class="kodza-code__file">fill-button.js</span>
<button class="kodza-code__copy" type="button"><span class="kodza-code__words"><span>Copy</span><span>Copied</span></span></button>
</figcaption>
<pre class="kodza-code__pre" data-lang="js" data-highlight="4-6"><code>// The ink comes in from where the pointer did
document.querySelectorAll('.kodza-fill-button').forEach((button) => {
button.addEventListener('pointerenter', (event) => {
const box = button.getBoundingClientRect();
const x = event.clientX - box.left;
button.style.setProperty('--x', `${x}px`);
});
});</code></pre>
</figure>
<figure class="kodza-code kodza-code--terminal">
<figcaption class="kodza-code__head">
<span class="kodza-code__file">Terminal</span>
<button class="kodza-code__copy" type="button"><span class="kodza-code__words"><span>Copy</span><span>Copied</span></span></button>
</figcaption>
<pre class="kodza-code__pre" data-lang="shell"><code>$ npx degit kodza/button-fill my-button
> cloned kodza/button-fill#main to my-button
$ open my-button/index.html</code></pre>
</figure>
/* Kodza License: free to use, client work included. Not to resell or redistribute, ported or not: https://kodza.dev/docs/#print-license */
/* Kodza — Code Block
Code to read and copy: a file name, line numbers, lines to look at (data-highlight="4-6"),
and a monochrome highlighting that works with weight and light rather than colors (keywords
bold, comments dimmed, strings bright). Copy rolls to Copied. --terminal shows a shell: the
prompt and the output are left out of what you copy. Languages: js, css, html, shell.
A long file scrolls inside the block past --kz-code-max-height (none by default).
Everything is tuned with the --kz-code-* variables. */
.kodza-code {
--kz-code-bg: #0f0f0f;
--kz-code-fg: rgba(255, 255, 255, 0.84);
--kz-code-bright: #ffffff;
--kz-code-dim: rgba(255, 255, 255, 0.58);
--kz-code-line: rgba(255, 255, 255, 0.09);
--kz-code-mark: rgba(255, 255, 255, 0.06);
--kz-code-max-height: none;
--kz-code-ease: cubic-bezier(0.22, 1, 0.36, 1);
margin: 0;
overflow: hidden;
border-radius: 14px;
background: var(--kz-code-bg);
box-shadow: inset 0 0 0 1px var(--kz-code-line);
color: var(--kz-code-fg);
}
.kodza-code__head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
height: 44px;
padding: 0 8px 0 18px;
border-bottom: 1px solid var(--kz-code-line);
}
/* A long file name keeps to one line and ends with an ellipsis */
.kodza-code__file {
min-width: 0;
overflow: hidden;
color: var(--kz-code-dim);
font-family: "Geist Mono", ui-monospace, "SF Mono", Menlo, monospace;
font-size: 12px;
white-space: nowrap;
text-overflow: ellipsis;
}
/* Copy → Copied: the words roll */
.kodza-code__copy {
display: inline-flex;
align-items: center;
height: 30px;
padding: 0 12px;
border: 0;
border-radius: 8px;
background: none;
color: var(--kz-code-fg);
font: inherit;
font-size: 13px;
font-weight: 500;
cursor: pointer;
transition: background-color 0.2s ease, color 0.2s ease;
}
.kodza-code__copy:hover {
background: var(--kz-code-mark);
color: var(--kz-code-bright);
}
.kodza-code__copy:focus-visible {
outline: 2px solid var(--kz-code-bright);
outline-offset: 1px;
}
.kodza-code__words {
display: inline-grid;
overflow: hidden;
}
.kodza-code__words > span {
grid-area: 1 / 1;
text-align: right;
transition: transform 0.45s var(--kz-code-ease), opacity 0.25s ease;
}
.kodza-code__words > span:nth-child(2),
.kodza-code__copy.is-copied .kodza-code__words > span:first-child {
opacity: 0;
transform: translateY(-110%);
}
.kodza-code__words > span:nth-child(2) {
transform: translateY(110%);
}
.kodza-code__copy.is-copied .kodza-code__words > span:nth-child(2) {
opacity: 1;
transform: none;
}
/* The code */
.kodza-code__pre {
max-height: var(--kz-code-max-height);
margin: 0;
padding: 16px 0;
overflow: auto;
font-family: "Geist Mono", ui-monospace, "SF Mono", Menlo, monospace;
font-size: 13px;
line-height: 1.7;
tab-size: 2;
}
.kodza-code__pre code {
display: block;
min-width: max-content;
font: inherit;
counter-reset: kodza-code-line;
}
.kodza-code__line {
display: block;
padding: 0 24px 0 0;
white-space: pre;
}
.kodza-code__line::before {
content: counter(kodza-code-line);
counter-increment: kodza-code-line;
display: inline-block;
width: 3.4em;
padding-right: 1.4em;
box-sizing: border-box;
color: var(--kz-code-dim);
text-align: right;
opacity: 0.7;
user-select: none;
}
/* Lines to look at: a soft band and a bar in the gutter */
.kodza-code__line.is-marked {
background: var(--kz-code-mark);
box-shadow: inset 2px 0 0 var(--kz-code-bright);
}
.kodza-code__line.is-marked::before {
color: var(--kz-code-bright);
opacity: 1;
}
/* Highlighting in weight and light */
.kodza-code__k { color: var(--kz-code-bright); font-weight: 600; }
.kodza-code__s { color: var(--kz-code-bright); }
.kodza-code__n { color: var(--kz-code-bright); }
.kodza-code__c { color: var(--kz-code-dim); }
.kodza-code__p { color: var(--kz-code-dim); }
.kodza-code__a { color: var(--kz-code-fg); font-weight: 600; }
/* Terminal: a prompt that is not copied, and quieter output */
.kodza-code--terminal .kodza-code__line::before {
content: none;
}
.kodza-code--terminal .kodza-code__line {
padding-left: 20px;
}
.kodza-code__prompt {
color: var(--kz-code-dim);
user-select: none;
}
.kodza-code__out {
color: var(--kz-code-dim);
}
@media (prefers-reduced-motion: reduce) {
.kodza-code__copy,
.kodza-code__words > span {
transition-duration: 0.01ms !important;
}
}
/* Theming example — on a dark page, a step lighter:
.dark-section .kodza-code {
--kz-code-bg: #161616;
--kz-code-line: rgba(255, 255, 255, 0.12);
}
*/
/* kodza-guard 9c5286696e96 — generated by Kodza, do not edit.
Theme guard: keeps a site's own CSS (WordPress themes, Bootstrap, CSS resets…) off this
component. :not(#_) is always true and weighs as much as an id, so these lines win over
theme rules made of classes and tags. Customise the component through its --kz-* variables. */
/* What the browser gives, where the component sets it only in some states: at the lowest weight, so its own rules below put it back */
:where(.kodza-code__copy):not(#_) {
outline: revert;
outline-offset: revert;
}
/* What the browser gives, on everything the component leaves to it */
.kodza-code:not(#_) {
border: revert;
padding: revert;
outline: revert;
text-decoration: revert;
transition: revert;
list-style: revert;
box-sizing: revert;
position: revert;
outline-offset: revert;
appearance: revert;
max-height: revert;
font-family: revert;
font-size: revert;
font-weight: revert;
font-style: revert;
line-height: revert;
letter-spacing: revert;
text-transform: revert;
text-underline-offset: revert;
text-align: revert;
text-indent: revert;
text-shadow: revert;
vertical-align: revert;
}
.kodza-code__head:not(#_) {
margin: revert;
border-radius: revert;
outline: revert;
text-decoration: revert;
transition: revert;
list-style: revert;
box-sizing: revert;
float: revert;
position: revert;
border-top-width: revert;
border-right-width: revert;
border-left-width: revert;
border-top-style: revert;
border-right-style: revert;
border-left-style: revert;
border-top-color: revert;
border-right-color: revert;
border-left-color: revert;
background-color: revert;
background-image: revert;
box-shadow: revert;
outline-offset: revert;
appearance: revert;
max-width: revert;
max-height: revert;
color: revert;
font-family: revert;
font-size: revert;
font-weight: revert;
font-style: revert;
line-height: revert;
letter-spacing: revert;
text-transform: revert;
text-underline-offset: revert;
text-align: revert;
text-indent: revert;
text-shadow: revert;
vertical-align: revert;
}
.kodza-code__file:not(#_),
.kodza-code__words:not(#_),
.kodza-code__line:not(#_),
.kodza-code__c:not(#_),
.kodza-code__p:not(#_),
.kodza-code__s:not(#_),
.kodza-code__k:not(#_),
.kodza-code__prompt:not(#_),
.kodza-code__out:not(#_),
.kodza-code__words span:where(:not([class*="kodza-"])):not(#_),
.kodza-code__line span:where(:not([class*="kodza-"])):not(#_) {
box-sizing: revert;
}
.kodza-code__copy:not(#_) {
margin: revert;
text-decoration: revert;
list-style: revert;
box-sizing: revert;
float: revert;
position: revert;
box-shadow: revert;
appearance: revert;
max-width: revert;
max-height: revert;
letter-spacing: revert;
text-transform: revert;
text-underline-offset: revert;
text-align: revert;
text-indent: revert;
text-shadow: revert;
vertical-align: revert;
white-space: revert;
min-height: revert;
}
.kodza-code__pre:not(#_) {
border: revert;
border-radius: revert;
outline: revert;
text-decoration: revert;
transition: revert;
list-style: revert;
box-sizing: revert;
float: revert;
position: revert;
background-color: revert;
background-image: revert;
box-shadow: revert;
outline-offset: revert;
appearance: revert;
max-width: revert;
color: revert;
font-weight: revert;
font-style: revert;
letter-spacing: revert;
text-transform: revert;
text-underline-offset: revert;
text-align: revert;
text-indent: revert;
text-shadow: revert;
vertical-align: revert;
white-space: revert;
}
.kodza-code__pre code:where(:not([class*="kodza-"])):not(#_) {
border: revert;
margin: revert;
padding: revert;
border-radius: revert;
outline: revert;
text-decoration: revert;
transition: revert;
list-style: revert;
box-sizing: revert;
float: revert;
position: revert;
background-color: revert;
background-image: revert;
box-shadow: revert;
outline-offset: revert;
appearance: revert;
max-width: revert;
max-height: revert;
color: revert;
letter-spacing: revert;
text-transform: revert;
text-underline-offset: revert;
text-align: revert;
text-indent: revert;
text-shadow: revert;
vertical-align: revert;
}
.kodza-code:not(#_):where(:not([hidden])),
.kodza-code__pre:not(#_):where(:not([hidden])) {
display: revert;
}
.kodza-code:not(#_):where([hidden]),
.kodza-code__pre:not(#_):where([hidden]) {
display: none;
}
/* The component's own rules, one step stronger: real buttons and fields, what it sets on tags themes style (links, lists, headings, images…), and the root width (a theme may narrow it, never widen it) */
.kodza-code:not(#_) {
border-radius: 14px;
background: var(--kz-code-bg);
box-shadow: inset 0 0 0 1px var(--kz-code-line);
color: var(--kz-code-fg);
}
.kodza-code__head:not(#_) {
display: flex;
padding: 0 8px 0 18px;
border-bottom: 1px solid var(--kz-code-line);
}
.kodza-code__copy:not(#_) {
display: inline-flex;
align-items: center;
height: 30px;
padding: 0 12px;
border: 0;
border-radius: 8px;
background: none;
color: var(--kz-code-fg);
font: inherit;
font-size: 13px;
font-weight: 500;
cursor: pointer;
transition: background-color 0.2s ease, color 0.2s ease;
}
.kodza-code__copy:hover:not(#_) {
background: var(--kz-code-mark);
color: var(--kz-code-bright);
}
.kodza-code__copy:focus-visible:not(#_) {
outline: 2px solid var(--kz-code-bright);
outline-offset: 1px;
}
.kodza-code__pre:not(#_) {
max-height: var(--kz-code-max-height);
margin: 0;
padding: 16px 0;
font-family: "Geist Mono", ui-monospace, "SF Mono", Menlo, monospace;
font-size: 13px;
line-height: 1.7;
}
.kodza-code__pre code:not(#_) {
display: block;
font: inherit;
}
@media (prefers-reduced-motion: reduce) {
.kodza-code__copy:not(#_) {
transition-duration: 0.01ms !important;
}
}
// Kodza License: free to use, client work included. Not to resell or redistribute, ported or not: https://kodza.dev/docs/#print-license
// Kodza — Code Block
// Highlights the code in weight and light (js, css, html, shell; anything else stays plain),
// splits it into numbered lines, marks the lines of data-highlight, and copies it: the
// whole code, or a terminal's commands only (no prompt, no output). Copy rolls to Copied.
(() => {
const SELECTOR = '.kodza-code';
const RULES = {
js: [
['c', /\/\/[^\n]*|\/\*[\s\S]*?\*\//y],
['s', /'(?:\\.|[^'\\\n])*'|"(?:\\.|[^"\\\n])*"|`(?:\\.|[^`\\])*`/y],
['k', /\b(?:const|let|var|function|return|if|else|for|while|of|in|new|class|extends|import|export|from|default|await|async|try|catch|throw|typeof|instanceof|this|null|undefined|true|false)\b/y],
['n', /\b\d+(?:\.\d+)?\b/y],
['p', /=>|[{}()[\];,.]/y],
],
css: [
['c', /\/\*[\s\S]*?\*\//y],
['s', /'[^'\n]*'|"[^"\n]*"/y],
['k', /@[\w-]+/y],
['a', /[\w-]+(?=\s*:[^:{}]*;)/y],
['n', /-?\d*\.?\d+(?:px|em|rem|%|s|ms|deg|vh|vw|fr)?\b/y],
['p', /[{}();:,]/y],
],
html: [
['c', /<!--[\s\S]*?-->/y],
['k', /<\/?[\w-]+|\/?>/y],
['a', /[\w:-]+(?==)/y],
['s', /"[^"]*"|'[^']*'/y],
],
};
// Tokens: [class or '', text]
const tokenize = (code, lang) => {
const rules = RULES[lang];
if (!rules) return [['', code]];
const out = [];
let i = 0;
let plain = '';
while (i < code.length) {
let hit = null;
for (const [cls, re] of rules) {
re.lastIndex = i;
const m = re.exec(code);
if (m && m[0]) {
hit = [cls, m[0]];
break;
}
}
if (hit) {
if (plain) out.push(['', plain]);
plain = '';
out.push(hit);
i += hit[1].length;
} else {
// Words go by whole, so a keyword is never found inside a name
const word = /[\w$-]+|[\s\S]/y;
word.lastIndex = i;
const chunk = word.exec(code)[0];
plain += chunk;
i += chunk.length;
}
}
if (plain) out.push(['', plain]);
return out;
};
const span = (cls, text) => {
const el = document.createElement('span');
if (cls) el.className = `kodza-code__${cls}`;
el.textContent = text;
return el;
};
// One instance. Everything it starts is tied to `signal`, so kodza:destroy stops it all
function mount(root, signal) {
const pre = root.querySelector('.kodza-code__pre');
const code = pre?.querySelector('code');
const copy = root.querySelector('.kodza-code__copy');
if (!code) return;
const source = code.textContent.replace(/\n$/, '');
const lang = pre.dataset.lang || '';
const marked = new Set();
(pre.dataset.highlight || '').split(',').forEach((part) => {
const [a, b] = part.split('-').map(Number);
for (let n = a; n <= (b || a); n++) if (n) marked.add(n);
});
// Lines: a terminal shows its prompt apart and quiets the output
const lines = [document.createElement('span')];
if (lang === 'shell') {
source.split('\n').forEach((text, i) => {
if (i) lines.push(document.createElement('span'));
const line = lines[lines.length - 1];
if (text.startsWith('$ ')) line.append(span('prompt', '$ '), text.slice(2));
else line.append(span('out', text));
});
} else {
tokenize(source, lang).forEach(([cls, text]) => text.split('\n').forEach((piece, i) => {
if (i) lines.push(document.createElement('span'));
if (piece) lines[lines.length - 1].append(span(cls, piece));
}));
}
lines.forEach((line, i) => {
line.classList.add('kodza-code__line');
if (marked.has(i + 1)) line.classList.add('is-marked');
if (!line.childNodes.length) line.append('');
});
code.replaceChildren(...lines);
// Copy: the code, or a terminal's commands only
const text = lang === 'shell'
? source.split('\n').filter((line) => line.startsWith('$ ')).map((line) => line.slice(2)).join('\n')
: source;
let timer = 0;
copy?.setAttribute('aria-label', 'Copy the code');
copy?.addEventListener('click', async () => {
try {
await navigator.clipboard.writeText(text);
} catch (error) {
const area = Object.assign(document.createElement('textarea'), { value: text });
area.style.cssText = 'position:fixed;opacity:0';
document.body.append(area);
area.select();
document.execCommand('copy');
area.remove();
}
copy.classList.add('is-copied');
copy.setAttribute('aria-label', 'Copied');
clearTimeout(timer);
timer = setTimeout(() => {
copy.classList.remove('is-copied');
copy.setAttribute('aria-label', 'Copy the code');
}, 1800);
}, { signal });
}
// Kodza lifecycle, the same in every component: `kodza:init` starts the instances inside
// event.detail.root (the whole page by default), `kodza:destroy` stops them and puts their
// markup back. For themes and apps that swap content without a page load.
const instances = new WeakMap();
const find = (scope = document) => [scope, ...scope.querySelectorAll(SELECTOR)].filter((el) => el.matches?.(SELECTOR));
const init = (scope) => find(scope).forEach((root) => {
if (root.classList.contains('is-ready')) return; // already running, maybe from another copy of this script
const controller = new AbortController();
instances.set(root, { controller, html: root.innerHTML, attrs: [...root.attributes].map((a) => [a.name, a.value]) });
mount(root, controller.signal);
root.classList.add('is-ready');
});
const destroy = (scope) => find(scope).forEach((root) => {
const instance = instances.get(root);
if (!instance) return;
instances.delete(root);
instance.controller.abort();
[...root.attributes].forEach((a) => root.removeAttribute(a.name));
instance.attrs.forEach(([name, value]) => root.setAttribute(name, value));
root.innerHTML = instance.html;
});
document.addEventListener('kodza:init', (event) => init(event.detail?.root));
document.addEventListener('kodza:destroy', (event) => destroy(event.detail?.root));
init();
// Loaded in <head>, the script runs before the markup exists: once more when it is parsed
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', () => init(), { once: true });
})();
'use client';
// Kodza — Code Block, for React. From https://kodza.dev/components/code-block/
// Kodza License: free to use, client work included. Not to resell or redistribute, ported or not: https://kodza.dev/docs/#print-license
// Next to this file: code-block.css and code-block.js, the component's CSS and JS from Kodza, as they are.
// Yours to edit: the markup is plain JSX below.
// The script changes this markup once React has rendered it (it adds layers, splits text, moves
// nodes), so React must not patch it afterwards. When a prop changes, the key gives React a fresh
// element: kodza:destroy stops the script on the old one, kodza:init starts it on the new one.
import { useRef, useEffect, type ComponentPropsWithoutRef, type ComponentRef, type CSSProperties } from 'react';
import './code-block.css';
type Variant = "terminal";
export type CodeBlockProps = Omit<ComponentPropsWithoutRef<'figure'>, "children" | "variant" | "vars"> & {
/** Modifier class(es): .kodza-code--terminal */
variant?: Variant | Variant[];
/** Settings, as CSS variables on the root */
vars?: {
/** default: #0f0f0f */
"--kz-code-bg"?: string | number;
/** default: rgba(255, 255, 255, 0.84) */
"--kz-code-fg"?: string | number;
/** default: #ffffff */
"--kz-code-bright"?: string | number;
/** default: rgba(255, 255, 255, 0.58) */
"--kz-code-dim"?: string | number;
/** default: rgba(255, 255, 255, 0.09) */
"--kz-code-line"?: string | number;
/** default: rgba(255, 255, 255, 0.06) */
"--kz-code-mark"?: string | number;
/** default: none */
"--kz-code-max-height"?: string | number;
/** default: cubic-bezier(0.22, 1, 0.36, 1) */
"--kz-code-ease"?: string | number;
};
className?: string;
};
export function CodeBlock({ variant, vars, className, style, ...rest }: CodeBlockProps) {
const ref = useRef<ComponentRef<'figure'>>(null);
const classes = ["kodza-code", ...([] as string[]).concat(variant ?? []).map((v) => `kodza-code--${v}`), className].filter(Boolean).join(' ');
// Everything the markup is made of: when it changes, React gets a fresh element (see the top)
const key = JSON.stringify([classes, vars, style, rest]);
// Starts the script on this element once React has rendered it, stops it when it goes
useEffect(() => {
const root = ref.current;
let live = true;
// @ts-ignore: a plain script, no types to find
import('./code-block.js').then(() => {
if (live) document.dispatchEvent(new CustomEvent('kodza:init', { detail: { root } }));
});
return () => {
live = false;
document.dispatchEvent(new CustomEvent('kodza:destroy', { detail: { root } }));
};
}, [key]);
return (
<figure key={key} {...rest} className={classes} style={{ ...vars, ...style } as CSSProperties} ref={ref}>
<figcaption className="kodza-code__head">
<span className="kodza-code__file">fill-button.js</span>
{' '}
<button className="kodza-code__copy" type="button">
<span className="kodza-code__words">
<span>Copy</span>
<span>Copied</span>
</span>
</button>
</figcaption>
<pre className="kodza-code__pre" data-lang="js" data-highlight="4-6">
<code>{"// The ink comes in from where the pointer did\ndocument.querySelectorAll('.kodza-fill-button').forEach((button) => {\n button.addEventListener('pointerenter', (event) => {\n const box = button.getBoundingClientRect();\n const x = event.clientX - box.left;\n button.style.setProperty('--x', `${x}px`);\n });\n});"}</code>
</pre>
</figure>
);
}
Checked
Robots poked at it before you did. Every component goes through the same checks, and again each time it changes.