agentic-ui logoagentic-ui

Button

Pressable button with multiple variants and sizes. Composes @base-ui/react Button with focus management and disabled-while-loading.

Variants

Controls the visual shape — filled, tinted, outlined, or borderless.

import { Button } from "@brijbyte/agentic-ui/button";
import "@brijbyte/agentic-ui/button.css";

export default function VariantsDemo() {
  return (
    <>
      <Button variant="solid">Solid</Button>
      <Button variant="soft">Soft</Button>
      <Button variant="outline">Outline</Button>
      <Button variant="ghost">Ghost</Button>
    </>
  );
}

Tones

Controls the semantic color. Combine any tone with any variant.

import { Button } from "@brijbyte/agentic-ui/button";
import "@brijbyte/agentic-ui/button.css";

const TONES = ["primary", "secondary", "destructive", "success", "warning"] as const;

export default function TonesDemo() {
  return (
    <>
      {TONES.map((tone) => (
        <Button key={tone} variant="solid" tone={tone}>
          {tone.charAt(0).toUpperCase() + tone.slice(1)}
        </Button>
      ))}
    </>
  );
}

Variants × Tones

Every combination of variant and tone.

Primary
Secondary
Destructive
Success
Warning
Solid
Soft
Outline
Ghost
import { Fragment } from "react";
import { Button } from "@brijbyte/agentic-ui/button";
import type { ButtonVariant, ButtonTone } from "@brijbyte/agentic-ui/button";
import "@brijbyte/agentic-ui/button.css";

const VARIANTS: ButtonVariant[] = ["solid", "soft", "outline", "ghost"];
const TONES: ButtonTone[] = ["primary", "secondary", "destructive", "success", "warning"];

const label = (s: string) => s.charAt(0).toUpperCase() + s.slice(1);

const cellStyle: React.CSSProperties = {
  display: "flex",
  alignItems: "center",
  justifyContent: "center",
};

const headStyle: React.CSSProperties = {
  fontFamily: "var(--font-mono)",
  fontSize: "var(--font-size-xs)",
  color: "var(--color-tertiary)",
  textAlign: "center",
};

export default function MatrixDemo() {
  return (
    <div
      style={{
        display: "grid",
        gridTemplateColumns: `80px repeat(${TONES.length}, 1fr)`,
        gap: "var(--space-2)",
        alignItems: "center",
        width: "100%",
      }}
    >
      {/* Header row */}
      <div />
      {TONES.map((tone) => (
        <div key={tone} style={headStyle}>
          {label(tone)}
        </div>
      ))}

      {/* Variant rows */}
      {VARIANTS.map((variant) => (
        <Fragment key={variant}>
          <div style={{ ...headStyle, textAlign: "left" }}>{label(variant)}</div>
          {TONES.map((tone) => (
            <div key={tone} style={cellStyle}>
              <Button variant={variant} tone={tone} size="sm">
                {label(tone)}
              </Button>
            </div>
          ))}
        </Fragment>
      ))}
    </div>
  );
}

Sizes

import { Button } from "@brijbyte/agentic-ui/button";
import "@brijbyte/agentic-ui/button.css";

export default function SizesDemo() {
  return (
    <>
      <Button size="xs">Extra Small</Button>
      <Button size="sm">Small</Button>
      <Button size="md">Medium</Button>
      <Button size="lg">Large</Button>
    </>
  );
}

States

import { useState } from "react";
import { Button } from "@brijbyte/agentic-ui/button";
import "@brijbyte/agentic-ui/button.css";

export default function StatesDemo() {
  const [loading, setLoading] = useState(false);

  const trigger = () => {
    setLoading(true);
    setTimeout(() => setLoading(false), 2000);
  };

  return (
    <>
      <Button disabled>Disabled</Button>
      <Button variant="outline" disabled>
        Disabled outline
      </Button>
      <Button loading={loading} onClick={trigger}>
        Deploy
      </Button>
      <Button loading={loading} loadingText="Deploying…" onClick={trigger}>
        Deploy
      </Button>
    </>
  );
}

Using base-ui render prop

import { Button } from "@brijbyte/agentic-ui/button";
import "@brijbyte/agentic-ui/button.css";

export default function RenderPropDemo() {
  return (
    // Render as <a> while keeping button behaviour and focus management.
    // eslint-disable-next-line jsx-a11y/anchor-has-content
    <Button variant="soft" nativeButton={false} render={<a href="/components/button" />}>
      Anchor button
    </Button>
  );
}

API Reference

Pressable button with multiple variants, tones, and sizes. Composes
@base-ui/react Button with focus management and disabled-while-loading.
Supports a spinner overlay that preserves button width.

PropTypeDefault
variant"solid" | "soft" | "outline" | "ghost"solid

Visual shape — filled, tinted, outlined, or borderless.

tone"primary" | "secondary" | "destructive" | "success" | "warning"primary

Semantic colour.

size"xs" | "sm" | "md" | "lg"md

Controls padding and font size.

loadingbooleanfalse

Shows a spinner overlay and disables interaction.

loadingTextstring

Text shown in place of children while loading.
When provided the button width adapts to this text (variable width).
When omitted, children stay rendered invisibly — width stays stable.

iconOnlybooleanfalse

Renders as a square button with equal padding on all sides.

renderReactElement<unknown, string | JSXElementConstructor<any>>

Custom render element forwarded to base-ui Button.

nativeButtonbooleantrue

Use a native <button> element.

CSS Class Names

Available as keys on the ButtonStyles object. Each key maps to a hashed CSS module class name at runtime.

content-loadingloaderloader-visiblerootsize-lgsize-mdsize-smsize-xsspinnertone-destructivetone-primarytone-secondarytone-successtone-warningvariant-ghostvariant-outlinevariant-softvariant-solid