Skip to content

Button

Terminal-inspired button with bracket notation and 7 variants.

Preview

Installation

$pnpm exec dgadd add ui/button
[Installs to]src/components/ui/button[Item]ui/button

dgadd is not public on npm yet. Until the first release, pack @diffgazer/add from the repository and install that tarball into this app, which is what puts dgadd on pnpm exec.

UI components require Tailwind CSS v4. Local copy mode imports src/styles/styles.css; package mode uses @diffgazer/ui CSS once packages are available.

Usage

tsx
import { Button } from "@/components/ui/button";export default function ButtonDefault() {  return (    <div className="flex items-center gap-3">      <Button variant="primary">Submit</Button>      <Button variant="ghost">Cancel</Button>      <Button bracket>Submit</Button>    </div>  );}

Examples

Variants

Preview

States

Preview

Highlighted (collection focus)

Preview

Link (as anchor)

Preview

Render-Prop

Preview

API Reference

Button

NameTypeDefaultDescription
variant"primary" | "secondary" | "destructive" | "success" | "ghost" | "outline" | "link""primary"Visual style of the button.
size"sm" | "md" | "lg" | "icon""md"Size token applied to height, padding, and font size.
bracketbooleanfalseWraps the button label in [ ] characters for terminal-style emphasis. Switches to [ ... ] when loading is true.
loadingbooleanfalseShows a Spinner in place of the label and disables click activation.
disabledbooleanfalseDisables interaction; sets aria-disabled and stops onClick.
highlightedbooleanfalseMarks the button as currently highlighted by a parent collection (data-highlighted attribute).
as"button" | "a""button"Render as a native <button> or as an <a> for navigation. The "link" variant is purely visual; combine it with as="a" for a semantic link.
childrenrequiredReactNode | (renderProps: ButtonRenderProps) => ReactNodeButton label, or a render function that receives computed props including the disabled/loading activation guard.

Data attributes

AttributeApplies toValuesDescription
data-highlightedButton in button, anchor, and render-prop modespresent when highlightedMarks the button as highlighted by a parent collection.

Accessibility

Notes

One Filled Voice

Only the primary variant is filled, and it paints the --action / --action-foreground pair. Semantic intents stay outlined — success and destructive carry meaning through colour, priority through fill — so the variant set keeps one filled voice per screen while success and destructive stay recognisable by hue alone.

--action, not --primary

The filled variant paints --action / --action-foreground, the call-to-action pair, and not --primary. They are different roles that happen to coincide in dark: --primary is monochrome emphasis (filled progress, completed steps, checked indicators) and stays mono in both themes; --action is the one place per screen a theme is allowed to spend chroma, so the light palette resolves it to the accent violet while dark keeps it white. A button that reads --primary would lose that distinction in light and turn every filled control into a call to action.

Disabled Primary

Disabled and loading primaries drop the --action fill for a half-strength solid --border edge and a --muted-foreground label rather than fading it. Fading a filled button drags its label toward the page background with it and the pair falls under 4.5:1; emptying the fill leaves the label on the ambient surface, where it keeps its own contrast in both themes, and the retained edge keeps the button's shape readable. No control in the system draws a dashed or dotted edge.

Coarse-Pointer Hit Area

On pointer:coarse the sm, md, and icon sizes extend a transparent ::before to a 44x44 effective target instead of growing: buttons live in fixed-height toolbars and panel headers where a real min-height would reflow the row. lg is already 44px tall and is left alone. Those three sizes therefore carry position:relative so the size itself is the pseudo-element's containing block — do not rely on Button being relatively positioned at lg. Two preconditions belong to the call site: no overflow-hidden ancestor between the button and the scroll root (it clips the overhang and the target silently shrinks back), and a minimum vertical gap to the next interactive row — 16px for sm, 8px for md and icon — otherwise stacked hit areas overlap and taps land on the wrong control. The extension is vertical only, so horizontal neighbours in a button row are safe; icon additionally widens by 4px per side to reach 44px across.

Bracket Mode

The bracket prop wraps the button label in [ ] characters, mimicking terminal UI conventions. When loading is true, bracket mode shows [ ... ] instead of [...].

Polymorphic Element

Use as="a" to render a semantic anchor element for navigation. The link variant is purely visual — combine it with as="a" for semantic anchor navigation.

Render-Prop Composition

Pass a function as children to control the rendered element. Button provides computed props, including an onClick activation guard. Spread them onto the rendered host so disabled and loading states prevent navigation for anchors and framework links.

Source

Install via CLI: pnpm exec dgadd add ui/button.

Highlighted source loads after this disclosure opens. Browse the source repository.