For the complete documentation index, see llms.txt. Markdown variants are available by appending .md to any URL or sending an Accept: text/markdown header. The asciicn agent skill, for building any ASCII effect on the engine, is at /.well-known/agent-skills/asciicn/SKILL.md.

ASCII button

A real button set in glyphs: a box-drawing frame around a label row that inverts like a terminal selection, with a spinner while loading.

Controls
Variant
Frame
Colour
Size
Loading

Installation

pnpm dlx shadcn@latest add https://asciicn.dev/r/ascii-button.json

Usage

import { AsciiButton } from "@/components/asciicn/ascii-button";
 
export function Actions() {
  return (
    <>
      <AsciiButton color="green">Deploy</AsciiButton>
      <AsciiButton variant="outline" frame="brackets">
        View logs
      </AsciiButton>
    </>
  );
}

AsciiButton renders a real <button> with type="button" unless you pass another type. It is set in the glyph font, two cells tall. Its frame is box-drawing text whose lines run along the button's edges, and the label sits on the row between them.

  • filled shows the label row in inverse video: a slab of the colour with the label knocked out in the page colour.
  • outline shows the frame and a plain label, and inverts the row on hover.
  • ghost is a plain label whose frame and inverse row appear on hover and focus.

On hover and keyboard focus, a cursor glyph (›) steps into the cell before the label, as a terminal menu marks its selection. Press darkens the row.

The label, the gap between children and any icon are whole cells wide (an icon takes two), so the frame always closes on the grid.

Examples

Variants, frames and colours

Each row is one variant across four colours and a mix of frames: rounded, light, heavy, double, ascii and brackets, which draws only the corners.

Loading in a form

loading keeps the label and the width, spins a SPINNERS sequence in the cursor's cell, sets aria-busy and ignores clicks until it clears. Submit this one to see it.

Pass asChild and a single child, such as Next.js Link or a plain <a>, and the child gets the button's frame and states. It stays a link: right click and middle click work, and the router handles navigation.

Props

PropTypeDefault
variant

filled is an inverse-video label row. outline inverts on hover. ghost shows its frame only on hover and focus.

Type: "filled" | "outline" | "ghost"Default "filled"
frame

Box-drawing set for the frame. brackets draws only the four corners.

Type: "rounded" | "light" | "heavy" | "double" | "ascii" | "brackets"Default "rounded"
color

A palette name such as "green", a CSS variable name or any CSS colour.

Type: AsciiColorDefault "ink"
size

Cells of 14, 16 and 20 pixels, so buttons 28, 32 and 40 pixels tall. icon fits one icon; give it an aria-label.

Type: "sm" | "md" | "lg" | "icon"Default "md"
cursor

Glyph shown before the label on hover and focus.

Type: stringDefault "›"
loading

Busy state: a spinner takes the cursor's cell, clicks are ignored and aria-busy is set.

Type: booleanDefault false
spinner

Which SPINNERS sequence spins while loading.

Type: "braille" | "orbit" | "arc" | "bar" | "quadrant" | "tick"Default "braille"
asChild

Render your own single child, such as a link, with the button's look.

Type: booleanDefault false

All other props pass through to the <button>, or to your child with asChild.

Notes

  • Keyboard focus shows a 2 pixel outline outside the frame and the cursor glyph, so keyboard users see the same marker as pointer users.
  • Hover styles only apply on devices that can hover. Press works everywhere, so touch screens get the darker row as feedback.
  • With reduced motion, the row switches without fading, the press does not scale and the spinner holds its first frame.
  • Disabled and loading buttons drop to 45% opacity with a not-allowed or progress cursor. For a disabled link, set aria-disabled on it and leave out href.
  • The frame and row are text and CSS in one shared <style> tag. Only the spinner runs JavaScript.