Display

Code inline

Inline code in running text outside prose, such as a command or an ID, with an optional copy button.

Built on
<code>Clipboard APIrole=status
Client JavaScript
Yes, for state
Works without React
React only (needs JavaScript)
Source, gzipped
2.3 kB
Dependencies
React

Run pnpm add @equip/cli and log in with your org ID org_8f2KQ1⁠

Try its options

Loading its options.

Code
import { CodeInline } from "@/components/equip/code-inline";

export default function Demo() {
  return (
    <p style={{ margin: 0, maxWidth: 208, fontSize: 13, lineHeight: 1.6, color: "var(--eq-ink-2)" }}>
      Run <CodeInline>pnpm add @equip/cli</CodeInline> and log in with your org ID <CodeInline copy>org_8f2KQ1</CodeInline>
    </p>
  );
}
Your versionchange its options in Look, This part

Examples

Copyable values

"Copied" is announced through a polite live region, and the button falls back to execCommand where the clipboard API is missing.

Project ID
prj_4Xw9tLm2⁠
Region
eu-west-1⁠
Commit
9c41e07⁠
Code
import { CodeInline } from "@/components/equip/code-inline";

export default function Example() {
  return (
    <dl style={{ display: "grid", gridTemplateColumns: "auto 1fr", gap: "8px 16px", margin: 0, fontSize: 14 }}>
      <dt style={{ color: "var(--eq-ink-2)" }}>Project ID</dt>
      <dd style={{ margin: 0, minWidth: 0 }}>
        <CodeInline copy>prj_4Xw9tLm2</CodeInline>
      </dd>
      <dt style={{ color: "var(--eq-ink-2)" }}>Region</dt>
      <dd style={{ margin: 0, minWidth: 0 }}>
        <CodeInline copy>eu-west-1</CodeInline>
      </dd>
      <dt style={{ color: "var(--eq-ink-2)" }}>Commit</dt>
      <dd style={{ margin: 0, minWidth: 0 }}>
        <CodeInline copy="9c41e07b2d5fa8836e1f0c4b7d2a915e3f6b08cd" copyLabel="Copy full commit hash">9c41e07</CodeInline>
      </dd>
    </dl>
  );
}

Long tokens

Long paths and hashes break anywhere rather than pushing the column wider than the screen.

The export is saved to s3://acme-reports-eu/exports/2026/q3/revenue_by_region_final.csv and kept for 30 days.

Code
import { CodeInline } from "@/components/equip/code-inline";

export default function Example() {
  return (
    <p style={{ margin: 0, maxWidth: 260, fontSize: 14, lineHeight: 1.6 }}>
      The export is saved to <CodeInline>s3://acme-reports-eu/exports/2026/q3/revenue_by_region_final.csv</CodeInline> and
      kept for 30 days.
    </p>
  );
}

In running text

The em-based padding and size follow whatever text the code sits in, from a hint to a heading.

Configure retry_limit

Set retry_limit to 0 to turn retries off. The default is 3.

Reads RETRY_LIMIT from the environment when it is set.

Code
import { CodeInline } from "@/components/equip/code-inline";

export default function Example() {
  return (
    <div style={{ display: "grid", gap: 12, maxWidth: 420 }}>
      <h3 style={{ margin: 0, fontSize: 18 }}>
        Configure <CodeInline>retry_limit</CodeInline>
      </h3>
      <p style={{ margin: 0, fontSize: 14, lineHeight: 1.6 }}>
        Set <CodeInline>retry_limit</CodeInline> to <CodeInline>0</CodeInline> to turn retries off. The default is{" "}
        <CodeInline>3</CodeInline>.
      </p>
      <p style={{ margin: 0, fontSize: 12, color: "var(--eq-ink-2)" }}>
        Reads <CodeInline>RETRY_LIMIT</CodeInline> from the environment when it is set.
      </p>
    </div>
  );
}

Props

