Skip to main content
astrocraft-ui/ components · 101

Code

Free · MIT

Headless, accessible, and styled entirely through the attribute surface below — bring your own CSS system, or start from the reference theme.

Install

Without a flag you get no styling at all — plain HTML carrying the attribute surface below. --theme copies the same theme in two dialects;--bridge lumos re-points either one onto Lumos for Astro's tokens (it needs a theme to re-point, and Lumos itself, already in your project).

Unstyled — behavior only, you write the CSS

Install command
npx astrocraft-ui add display/code

Plain-CSS theme — no build step

Install command
npx astrocraft-ui add display/code --theme css

Tailwind theme — needs Tailwind v4

Install command
npx astrocraft-ui add display/code --theme tailwind

Plain-CSS theme, re-pointed onto Lumos tokens

Install command
npx astrocraft-ui add display/code --theme css --bridge lumos

Tailwind theme, re-pointed onto Lumos tokens

Install command
npx astrocraft-ui add display/code --theme tailwind --bridge lumos

Live demo

Code, CodeBlock, Kbd

The block is focusable so it can be scrolled without a mouse, and the copy button announces its result through a live region outside itself — inside, its text would become part of the button's own name. Press Ctrl + C or use the button; on an insecure origin the clipboard is refused and it says so rather than doing nothing.

Inline, it looks like this: run pnpm headless before you call a change done.

Terminal
pnpm add astrocraft-ui
import "astrocraft-ui/styles/structure.css";

Attribute surface

With no class-merging machinery, these attributes are the whole extension point. State is a data-attribute or a native attribute; visibility is the hiddenattribute. Generated from source by pnpm slots.

ComponentSlotsVariantsRuntime stateNative state
`Code.astro``code`———
`CodeBlock.astro``code-block` `code-block-code``data-wrap`: `true` when set——
`CodeCopyButton.astro``code-copy-button` `code-copy-status`—`data-copied``[hidden]`
`Kbd.astro``kbd``data-depth`: `single` · `chord`——

Source

What the command copies — 5 files, alias-free so the copy lands standing alone. This is the whole component, shown without its comments; the files you install keep them, with the reasoning behind every rule.

src/components/ui/display/code/Code.astro
---
import type { HTMLAttributes } from "astro/types";

type Props = HTMLAttributes<"code">;

const { class: className, ...rest } = Astro.props;
---

<code class={className} data-slot="code" {...rest}><slot /></code>
src/components/ui/display/code/CodeBlock.astro
---
import type { HTMLAttributes } from "astro/types";

type Props = HTMLAttributes<"pre"> & {
  label?: string;
  wrap?: boolean;
};

const { label, wrap = false, class: className, ...rest } = Astro.props;
---

<pre
  tabindex="0"
  role={label ? "region" : undefined}
  aria-label={label}
  class={className}
  data-slot="code-block"
  data-wrap={wrap ? "true" : undefined}
  {...rest}><code data-slot="code-block-code"><slot /></code></pre>
src/components/ui/display/code/CodeCopyButton.astro
---
import "../../../../styles/structure.css";

import type { HTMLAttributes } from "astro/types";

type Props = HTMLAttributes<"button"> & {
  for?: string;
  label?: string;
  copiedText?: string;
  failedText?: string;
};

const {
  for: controls,
  label = "Copy code",
  copiedText = "Copied",
  failedText = "Copy failed",
  class: className,
  ...rest
} = Astro.props;
---

<button
  type="button"
  aria-controls={controls}
  aria-label={Astro.slots.has("default") ? undefined : label}
  class={className}
  data-slot="code-copy-button"
  data-copied-text={copiedText}
  data-failed-text={failedText}
  {...rest}
>
  <slot />
</button>
<span role="status" aria-live="polite" data-slot="code-copy-status"></span>

<script>
  import { onReadyOnce } from "../../_once";

  const FEEDBACK_MS = 2000;

  function blockFor(button: HTMLElement): HTMLElement | null {
    const id = button.getAttribute("aria-controls");
    if (id) return document.getElementById(id);
    for (let el = button.parentElement; el; el = el.parentElement) {
      const found = el.querySelector<HTMLElement>('[data-slot="code-block"]');
      if (found) return found;
    }
    return null;
  }

  function wire(button: HTMLElement) {
    const sibling = button.nextElementSibling;
    const status =
      sibling instanceof HTMLElement && sibling.dataset.slot === "code-copy-status"
        ? sibling
        : null;
    let clear: ReturnType<typeof setTimeout> | undefined;

    const report = (ok: boolean) => {
      button.dataset.copied = String(ok);
      if (status) {
        status.textContent = "";
        status.textContent = (ok ? button.dataset.copiedText : button.dataset.failedText) ?? "";
      }
      clearTimeout(clear);
      clear = setTimeout(() => delete button.dataset.copied, FEEDBACK_MS);
    };

    button.addEventListener("click", async () => {
      const block = blockFor(button);
      if (!block || !navigator.clipboard) return report(false);
      try {
        await navigator.clipboard.writeText(block.textContent ?? "");
        report(true);
      } catch {
        report(false);
      }
    });
  }

  onReadyOnce('[data-slot="code-copy-button"]', wire);
</script>
src/components/ui/display/code/Kbd.astro
---
import type { HTMLAttributes } from "astro/types";

type Props = HTMLAttributes<"kbd"> & { depth?: "single" | "chord" };

const { depth = "single", class: className, ...rest } = Astro.props;
---

<kbd class={className} data-slot="kbd" data-depth={depth} {...rest}><slot /></kbd>
src/components/ui/display/code/index.ts
import Code from "./Code.astro";
import CodeBlock from "./CodeBlock.astro";
import CodeCopyButton from "./CodeCopyButton.astro";
import Kbd from "./Kbd.astro";

export { Code, CodeBlock, CodeCopyButton, Kbd };
export default Code;

What you get

The component source, copied into your project by npx astrocraft-ui add display/code — no package dependency, no CSS to fight. Whatever the paid blocks compose, this is it.