PropTypeDefaultWhat it does
copyboolean | stringfalseAdds a copy button. true copies the text as shown; a string copies that instead, such as a full hash behind a short one.
copyLabelstringnoneNames the copy button. Defaults to Copy and the text when it is a string.
...restComponentProps<"code">noneEvery native code attribute and ref, applied to the code element even in copy mode.

Anatomy

root.eq-code-inlineThe code element, with the same metrics as code inside prose.
group.eq-code-inline-groupCopy mode only. An inline span around the code, the button and the status.
copy.eq-code-inline-copyCopy mode only. 24px, pulled into the line so it does not push it apart.
status.eq-code-inline-srCopy mode only. A visually hidden polite live region.

Keyboard

EnterSpaceCopies the value when the copy button has focus.

Notes

  • Exists because prose only styles code inside .eq-prose; use this for code in a label, a hint, a table cell or a settings row.
  • Kbd is for keys a person presses and code-block is for whole blocks with lines; this is for a short token in a sentence.
  • Long tokens break anywhere rather than overflowing, since hashes and paths have no spaces to wrap at.
  • Copied or Copy failed is announced through a polite live region that stays mounted, and the button's tick draws in.
  • Copy uses the async clipboard and falls back to a temporary textarea and execCommand; if both fail it says so.
  • The file is a client component only for copying; without copy it renders a plain code element.

Source

Two files, and they are the whole part. Open either to read it, copy it straight from its bar, or add the part to your loadout and install it with everything else you picked.

components/equip/code-inline.tsx111 lines
"use client";

import { useEffect, useRef, useState } from "react";
import type { ComponentProps } from "react";
import "./code-inline.css";

export type CodeInlineProps = ComponentProps<"code"> & {
  /** Adds a copy button. `true` copies the text as shown; a string copies that instead. */
  copy?: boolean | string;
  /** Names the copy button. Defaults to "Copy" and the text. */
  copyLabel?: string;
};

const COPIED_MS = 1600;

type CopyState = "idle" | "copied" | "failed";

/** Last resort for browsers without the async clipboard: a throwaway textarea inside the part. */
function legacyCopy(text: string, host: HTMLElement) {
  const active = document.activeElement;
  const area = document.createElement("textarea");
  area.value = text;
  area.setAttribute("readonly", "");
  area.setAttribute("aria-hidden", "true");
  area.style.position = "fixed";
  area.style.inset = "0 auto auto 0";
  area.style.opacity = "0";
  area.style.pointerEvents = "none";
  host.appendChild(area);
  area.select();
  let ok = false;
  try {
    ok = document.execCommand("copy");
  } catch {
    ok = false;
  }
  host.removeChild(area);
  if (active instanceof HTMLElement) active.focus();
  return ok;
}

export function CodeInline({ copy = false, copyLabel, className, children, ...rest }: CodeInlineProps) {
  const rootRef = useRef<HTMLSpanElement>(null);
  const [copied, setCopied] = useState<CopyState>("idle");

  useEffect(() => {
    if (copied === "idle") return;
    const t = window.setTimeout(() => setCopied("idle"), COPIED_MS);
    return () => window.clearTimeout(t);
  }, [copied]);

  const code = (
    <code {...rest} className={["eq-code-inline", className].filter(Boolean).join(" ")}>
      {children}
    </code>
  );

  if (copy === false) return code;

  const shown = typeof children === "string" || typeof children === "number" ? String(children) : undefined;
  const label = copyLabel ?? (shown != null ? `Copy ${shown}` : "Copy");

  async function handleCopy() {
    const text = typeof copy === "string" ? copy : (rootRef.current?.querySelector(".eq-code-inline")?.textContent ?? "");
    let ok = false;
    try {
      if (navigator.clipboard?.writeText) {
        await navigator.clipboard.writeText(text);
        ok = true;
      }
    } catch {
      ok = false;
    }
    if (!ok && rootRef.current) ok = legacyCopy(text, rootRef.current);
    setCopied(ok ? "copied" : "failed");
  }

  return (
    <span ref={rootRef} className="eq-code-inline-group">
      {code}
      {/* A word joiner, so the button never wraps onto a line of its own. */}
      {"⁠"}
      <button
        type="button"
        className="eq-code-inline-copy"
        data-state={copied}
        aria-label={label}
        title={copied === "copied" ? "Copied" : copied === "failed" ? "Copy failed" : "Copy"}
        onClick={handleCopy}
      >
        {copied === "copied" ? (
          <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
            <path d="M3.5 8.5l3 3 6-7" pathLength={1} />
          </svg>
        ) : copied === "failed" ? (
          <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" aria-hidden="true">
            <path d="M8 4.5v4M8 11.25v.25" />
          </svg>
        ) : (
          <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
            <rect x="5.5" y="5.5" width="8" height="8" rx="1.5" />
            <path d="M10.5 3.5v-.5A1.5 1.5 0 0 0 9 1.5H4A1.5 1.5 0 0 0 2.5 3v5A1.5 1.5 0 0 0 4 9.5h.5" />
          </svg>
        )}
      </button>
      <span className="eq-code-inline-sr" role="status">
        {copied === "copied" ? "Copied" : copied === "failed" ? "Copy failed" : ""}
      </span>
    </span>
  );
}
components/equip/code-inline.css100 lines
@layer equip {
  /* The same metrics as code inside .eq-prose, so the two read as one. */
  .eq-code-inline {
    padding: 0.1em 0.3em;
    border-radius: var(--eq-radius-s);
    background: var(--eq-tint);
    color: inherit;
    font-family: var(--eq-font-mono);
    font-variation-settings: var(--eq-font-mono-settings);
    font-size: 0.875em;
    overflow-wrap: anywhere;
  }

  /* Inline, so a long token still breaks across lines with the button after its last piece. */
  .eq-code-inline-group {
    display: inline;
    /* No break between the code and its button; the code itself still wraps. */
    white-space: nowrap;
  }

  .eq-code-inline-group > .eq-code-inline {
    white-space: normal;
  }

  .eq-code-inline-copy {
    display: inline-grid;
    place-items: center;
    width: 24px;
    height: 24px;
    /* Pulled in above and below so the button never pushes the line apart. */
    margin-block: -4px;
    margin-inline: 2px 0;
    padding: 0;
    border: 0;
    border-radius: var(--eq-radius-s);
    background: transparent;
    color: var(--eq-ink-3);
    vertical-align: middle;
    cursor: pointer;
    transition:
      background-color var(--eq-dur) var(--eq-ease),
      color var(--eq-dur) var(--eq-ease),
      scale var(--eq-dur) var(--eq-ease);
  }

  .eq-code-inline-copy > svg {
    width: 14px;
    height: 14px;
  }

  @media (hover: hover) {
    .eq-code-inline-copy:hover {
      background: var(--eq-tint);
      color: var(--eq-ink);
    }
  }

  .eq-code-inline-copy:active {
    scale: var(--eq-press);
  }

  .eq-code-inline-copy:focus-visible {
    outline: 2px solid var(--eq-focus);
    outline-offset: 2px;
  }

  .eq-code-inline-copy[data-state="copied"] {
    color: var(--eq-ok);
  }

  .eq-code-inline-copy[data-state="failed"] {
    color: var(--eq-danger);
  }

  /* Copied: the tick draws itself in. */
  .eq-code-inline-copy[data-state="copied"] path {
    stroke-dasharray: 1;
    stroke-dashoffset: 0;
    transition: stroke-dashoffset var(--eq-dur-move) var(--eq-ease);
  }

  @starting-style {
    .eq-code-inline-copy[data-state="copied"] path {
      stroke-dashoffset: 1;
    }
  }

  /* Read by screen readers, never seen. */
  .eq-code-inline-sr {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }
}

EqUIp is in beta: free for non-commercial use, no warranty, and things can change. Terms

Nothing picked yet. Press A on a part, or use the plus on the board, and the install command builds here.

Loading the index.

Loading the index